Browse code

proxy: fix an issue about half-closing net.TCPConn after io.Copy()

Fix docker/docker#27539

After io.Copy(to, from), we should call to.CloseWrite(), not to.CloseRead().

Without this fix, TestTCP4ProxyHalfClose (newly added in this commit) fails as
follows:

--- FAIL: TestTCP4ProxyHalfClose (0.00s)
network_proxy_test.go:135: EOF

Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>

Akihiro Suda authored on 2016/12/22 13:30:40
Showing 2 changed files
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"flag"
6 6
 	"fmt"
7 7
 	"io"
8
+	"io/ioutil"
8 9
 	"net"
9 10
 	"strings"
10 11
 	"testing"
... ...
@@ -22,9 +23,14 @@ type EchoServer interface {
22 22
 	LocalAddr() net.Addr
23 23
 }
24 24
 
25
+type EchoServerOptions struct {
26
+	TCPHalfClose bool
27
+}
28
+
25 29
 type TCPEchoServer struct {
26 30
 	listener net.Listener
27 31
 	testCtx  *testing.T
32
+	opts     EchoServerOptions
28 33
 }
29 34
 
30 35
 type UDPEchoServer struct {
... ...
@@ -32,15 +38,18 @@ type UDPEchoServer struct {
32 32
 	testCtx *testing.T
33 33
 }
34 34
 
35
-func NewEchoServer(t *testing.T, proto, address string) EchoServer {
35
+func NewEchoServer(t *testing.T, proto, address string, opts EchoServerOptions) EchoServer {
36 36
 	var server EchoServer
37 37
 	if strings.HasPrefix(proto, "tcp") {
38 38
 		listener, err := net.Listen(proto, address)
39 39
 		if err != nil {
40 40
 			t.Fatal(err)
41 41
 		}
42
-		server = &TCPEchoServer{listener: listener, testCtx: t}
42
+		server = &TCPEchoServer{listener: listener, testCtx: t, opts: opts}
43 43
 	} else {
44
+		if opts.TCPHalfClose {
45
+			t.Fatalf("TCPHalfClose is not supported for %s", proto)
46
+		}
44 47
 		socket, err := net.ListenPacket(proto, address)
45 48
 		if err != nil {
46 49
 			t.Fatal(err)
... ...
@@ -58,10 +67,21 @@ func (server *TCPEchoServer) Run() {
58 58
 				return
59 59
 			}
60 60
 			go func(client net.Conn) {
61
-				if _, err := io.Copy(client, client); err != nil {
62
-					server.testCtx.Logf("can't echo to the client: %v\n", err.Error())
61
+				if server.opts.TCPHalfClose {
62
+					data, err := ioutil.ReadAll(client)
63
+					if err != nil {
64
+						server.testCtx.Logf("io.ReadAll() failed for the client: %v\n", err.Error())
65
+					}
66
+					if _, err := client.Write(data); err != nil {
67
+						server.testCtx.Logf("can't echo to the client: %v\n", err.Error())
68
+					}
69
+					client.(*net.TCPConn).CloseWrite()
70
+				} else {
71
+					if _, err := io.Copy(client, client); err != nil {
72
+						server.testCtx.Logf("can't echo to the client: %v\n", err.Error())
73
+					}
74
+					client.Close()
63 75
 				}
64
-				client.Close()
65 76
 			}(client)
66 77
 		}
67 78
 	}()
... ...
@@ -92,7 +112,7 @@ func (server *UDPEchoServer) Run() {
92 92
 func (server *UDPEchoServer) LocalAddr() net.Addr { return server.conn.LocalAddr() }
93 93
 func (server *UDPEchoServer) Close()              { server.conn.Close() }
94 94
 
95
-func testProxyAt(t *testing.T, proto string, proxy Proxy, addr string) {
95
+func testProxyAt(t *testing.T, proto string, proxy Proxy, addr string, halfClose bool) {
96 96
 	defer proxy.Close()
97 97
 	go proxy.Run()
98 98
 	client, err := net.Dial(proto, addr)
... ...
@@ -104,6 +124,12 @@ func testProxyAt(t *testing.T, proto string, proxy Proxy, addr string) {
104 104
 	if _, err = client.Write(testBuf); err != nil {
105 105
 		t.Fatal(err)
106 106
 	}
107
+	if halfClose {
108
+		if proto != "tcp" {
109
+			t.Fatalf("halfClose is not supported for %s", proto)
110
+		}
111
+		client.(*net.TCPConn).CloseWrite()
112
+	}
107 113
 	recvBuf := make([]byte, testBufSize)
108 114
 	if _, err = client.Read(recvBuf); err != nil {
109 115
 		t.Fatal(err)
... ...
@@ -113,12 +139,12 @@ func testProxyAt(t *testing.T, proto string, proxy Proxy, addr string) {
113 113
 	}
114 114
 }
115 115
 
116
-func testProxy(t *testing.T, proto string, proxy Proxy) {
117
-	testProxyAt(t, proto, proxy, proxy.FrontendAddr().String())
116
+func testProxy(t *testing.T, proto string, proxy Proxy, halfClose bool) {
117
+	testProxyAt(t, proto, proxy, proxy.FrontendAddr().String(), halfClose)
118 118
 }
119 119
 
120
-func TestTCP4Proxy(t *testing.T) {
121
-	backend := NewEchoServer(t, "tcp", "127.0.0.1:0")
120
+func testTCP4Proxy(t *testing.T, halfClose bool) {
121
+	backend := NewEchoServer(t, "tcp", "127.0.0.1:0", EchoServerOptions{TCPHalfClose: halfClose})
122 122
 	defer backend.Close()
123 123
 	backend.Run()
124 124
 	frontendAddr := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}
... ...
@@ -126,12 +152,20 @@ func TestTCP4Proxy(t *testing.T) {
126 126
 	if err != nil {
127 127
 		t.Fatal(err)
128 128
 	}
129
-	testProxy(t, "tcp", proxy)
129
+	testProxy(t, "tcp", proxy, halfClose)
130
+}
131
+
132
+func TestTCP4Proxy(t *testing.T) {
133
+	testTCP4Proxy(t, false)
134
+}
135
+
136
+func TestTCP4ProxyHalfClose(t *testing.T) {
137
+	testTCP4Proxy(t, true)
130 138
 }
131 139
 
132 140
 func TestTCP6Proxy(t *testing.T) {
133 141
 	t.Skip("Need to start CI docker with --ipv6")
134
-	backend := NewEchoServer(t, "tcp", "[::1]:0")
142
+	backend := NewEchoServer(t, "tcp", "[::1]:0", EchoServerOptions{})
135 143
 	defer backend.Close()
136 144
 	backend.Run()
137 145
 	frontendAddr := &net.TCPAddr{IP: net.IPv6loopback, Port: 0}
... ...
@@ -139,14 +173,14 @@ func TestTCP6Proxy(t *testing.T) {
139 139
 	if err != nil {
140 140
 		t.Fatal(err)
141 141
 	}
142
-	testProxy(t, "tcp", proxy)
142
+	testProxy(t, "tcp", proxy, false)
143 143
 }
144 144
 
145 145
 func TestTCPDualStackProxy(t *testing.T) {
146 146
 	// If I understand `godoc -src net favoriteAddrFamily` (used by the
147 147
 	// net.Listen* functions) correctly this should work, but it doesn't.
148 148
 	t.Skip("No support for dual stack yet")
149
-	backend := NewEchoServer(t, "tcp", "[::1]:0")
149
+	backend := NewEchoServer(t, "tcp", "[::1]:0", EchoServerOptions{})
150 150
 	defer backend.Close()
151 151
 	backend.Run()
152 152
 	frontendAddr := &net.TCPAddr{IP: net.IPv6loopback, Port: 0}
... ...
@@ -158,11 +192,11 @@ func TestTCPDualStackProxy(t *testing.T) {
158 158
 		IP:   net.IPv4(127, 0, 0, 1),
159 159
 		Port: proxy.FrontendAddr().(*net.TCPAddr).Port,
160 160
 	}
161
-	testProxyAt(t, "tcp", proxy, ipv4ProxyAddr.String())
161
+	testProxyAt(t, "tcp", proxy, ipv4ProxyAddr.String(), false)
162 162
 }
163 163
 
164 164
 func TestUDP4Proxy(t *testing.T) {
165
-	backend := NewEchoServer(t, "udp", "127.0.0.1:0")
165
+	backend := NewEchoServer(t, "udp", "127.0.0.1:0", EchoServerOptions{})
166 166
 	defer backend.Close()
167 167
 	backend.Run()
168 168
 	frontendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}
... ...
@@ -170,12 +204,12 @@ func TestUDP4Proxy(t *testing.T) {
170 170
 	if err != nil {
171 171
 		t.Fatal(err)
172 172
 	}
173
-	testProxy(t, "udp", proxy)
173
+	testProxy(t, "udp", proxy, false)
174 174
 }
175 175
 
176 176
 func TestUDP6Proxy(t *testing.T) {
177 177
 	t.Skip("Need to start CI docker with --ipv6")
178
-	backend := NewEchoServer(t, "udp", "[::1]:0")
178
+	backend := NewEchoServer(t, "udp", "[::1]:0", EchoServerOptions{})
179 179
 	defer backend.Close()
180 180
 	backend.Run()
181 181
 	frontendAddr := &net.UDPAddr{IP: net.IPv6loopback, Port: 0}
... ...
@@ -183,7 +217,7 @@ func TestUDP6Proxy(t *testing.T) {
183 183
 	if err != nil {
184 184
 		t.Fatal(err)
185 185
 	}
186
-	testProxy(t, "udp", proxy)
186
+	testProxy(t, "udp", proxy, false)
187 187
 }
188 188
 
189 189
 func TestUDPWriteError(t *testing.T) {
... ...
@@ -204,7 +238,7 @@ func TestUDPWriteError(t *testing.T) {
204 204
 	// Make sure the proxy doesn't stop when there is no actual backend:
205 205
 	client.Write(testBuf)
206 206
 	client.Write(testBuf)
207
-	backend := NewEchoServer(t, "udp", "127.0.0.1:25587")
207
+	backend := NewEchoServer(t, "udp", "127.0.0.1:25587", EchoServerOptions{})
208 208
 	defer backend.Close()
209 209
 	backend.Run()
210 210
 	client.SetDeadline(time.Now().Add(10 * time.Second))
... ...
@@ -48,7 +48,8 @@ func (proxy *TCPProxy) clientLoop(client *net.TCPConn, quit chan bool) {
48 48
 				from.CloseWrite()
49 49
 			}
50 50
 		}
51
-		to.CloseRead()
51
+		from.CloseRead()
52
+		to.CloseWrite()
52 53
 		wg.Done()
53 54
 	}
54 55