Home | History | Annotate | Download | only in nettest
      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 }
    457