github.com/icodeface/tls@v0.0.0-20230910023335-34df9250cd12/internal/x/net/nettest/conntest.go (about) 1 // Copyright 2016 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package nettest provides utilities for network testing. 6 package nettest 7 8 import ( 9 "bytes" 10 "encoding/binary" 11 "io" 12 "io/ioutil" 13 "math/rand" 14 "net" 15 "runtime" 16 "sync" 17 "testing" 18 "time" 19 ) 20 21 var ( 22 aLongTimeAgo = time.Unix(233431200, 0) 23 neverTimeout = time.Time{} 24 ) 25 26 // MakePipe creates a connection between two endpoints and returns the pair 27 // as c1 and c2, such that anything written to c1 is read by c2 and vice-versa. 28 // The stop function closes all resources, including c1, c2, and the underlying 29 // net.Listener (if there is one), and should not be nil. 30 type MakePipe func() (c1, c2 net.Conn, stop func(), err error) 31 32 // TestConn tests that a net.Conn implementation properly satisfies the interface. 33 // The tests should not produce any false positives, but may experience 34 // false negatives. Thus, some issues may only be detected when the test is 35 // run multiple times. For maximal effectiveness, run the tests under the 36 // race detector. 37 func TestConn(t *testing.T, mp MakePipe) { 38 testConn(t, mp) 39 } 40 41 type connTester func(t *testing.T, c1, c2 net.Conn) 42 43 func timeoutWrapper(t *testing.T, mp MakePipe, f connTester) { 44 c1, c2, stop, err := mp() 45 if err != nil { 46 t.Fatalf("unable to make pipe: %v", err) 47 } 48 var once sync.Once 49 defer once.Do(func() { stop() }) 50 timer := time.AfterFunc(time.Minute, func() { 51 once.Do(func() { 52 t.Error("test timed out; terminating pipe") 53 stop() 54 }) 55 }) 56 defer timer.Stop() 57 f(t, c1, c2) 58 } 59 60 // testBasicIO tests that the data sent on c1 is properly received on c2. 61 func testBasicIO(t *testing.T, c1, c2 net.Conn) { 62 want := make([]byte, 1<<20) 63 rand.New(rand.NewSource(0)).Read(want) 64 65 dataCh := make(chan []byte) 66 go func() { 67 rd := bytes.NewReader(want) 68 if err := chunkedCopy(c1, rd); err != nil { 69 t.Errorf("unexpected c1.Write error: %v", err) 70 } 71 if err := c1.Close(); err != nil { 72 t.Errorf("unexpected c1.Close error: %v", err) 73 } 74 }() 75 76 go func() { 77 wr := new(bytes.Buffer) 78 if err := chunkedCopy(wr, c2); err != nil { 79 t.Errorf("unexpected c2.Read error: %v", err) 80 } 81 if err := c2.Close(); err != nil { 82 t.Errorf("unexpected c2.Close error: %v", err) 83 } 84 dataCh <- wr.Bytes() 85 }() 86 87 if got := <-dataCh; !bytes.Equal(got, want) { 88 t.Errorf("transmitted data differs") 89 } 90 } 91 92 // testPingPong tests that the two endpoints can synchronously send data to 93 // each other in a typical request-response pattern. 94 func testPingPong(t *testing.T, c1, c2 net.Conn) { 95 var wg sync.WaitGroup 96 defer wg.Wait() 97 98 pingPonger := func(c net.Conn) { 99 defer wg.Done() 100 buf := make([]byte, 8) 101 var prev uint64 102 for { 103 if _, err := io.ReadFull(c, buf); err != nil { 104 if err == io.EOF { 105 break 106 } 107 t.Errorf("unexpected Read error: %v", err) 108 } 109 110 v := binary.LittleEndian.Uint64(buf) 111 binary.LittleEndian.PutUint64(buf, v+1) 112 if prev != 0 && prev+2 != v { 113 t.Errorf("mismatching value: got %d, want %d", v, prev+2) 114 } 115 prev = v 116 if v == 1000 { 117 break 118 } 119 120 if _, err := c.Write(buf); err != nil { 121 t.Errorf("unexpected Write error: %v", err) 122 break 123 } 124 } 125 if err := c.Close(); err != nil { 126 t.Errorf("unexpected Close error: %v", err) 127 } 128 } 129 130 wg.Add(2) 131 go pingPonger(c1) 132 go pingPonger(c2) 133 134 // Start off the chain reaction. 135 if _, err := c1.Write(make([]byte, 8)); err != nil { 136 t.Errorf("unexpected c1.Write error: %v", err) 137 } 138 } 139 140 // testRacyRead tests that it is safe to mutate the input Read buffer 141 // immediately after cancelation has occurred. 142 func testRacyRead(t *testing.T, c1, c2 net.Conn) { 143 go chunkedCopy(c2, rand.New(rand.NewSource(0))) 144 145 var wg sync.WaitGroup 146 defer wg.Wait() 147 148 c1.SetReadDeadline(time.Now().Add(time.Millisecond)) 149 for i := 0; i < 10; i++ { 150 wg.Add(1) 151 go func() { 152 defer wg.Done() 153 154 b1 := make([]byte, 1024) 155 b2 := make([]byte, 1024) 156 for j := 0; j < 100; j++ { 157 _, err := c1.Read(b1) 158 copy(b1, b2) // Mutate b1 to trigger potential race 159 if err != nil { 160 checkForTimeoutError(t, err) 161 c1.SetReadDeadline(time.Now().Add(time.Millisecond)) 162 } 163 } 164 }() 165 } 166 } 167 168 // testRacyWrite tests that it is safe to mutate the input Write buffer 169 // immediately after cancelation has occurred. 170 func testRacyWrite(t *testing.T, c1, c2 net.Conn) { 171 go chunkedCopy(ioutil.Discard, c2) 172 173 var wg sync.WaitGroup 174 defer wg.Wait() 175 176 c1.SetWriteDeadline(time.Now().Add(time.Millisecond)) 177 for i := 0; i < 10; i++ { 178 wg.Add(1) 179 go func() { 180 defer wg.Done() 181 182 b1 := make([]byte, 1024) 183 b2 := make([]byte, 1024) 184 for j := 0; j < 100; j++ { 185 _, err := c1.Write(b1) 186 copy(b1, b2) // Mutate b1 to trigger potential race 187 if err != nil { 188 checkForTimeoutError(t, err) 189 c1.SetWriteDeadline(time.Now().Add(time.Millisecond)) 190 } 191 } 192 }() 193 } 194 } 195 196 // testReadTimeout tests that Read timeouts do not affect Write. 197 func testReadTimeout(t *testing.T, c1, c2 net.Conn) { 198 go chunkedCopy(ioutil.Discard, c2) 199 200 c1.SetReadDeadline(aLongTimeAgo) 201 _, err := c1.Read(make([]byte, 1024)) 202 checkForTimeoutError(t, err) 203 if _, err := c1.Write(make([]byte, 1024)); err != nil { 204 t.Errorf("unexpected Write error: %v", err) 205 } 206 } 207 208 // testWriteTimeout tests that Write timeouts do not affect Read. 209 func testWriteTimeout(t *testing.T, c1, c2 net.Conn) { 210 go chunkedCopy(c2, rand.New(rand.NewSource(0))) 211 212 c1.SetWriteDeadline(aLongTimeAgo) 213 _, err := c1.Write(make([]byte, 1024)) 214 checkForTimeoutError(t, err) 215 if _, err := c1.Read(make([]byte, 1024)); err != nil { 216 t.Errorf("unexpected Read error: %v", err) 217 } 218 } 219 220 // testPastTimeout tests that a deadline set in the past immediately times out 221 // Read and Write requests. 222 func testPastTimeout(t *testing.T, c1, c2 net.Conn) { 223 go chunkedCopy(c2, c2) 224 225 testRoundtrip(t, c1) 226 227 c1.SetDeadline(aLongTimeAgo) 228 n, err := c1.Write(make([]byte, 1024)) 229 if n != 0 { 230 t.Errorf("unexpected Write count: got %d, want 0", n) 231 } 232 checkForTimeoutError(t, err) 233 n, err = c1.Read(make([]byte, 1024)) 234 if n != 0 { 235 t.Errorf("unexpected Read count: got %d, want 0", n) 236 } 237 checkForTimeoutError(t, err) 238 239 testRoundtrip(t, c1) 240 } 241 242 // testPresentTimeout tests that a deadline set while there are pending 243 // Read and Write operations immediately times out those operations. 244 func testPresentTimeout(t *testing.T, c1, c2 net.Conn) { 245 var wg sync.WaitGroup 246 defer wg.Wait() 247 wg.Add(3) 248 249 deadlineSet := make(chan bool, 1) 250 go func() { 251 defer wg.Done() 252 time.Sleep(100 * time.Millisecond) 253 deadlineSet <- true 254 c1.SetReadDeadline(aLongTimeAgo) 255 c1.SetWriteDeadline(aLongTimeAgo) 256 }() 257 go func() { 258 defer wg.Done() 259 n, err := c1.Read(make([]byte, 1024)) 260 if n != 0 { 261 t.Errorf("unexpected Read count: got %d, want 0", n) 262 } 263 checkForTimeoutError(t, err) 264 if len(deadlineSet) == 0 { 265 t.Error("Read timed out before deadline is set") 266 } 267 }() 268 go func() { 269 defer wg.Done() 270 var err error 271 for err == nil { 272 _, err = c1.Write(make([]byte, 1024)) 273 } 274 checkForTimeoutError(t, err) 275 if len(deadlineSet) == 0 { 276 t.Error("Write timed out before deadline is set") 277 } 278 }() 279 } 280 281 // testFutureTimeout tests that a future deadline will eventually time out 282 // Read and Write operations. 283 func testFutureTimeout(t *testing.T, c1, c2 net.Conn) { 284 var wg sync.WaitGroup 285 wg.Add(2) 286 287 c1.SetDeadline(time.Now().Add(100 * time.Millisecond)) 288 go func() { 289 defer wg.Done() 290 _, err := c1.Read(make([]byte, 1024)) 291 checkForTimeoutError(t, err) 292 }() 293 go func() { 294 defer wg.Done() 295 var err error 296 for err == nil { 297 _, err = c1.Write(make([]byte, 1024)) 298 } 299 checkForTimeoutError(t, err) 300 }() 301 wg.Wait() 302 303 go chunkedCopy(c2, c2) 304 resyncConn(t, c1) 305 testRoundtrip(t, c1) 306 } 307 308 // testCloseTimeout tests that calling Close immediately times out pending 309 // Read and Write operations. 310 func testCloseTimeout(t *testing.T, c1, c2 net.Conn) { 311 go chunkedCopy(c2, c2) 312 313 var wg sync.WaitGroup 314 defer wg.Wait() 315 wg.Add(3) 316 317 // Test for cancelation upon connection closure. 318 c1.SetDeadline(neverTimeout) 319 go func() { 320 defer wg.Done() 321 time.Sleep(100 * time.Millisecond) 322 c1.Close() 323 }() 324 go func() { 325 defer wg.Done() 326 var err error 327 buf := make([]byte, 1024) 328 for err == nil { 329 _, err = c1.Read(buf) 330 } 331 }() 332 go func() { 333 defer wg.Done() 334 var err error 335 buf := make([]byte, 1024) 336 for err == nil { 337 _, err = c1.Write(buf) 338 } 339 }() 340 } 341 342 // testConcurrentMethods tests that the methods of net.Conn can safely 343 // be called concurrently. 344 func testConcurrentMethods(t *testing.T, c1, c2 net.Conn) { 345 if runtime.GOOS == "plan9" { 346 t.Skip("skipping on plan9; see https://golang.org/issue/20489") 347 } 348 go chunkedCopy(c2, c2) 349 350 // The results of the calls may be nonsensical, but this should 351 // not trigger a race detector warning. 352 var wg sync.WaitGroup 353 for i := 0; i < 100; i++ { 354 wg.Add(7) 355 go func() { 356 defer wg.Done() 357 c1.Read(make([]byte, 1024)) 358 }() 359 go func() { 360 defer wg.Done() 361 c1.Write(make([]byte, 1024)) 362 }() 363 go func() { 364 defer wg.Done() 365 c1.SetDeadline(time.Now().Add(10 * time.Millisecond)) 366 }() 367 go func() { 368 defer wg.Done() 369 c1.SetReadDeadline(aLongTimeAgo) 370 }() 371 go func() { 372 defer wg.Done() 373 c1.SetWriteDeadline(aLongTimeAgo) 374 }() 375 go func() { 376 defer wg.Done() 377 c1.LocalAddr() 378 }() 379 go func() { 380 defer wg.Done() 381 c1.RemoteAddr() 382 }() 383 } 384 wg.Wait() // At worst, the deadline is set 10ms into the future 385 386 resyncConn(t, c1) 387 testRoundtrip(t, c1) 388 } 389 390 // checkForTimeoutError checks that the error satisfies the Error interface 391 // and that Timeout returns true. 392 func checkForTimeoutError(t *testing.T, err error) { 393 if nerr, ok := err.(net.Error); ok { 394 if !nerr.Timeout() { 395 t.Errorf("err.Timeout() = false, want true") 396 } 397 } else { 398 t.Errorf("got %T, want net.Error", err) 399 } 400 } 401 402 // testRoundtrip writes something into c and reads it back. 403 // It assumes that everything written into c is echoed back to itself. 404 func testRoundtrip(t *testing.T, c net.Conn) { 405 if err := c.SetDeadline(neverTimeout); err != nil { 406 t.Errorf("roundtrip SetDeadline error: %v", err) 407 } 408 409 const s = "Hello, world!" 410 buf := []byte(s) 411 if _, err := c.Write(buf); err != nil { 412 t.Errorf("roundtrip Write error: %v", err) 413 } 414 if _, err := io.ReadFull(c, buf); err != nil { 415 t.Errorf("roundtrip Read error: %v", err) 416 } 417 if string(buf) != s { 418 t.Errorf("roundtrip data mismatch: got %q, want %q", buf, s) 419 } 420 } 421 422 // resyncConn resynchronizes the connection into a sane state. 423 // It assumes that everything written into c is echoed back to itself. 424 // It assumes that 0xff is not currently on the wire or in the read buffer. 425 func resyncConn(t *testing.T, c net.Conn) { 426 c.SetDeadline(neverTimeout) 427 errCh := make(chan error) 428 go func() { 429 _, err := c.Write([]byte{0xff}) 430 errCh <- err 431 }() 432 buf := make([]byte, 1024) 433 for { 434 n, err := c.Read(buf) 435 if n > 0 && bytes.IndexByte(buf[:n], 0xff) == n-1 { 436 break 437 } 438 if err != nil { 439 t.Errorf("unexpected Read error: %v", err) 440 break 441 } 442 } 443 if err := <-errCh; err != nil { 444 t.Errorf("unexpected Write error: %v", err) 445 } 446 } 447 448 // chunkedCopy copies from r to w in fixed-width chunks to avoid 449 // causing a Write that exceeds the maximum packet size for packet-based 450 // connections like "unixpacket". 451 // We assume that the maximum packet size is at least 1024. 452 func chunkedCopy(w io.Writer, r io.Reader) error { 453 b := make([]byte, 1024) 454 _, err := io.CopyBuffer(struct{ io.Writer }{w}, struct{ io.Reader }{r}, b) 455 return err 456 }