diff --git a/test/e2e/portforward.go b/test/e2e/portforward.go index 3a8974c0578..922b7fdad46 100644 --- a/test/e2e/portforward.go +++ b/test/e2e/portforward.go @@ -56,7 +56,7 @@ func pfPod(expectedClientData, chunks, chunkSize, chunkIntervalMillis string) *v Containers: []v1.Container{ { Name: "portforwardtester", - Image: "gcr.io/google_containers/portforwardtester:1.0", + Image: "gcr.io/google_containers/portforwardtester:1.2", Env: []v1.EnvVar{ { Name: "BIND_PORT", diff --git a/test/images/port-forward-tester/Makefile b/test/images/port-forward-tester/Makefile index 5c277e13cb5..4bcac25e252 100644 --- a/test/images/port-forward-tester/Makefile +++ b/test/images/port-forward-tester/Makefile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -TAG = 1.1 +TAG = 1.2 PREFIX = gcr.io/google_containers all: push diff --git a/test/images/port-forward-tester/portforwardtester.go b/test/images/port-forward-tester/portforwardtester.go index f1e721cdbb9..6131edc6582 100644 --- a/test/images/port-forward-tester/portforwardtester.go +++ b/test/images/port-forward-tester/portforwardtester.go @@ -47,24 +47,40 @@ func getEnvInt(name string) int { 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") - listener, err := net.Listen("tcp", fmt.Sprintf("%s:%s", bindAddress, bindPort)) + 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.Accept() + conn, err := listener.AcceptTCP() if err != nil { fmt.Printf("Error accepting connection: %v\n", err) os.Exit(1) } - defer conn.Close() + fmt.Println("Accepted client connection") expectedClientData := os.Getenv("EXPECTED_CLIENT_DATA") @@ -106,5 +122,20 @@ func main() { } } + 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") }