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//go:build !plan9 && !windows
6
7package net
8
9import (
10	"context"
11	"math/rand"
12	"runtime"
13	"sync"
14	"syscall"
15	"testing"
16	"time"
17)
18
19// See golang.org/issue/14548.
20func TestTCPSpuriousConnSetupCompletion(t *testing.T) {
21	if testing.Short() {
22		t.Skip("skipping in short mode")
23	}
24
25	ln := newLocalListener(t, "tcp")
26	var wg sync.WaitGroup
27	wg.Add(1)
28	go func(ln Listener) {
29		defer wg.Done()
30		for {
31			c, err := ln.Accept()
32			if err != nil {
33				return
34			}
35			wg.Add(1)
36			go func(c Conn) {
37				var b [1]byte
38				c.Read(b[:])
39				c.Close()
40				wg.Done()
41			}(c)
42		}
43	}(ln)
44
45	attempts := int(1e4) // larger is better
46	wg.Add(attempts)
47	throttle := make(chan struct{}, runtime.GOMAXPROCS(-1)*2)
48	for i := 0; i < attempts; i++ {
49		throttle <- struct{}{}
50		go func(i int) {
51			defer func() {
52				<-throttle
53				wg.Done()
54			}()
55			d := Dialer{Timeout: 50 * time.Millisecond}
56			c, err := d.Dial(ln.Addr().Network(), ln.Addr().String())
57			if err != nil {
58				if perr := parseDialError(err); perr != nil {
59					t.Errorf("#%d: %v (original error: %v)", i, perr, err)
60				}
61				return
62			}
63			var b [1]byte
64			if _, err := c.Write(b[:]); err != nil {
65				if perr := parseWriteError(err); perr != nil {
66					t.Errorf("#%d: %v", i, err)
67				}
68				if samePlatformError(err, syscall.ENOTCONN) {
69					t.Errorf("#%d: %v", i, err)
70				}
71			}
72			c.Close()
73		}(i)
74	}
75
76	ln.Close()
77	wg.Wait()
78}
79
80// Issue 19289.
81// Test that a canceled Dial does not cause a subsequent Dial to succeed.
82func TestTCPSpuriousConnSetupCompletionWithCancel(t *testing.T) {
83	mustHaveExternalNetwork(t)
84
85	defer dnsWaitGroup.Wait()
86	t.Parallel()
87	const tries = 10000
88	var wg sync.WaitGroup
89	wg.Add(tries * 2)
90	sem := make(chan bool, 5)
91	for i := 0; i < tries; i++ {
92		sem <- true
93		ctx, cancel := context.WithCancel(context.Background())
94		go func() {
95			defer wg.Done()
96			time.Sleep(time.Duration(rand.Int63n(int64(5 * time.Millisecond))))
97			cancel()
98		}()
99		go func(i int) {
100			defer wg.Done()
101			var dialer Dialer
102			// Try to connect to a real host on a port
103			// that it is not listening on.
104			_, err := dialer.DialContext(ctx, "tcp", "golang.org:3")
105			if err == nil {
106				t.Errorf("Dial to unbound port succeeded on attempt %d", i)
107			}
108			<-sem
109		}(i)
110	}
111	wg.Wait()
112}
113