1
2
3
4
5 package nettest
6
7 import (
8 "bytes"
9 "encoding/binary"
10 "io"
11 "math/rand"
12 "net"
13 "runtime"
14 "sync"
15 "testing"
16 "time"
17 )
18
19
20
21
22
23 type MakePipe func() (c1, c2 net.Conn, stop func(), err error)
24
25
26
27
28
29
30 func TestConn(t *testing.T, mp MakePipe) {
31 t.Run("BasicIO", func(t *testing.T) { timeoutWrapper(t, mp, testBasicIO) })
32 t.Run("PingPong", func(t *testing.T) { timeoutWrapper(t, mp, testPingPong) })
33 t.Run("RacyRead", func(t *testing.T) { timeoutWrapper(t, mp, testRacyRead) })
34 t.Run("RacyWrite", func(t *testing.T) { timeoutWrapper(t, mp, testRacyWrite) })
35 t.Run("ReadTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testReadTimeout) })
36 t.Run("WriteTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testWriteTimeout) })
37 t.Run("PastTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testPastTimeout) })
38 t.Run("PresentTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testPresentTimeout) })
39 t.Run("FutureTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testFutureTimeout) })
40 t.Run("CloseTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testCloseTimeout) })
41 t.Run("ConcurrentMethods", func(t *testing.T) { timeoutWrapper(t, mp, testConcurrentMethods) })
42 }
43
44 type connTester func(t *testing.T, c1, c2 net.Conn)
45
46 func timeoutWrapper(t *testing.T, mp MakePipe, f connTester) {
47 t.Helper()
48 c1, c2, stop, err := mp()
49 if err != nil {
50 t.Fatalf("unable to make pipe: %v", err)
51 }
52 var once sync.Once
53 defer once.Do(func() { stop() })
54 timer := time.AfterFunc(time.Minute, func() {
55 once.Do(func() {
56 t.Error("test timed out; terminating pipe")
57 stop()
58 })
59 })
60 defer timer.Stop()
61 f(t, c1, c2)
62 }
63
64
65 func testBasicIO(t *testing.T, c1, c2 net.Conn) {
66 want := make([]byte, 1<<20)
67 rand.New(rand.NewSource(0)).Read(want)
68
69 dataCh := make(chan []byte)
70 go func() {
71 rd := bytes.NewReader(want)
72 if err := chunkedCopy(c1, rd); err != nil {
73 t.Errorf("unexpected c1.Write error: %v", err)
74 }
75 if err := c1.Close(); err != nil {
76 t.Errorf("unexpected c1.Close error: %v", err)
77 }
78 }()
79
80 go func() {
81 wr := new(bytes.Buffer)
82 if err := chunkedCopy(wr, c2); err != nil {
83 t.Errorf("unexpected c2.Read error: %v", err)
84 }
85 if err := c2.Close(); err != nil {
86 t.Errorf("unexpected c2.Close error: %v", err)
87 }
88 dataCh <- wr.Bytes()
89 }()
90
91 if got := <-dataCh; !bytes.Equal(got, want) {
92 t.Error("transmitted data differs")
93 }
94 }
95
96
97
98 func testPingPong(t *testing.T, c1, c2 net.Conn) {
99 var wg sync.WaitGroup
100 defer wg.Wait()
101
102 pingPonger := func(c net.Conn) {
103 defer wg.Done()
104 buf := make([]byte, 8)
105 var prev uint64
106 for {
107 if _, err := io.ReadFull(c, buf); err != nil {
108 if err == io.EOF {
109 break
110 }
111 t.Errorf("unexpected Read error: %v", err)
112 }
113
114 v := binary.LittleEndian.Uint64(buf)
115 binary.LittleEndian.PutUint64(buf, v+1)
116 if prev != 0 && prev+2 != v {
117 t.Errorf("mismatching value: got %d, want %d", v, prev+2)
118 }
119 prev = v
120 if v == 1000 {
121 break
122 }
123
124 if _, err := c.Write(buf); err != nil {
125 t.Errorf("unexpected Write error: %v", err)
126 break
127 }
128 }
129 if err := c.Close(); err != nil {
130 t.Errorf("unexpected Close error: %v", err)
131 }
132 }
133
134 wg.Add(2)
135 go pingPonger(c1)
136 go pingPonger(c2)
137
138
139 if _, err := c1.Write(make([]byte, 8)); err != nil {
140 t.Errorf("unexpected c1.Write error: %v", err)
141 }
142 }
143
144
145
146 func testRacyRead(t *testing.T, c1, c2 net.Conn) {
147 go chunkedCopy(c2, rand.New(rand.NewSource(0)))
148
149 var wg sync.WaitGroup
150 defer wg.Wait()
151
152 c1.SetReadDeadline(time.Now().Add(time.Millisecond))
153 for i := 0; i < 10; i++ {
154 wg.Add(1)
155 go func() {
156 defer wg.Done()
157
158 b1 := make([]byte, 1024)
159 b2 := make([]byte, 1024)
160 for j := 0; j < 100; j++ {
161 _, err := c1.Read(b1)
162 copy(b1, b2)
163 if err != nil {
164 checkForTimeoutError(t, err)
165 c1.SetReadDeadline(time.Now().Add(time.Millisecond))
166 }
167 }
168 }()
169 }
170 }
171
172
173
174 func testRacyWrite(t *testing.T, c1, c2 net.Conn) {
175 go chunkedCopy(io.Discard, c2)
176
177 var wg sync.WaitGroup
178 defer wg.Wait()
179
180 c1.SetWriteDeadline(time.Now().Add(time.Millisecond))
181 for i := 0; i < 10; i++ {
182 wg.Add(1)
183 go func() {
184 defer wg.Done()
185
186 b1 := make([]byte, 1024)
187 b2 := make([]byte, 1024)
188 for j := 0; j < 100; j++ {
189 _, err := c1.Write(b1)
190 copy(b1, b2)
191 if err != nil {
192 checkForTimeoutError(t, err)
193 c1.SetWriteDeadline(time.Now().Add(time.Millisecond))
194 }
195 }
196 }()
197 }
198 }
199
200
201 func testReadTimeout(t *testing.T, c1, c2 net.Conn) {
202 go chunkedCopy(io.Discard, c2)
203
204 c1.SetReadDeadline(aLongTimeAgo)
205 _, err := c1.Read(make([]byte, 1024))
206 checkForTimeoutError(t, err)
207 if _, err := c1.Write(make([]byte, 1024)); err != nil {
208 t.Errorf("unexpected Write error: %v", err)
209 }
210 }
211
212
213 func testWriteTimeout(t *testing.T, c1, c2 net.Conn) {
214 go chunkedCopy(c2, rand.New(rand.NewSource(0)))
215
216 c1.SetWriteDeadline(aLongTimeAgo)
217 _, err := c1.Write(make([]byte, 1024))
218 checkForTimeoutError(t, err)
219 if _, err := c1.Read(make([]byte, 1024)); err != nil {
220 t.Errorf("unexpected Read error: %v", err)
221 }
222 }
223
224
225
226 func testPastTimeout(t *testing.T, c1, c2 net.Conn) {
227 go chunkedCopy(c2, c2)
228
229 testRoundtrip(t, c1)
230
231 c1.SetDeadline(aLongTimeAgo)
232 n, err := c1.Write(make([]byte, 1024))
233 if n != 0 {
234 t.Errorf("unexpected Write count: got %d, want 0", n)
235 }
236 checkForTimeoutError(t, err)
237 n, err = c1.Read(make([]byte, 1024))
238 if n != 0 {
239 t.Errorf("unexpected Read count: got %d, want 0", n)
240 }
241 checkForTimeoutError(t, err)
242
243 testRoundtrip(t, c1)
244 }
245
246
247
248 func testPresentTimeout(t *testing.T, c1, c2 net.Conn) {
249 var wg sync.WaitGroup
250 defer wg.Wait()
251 wg.Add(3)
252
253 deadlineSet := make(chan bool, 1)
254 go func() {
255 defer wg.Done()
256 time.Sleep(100 * time.Millisecond)
257 deadlineSet <- true
258 c1.SetReadDeadline(aLongTimeAgo)
259 c1.SetWriteDeadline(aLongTimeAgo)
260 }()
261 go func() {
262 defer wg.Done()
263 n, err := c1.Read(make([]byte, 1024))
264 if n != 0 {
265 t.Errorf("unexpected Read count: got %d, want 0", n)
266 }
267 checkForTimeoutError(t, err)
268 if len(deadlineSet) == 0 {
269 t.Error("Read timed out before deadline is set")
270 }
271 }()
272 go func() {
273 defer wg.Done()
274 var err error
275 for err == nil {
276 _, err = c1.Write(make([]byte, 1024))
277 }
278 checkForTimeoutError(t, err)
279 if len(deadlineSet) == 0 {
280 t.Error("Write timed out before deadline is set")
281 }
282 }()
283 }
284
285
286
287 func testFutureTimeout(t *testing.T, c1, c2 net.Conn) {
288 var wg sync.WaitGroup
289 wg.Add(2)
290
291 c1.SetDeadline(time.Now().Add(100 * time.Millisecond))
292 go func() {
293 defer wg.Done()
294 _, err := c1.Read(make([]byte, 1024))
295 checkForTimeoutError(t, err)
296 }()
297 go func() {
298 defer wg.Done()
299 var err error
300 for err == nil {
301 _, err = c1.Write(make([]byte, 1024))
302 }
303 checkForTimeoutError(t, err)
304 }()
305 wg.Wait()
306
307 go chunkedCopy(c2, c2)
308 resyncConn(t, c1)
309 testRoundtrip(t, c1)
310 }
311
312
313
314 func testCloseTimeout(t *testing.T, c1, c2 net.Conn) {
315 go chunkedCopy(c2, c2)
316
317 var wg sync.WaitGroup
318 defer wg.Wait()
319 wg.Add(3)
320
321
322 c1.SetDeadline(neverTimeout)
323 go func() {
324 defer wg.Done()
325 time.Sleep(100 * time.Millisecond)
326 c1.Close()
327 }()
328 go func() {
329 defer wg.Done()
330 var err error
331 buf := make([]byte, 1024)
332 for err == nil {
333 _, err = c1.Read(buf)
334 }
335 }()
336 go func() {
337 defer wg.Done()
338 var err error
339 buf := make([]byte, 1024)
340 for err == nil {
341 _, err = c1.Write(buf)
342 }
343 }()
344 }
345
346
347
348 func testConcurrentMethods(t *testing.T, c1, c2 net.Conn) {
349 if runtime.GOOS == "plan9" {
350 t.Skip("skipping on plan9; see https://golang.org/issue/20489")
351 }
352 go chunkedCopy(c2, c2)
353
354
355
356 var wg sync.WaitGroup
357 for i := 0; i < 100; i++ {
358 wg.Add(7)
359 go func() {
360 defer wg.Done()
361 c1.Read(make([]byte, 1024))
362 }()
363 go func() {
364 defer wg.Done()
365 c1.Write(make([]byte, 1024))
366 }()
367 go func() {
368 defer wg.Done()
369 c1.SetDeadline(time.Now().Add(10 * time.Millisecond))
370 }()
371 go func() {
372 defer wg.Done()
373 c1.SetReadDeadline(aLongTimeAgo)
374 }()
375 go func() {
376 defer wg.Done()
377 c1.SetWriteDeadline(aLongTimeAgo)
378 }()
379 go func() {
380 defer wg.Done()
381 c1.LocalAddr()
382 }()
383 go func() {
384 defer wg.Done()
385 c1.RemoteAddr()
386 }()
387 }
388 wg.Wait()
389
390 resyncConn(t, c1)
391 testRoundtrip(t, c1)
392 }
393
394
395
396 func checkForTimeoutError(t *testing.T, err error) {
397 t.Helper()
398 if nerr, ok := err.(net.Error); ok {
399 if !nerr.Timeout() {
400 if runtime.GOOS == "windows" && runtime.GOARCH == "arm64" && t.Name() == "TestTestConn/TCP/RacyRead" {
401 t.Logf("ignoring known failure mode on windows/arm64; see https://go.dev/issue/52893")
402 } else {
403 t.Errorf("got error: %v, want err.Timeout() = true", nerr)
404 }
405 }
406 } else {
407 t.Errorf("got %T: %v, want net.Error", err, err)
408 }
409 }
410
411
412
413 func testRoundtrip(t *testing.T, c net.Conn) {
414 t.Helper()
415 if err := c.SetDeadline(neverTimeout); err != nil {
416 t.Errorf("roundtrip SetDeadline error: %v", err)
417 }
418
419 const s = "Hello, world!"
420 buf := []byte(s)
421 if _, err := c.Write(buf); err != nil {
422 t.Errorf("roundtrip Write error: %v", err)
423 }
424 if _, err := io.ReadFull(c, buf); err != nil {
425 t.Errorf("roundtrip Read error: %v", err)
426 }
427 if string(buf) != s {
428 t.Errorf("roundtrip data mismatch: got %q, want %q", buf, s)
429 }
430 }
431
432
433
434
435 func resyncConn(t *testing.T, c net.Conn) {
436 t.Helper()
437 c.SetDeadline(neverTimeout)
438 errCh := make(chan error)
439 go func() {
440 _, err := c.Write([]byte{0xff})
441 errCh <- err
442 }()
443 buf := make([]byte, 1024)
444 for {
445 n, err := c.Read(buf)
446 if n > 0 && bytes.IndexByte(buf[:n], 0xff) == n-1 {
447 break
448 }
449 if err != nil {
450 t.Errorf("unexpected Read error: %v", err)
451 break
452 }
453 }
454 if err := <-errCh; err != nil {
455 t.Errorf("unexpected Write error: %v", err)
456 }
457 }
458
459
460
461
462
463 func chunkedCopy(w io.Writer, r io.Reader) error {
464 b := make([]byte, 1024)
465 _, err := io.CopyBuffer(struct{ io.Writer }{w}, struct{ io.Reader }{r}, b)
466 return err
467 }
468
View as plain text