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  }