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.
6package nettest
7
8import (
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
21var (
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.
30type 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.
37func TestConn(t *testing.T, mp MakePipe) {
38	testConn(t, mp)
39}
40
41type connTester func(t *testing.T, c1, c2 net.Conn)
42
43func 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.
61func 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.
94func 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.
142func 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.
170func 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.
197func 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.
209func 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.
222func 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.
244func 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.
283func 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.
310func 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.
344func 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.
392func 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.
404func 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.
425func 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.
452func 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