mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-11-04 07:49:35 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			142 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			142 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2015 The Kubernetes Authors.
 | 
						|
 | 
						|
Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
you may not use this file except in compliance with the License.
 | 
						|
You may obtain a copy of the License at
 | 
						|
 | 
						|
    http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
Unless required by applicable law or agreed to in writing, software
 | 
						|
distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
See the License for the specific language governing permissions and
 | 
						|
limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
// A tiny binary for testing port forwarding. The following environment variables
 | 
						|
// control the binary's logic:
 | 
						|
//
 | 
						|
// BIND_PORT - the TCP port to use for the listener
 | 
						|
// EXPECTED_CLIENT_DATA - data that we expect to receive from the client; may be "".
 | 
						|
// CHUNKS - how many chunks of data we should send to the client
 | 
						|
// CHUNK_SIZE - how large each chunk should be
 | 
						|
// CHUNK_INTERVAL - the delay in between sending each chunk
 | 
						|
//
 | 
						|
// Log messages are written to stdout at various stages of the binary's execution.
 | 
						|
// Test code can retrieve this container's log and validate that the expected
 | 
						|
// behavior is taking place.
 | 
						|
package main
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"net"
 | 
						|
	"os"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
	"time"
 | 
						|
)
 | 
						|
 | 
						|
func getEnvInt(name string) int {
 | 
						|
	s := os.Getenv(name)
 | 
						|
	value, err := strconv.Atoi(s)
 | 
						|
	if err != nil {
 | 
						|
		fmt.Printf("Error parsing %s %q: %v\n", name, s, err)
 | 
						|
		os.Exit(1)
 | 
						|
	}
 | 
						|
	return value
 | 
						|
}
 | 
						|
 | 
						|
// taken from net/http/server.go:
 | 
						|
//
 | 
						|
// rstAvoidanceDelay is the amount of time we sleep after closing the
 | 
						|
// write side of a TCP connection before closing the entire socket.
 | 
						|
// By sleeping, we increase the chances that the client sees our FIN
 | 
						|
// and processes its final data before they process the subsequent RST
 | 
						|
// from closing a connection with known unread data.
 | 
						|
// This RST seems to occur mostly on BSD systems. (And Windows?)
 | 
						|
// This timeout is somewhat arbitrary (~latency around the planet).
 | 
						|
const rstAvoidanceDelay = 500 * time.Millisecond
 | 
						|
 | 
						|
func main() {
 | 
						|
	bindAddress := os.Getenv("BIND_ADDRESS")
 | 
						|
	if bindAddress == "" {
 | 
						|
		bindAddress = "localhost"
 | 
						|
	}
 | 
						|
	bindPort := os.Getenv("BIND_PORT")
 | 
						|
	addr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(bindAddress, bindPort))
 | 
						|
	if err != nil {
 | 
						|
		fmt.Printf("Error resolving: %v\n", err)
 | 
						|
		os.Exit(1)
 | 
						|
	}
 | 
						|
	listener, err := net.ListenTCP("tcp", addr)
 | 
						|
	if err != nil {
 | 
						|
		fmt.Printf("Error listening: %v\n", err)
 | 
						|
		os.Exit(1)
 | 
						|
	}
 | 
						|
 | 
						|
	conn, err := listener.AcceptTCP()
 | 
						|
	if err != nil {
 | 
						|
		fmt.Printf("Error accepting connection: %v\n", err)
 | 
						|
		os.Exit(1)
 | 
						|
	}
 | 
						|
 | 
						|
	fmt.Println("Accepted client connection")
 | 
						|
 | 
						|
	expectedClientData := os.Getenv("EXPECTED_CLIENT_DATA")
 | 
						|
	if len(expectedClientData) > 0 {
 | 
						|
		buf := make([]byte, len(expectedClientData))
 | 
						|
		read, err := conn.Read(buf)
 | 
						|
		if read != len(expectedClientData) {
 | 
						|
			fmt.Printf("Expected to read %d bytes from client, but got %d instead. err=%v\n", len(expectedClientData), read, err)
 | 
						|
			os.Exit(2)
 | 
						|
		}
 | 
						|
		if expectedClientData != string(buf) {
 | 
						|
			fmt.Printf("Expect to read %q, but got %q. err=%v\n", expectedClientData, string(buf), err)
 | 
						|
			os.Exit(3)
 | 
						|
		}
 | 
						|
		if err != nil {
 | 
						|
			fmt.Printf("Read err: %v\n", err)
 | 
						|
		}
 | 
						|
		fmt.Println("Received expected client data")
 | 
						|
	}
 | 
						|
 | 
						|
	chunks := getEnvInt("CHUNKS")
 | 
						|
	chunkSize := getEnvInt("CHUNK_SIZE")
 | 
						|
	chunkInterval := getEnvInt("CHUNK_INTERVAL")
 | 
						|
 | 
						|
	stringData := strings.Repeat("x", chunkSize)
 | 
						|
	data := []byte(stringData)
 | 
						|
 | 
						|
	for i := 0; i < chunks; i++ {
 | 
						|
		written, err := conn.Write(data)
 | 
						|
		if written != chunkSize {
 | 
						|
			fmt.Printf("Expected to write %d bytes from client, but wrote %d instead. err=%v\n", chunkSize, written, err)
 | 
						|
			os.Exit(4)
 | 
						|
		}
 | 
						|
		if err != nil {
 | 
						|
			fmt.Printf("Write err: %v\n", err)
 | 
						|
		}
 | 
						|
		if i+1 < chunks {
 | 
						|
			time.Sleep(time.Duration(chunkInterval) * time.Millisecond)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	fmt.Println("Shutting down connection")
 | 
						|
 | 
						|
	// set linger timeout to flush buffers. This is the official way according to the go api docs. But
 | 
						|
	// there are controversial discussions whether this value has any impact on most platforms
 | 
						|
	// (compare https://codereview.appspot.com/95320043).
 | 
						|
	conn.SetLinger(-1)
 | 
						|
 | 
						|
	// Flush the connection cleanly, following https://blog.netherlabs.nl/articles/2009/01/18/the-ultimate-so_linger-page-or-why-is-my-tcp-not-reliable:
 | 
						|
	// 1. close write half of connection which sends a FIN packet
 | 
						|
	// 2. give client some time to receive the FIN
 | 
						|
	// 3. close the complete connection
 | 
						|
	conn.CloseWrite()
 | 
						|
	time.Sleep(rstAvoidanceDelay)
 | 
						|
	conn.Close()
 | 
						|
 | 
						|
	fmt.Println("Done")
 | 
						|
}
 |