Home | History | Annotate | Download | only in net
      1 // Copyright 2011 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 net
      6 
      7 import (
      8 	"io"
      9 	"net/internal/socktest"
     10 	"runtime"
     11 	"sync"
     12 	"testing"
     13 	"time"
     14 )
     15 
     16 var prohibitionaryDialArgTests = []struct {
     17 	network string
     18 	address string
     19 }{
     20 	{"tcp6", "127.0.0.1"},
     21 	{"tcp6", "::ffff:127.0.0.1"},
     22 }
     23 
     24 func TestProhibitionaryDialArg(t *testing.T) {
     25 	switch runtime.GOOS {
     26 	case "plan9":
     27 		t.Skipf("not supported on %s", runtime.GOOS)
     28 	}
     29 	if testing.Short() || !*testExternal {
     30 		t.Skip("avoid external network")
     31 	}
     32 	if !supportsIPv4map {
     33 		t.Skip("mapping ipv4 address inside ipv6 address not supported")
     34 	}
     35 
     36 	ln, err := Listen("tcp", "[::]:0")
     37 	if err != nil {
     38 		t.Fatal(err)
     39 	}
     40 	defer ln.Close()
     41 
     42 	_, port, err := SplitHostPort(ln.Addr().String())
     43 	if err != nil {
     44 		t.Fatal(err)
     45 	}
     46 
     47 	for i, tt := range prohibitionaryDialArgTests {
     48 		c, err := Dial(tt.network, JoinHostPort(tt.address, port))
     49 		if err == nil {
     50 			c.Close()
     51 			t.Errorf("#%d: %v", i, err)
     52 		}
     53 	}
     54 }
     55 
     56 func TestSelfConnect(t *testing.T) {
     57 	if runtime.GOOS == "windows" {
     58 		// TODO(brainman): do not know why it hangs.
     59 		t.Skip("known-broken test on windows")
     60 	}
     61 
     62 	// Test that Dial does not honor self-connects.
     63 	// See the comment in DialTCP.
     64 
     65 	// Find a port that would be used as a local address.
     66 	l, err := Listen("tcp", "127.0.0.1:0")
     67 	if err != nil {
     68 		t.Fatal(err)
     69 	}
     70 	c, err := Dial("tcp", l.Addr().String())
     71 	if err != nil {
     72 		t.Fatal(err)
     73 	}
     74 	addr := c.LocalAddr().String()
     75 	c.Close()
     76 	l.Close()
     77 
     78 	// Try to connect to that address repeatedly.
     79 	n := 100000
     80 	if testing.Short() {
     81 		n = 1000
     82 	}
     83 	switch runtime.GOOS {
     84 	case "darwin", "dragonfly", "freebsd", "netbsd", "openbsd", "plan9", "solaris", "windows":
     85 		// Non-Linux systems take a long time to figure
     86 		// out that there is nothing listening on localhost.
     87 		n = 100
     88 	}
     89 	for i := 0; i < n; i++ {
     90 		c, err := DialTimeout("tcp", addr, time.Millisecond)
     91 		if err == nil {
     92 			if c.LocalAddr().String() == addr {
     93 				t.Errorf("#%d: Dial %q self-connect", i, addr)
     94 			} else {
     95 				t.Logf("#%d: Dial %q succeeded - possibly racing with other listener", i, addr)
     96 			}
     97 			c.Close()
     98 		}
     99 	}
    100 }
    101 
    102 func TestDialTimeoutFDLeak(t *testing.T) {
    103 	switch runtime.GOOS {
    104 	case "plan9":
    105 		t.Skipf("%s does not have full support of socktest", runtime.GOOS)
    106 	}
    107 
    108 	const T = 100 * time.Millisecond
    109 
    110 	switch runtime.GOOS {
    111 	case "plan9", "windows":
    112 		origTestHookDialChannel := testHookDialChannel
    113 		testHookDialChannel = func() { time.Sleep(2 * T) }
    114 		defer func() { testHookDialChannel = origTestHookDialChannel }()
    115 		if runtime.GOOS == "plan9" {
    116 			break
    117 		}
    118 		fallthrough
    119 	default:
    120 		sw.Set(socktest.FilterConnect, func(so *socktest.Status) (socktest.AfterFilter, error) {
    121 			time.Sleep(2 * T)
    122 			return nil, errTimeout
    123 		})
    124 		defer sw.Set(socktest.FilterConnect, nil)
    125 	}
    126 
    127 	// Avoid tracking open-close jitterbugs between netFD and
    128 	// socket that leads to confusion of information inside
    129 	// socktest.Switch.
    130 	// It may happen when the Dial call bumps against TCP
    131 	// simultaneous open. See selfConnect in tcpsock_posix.go.
    132 	defer func() {
    133 		sw.Set(socktest.FilterClose, nil)
    134 		forceCloseSockets()
    135 	}()
    136 	var mu sync.Mutex
    137 	var attempts int
    138 	sw.Set(socktest.FilterClose, func(so *socktest.Status) (socktest.AfterFilter, error) {
    139 		mu.Lock()
    140 		attempts++
    141 		mu.Unlock()
    142 		return nil, errTimedout
    143 	})
    144 
    145 	const N = 100
    146 	var wg sync.WaitGroup
    147 	wg.Add(N)
    148 	for i := 0; i < N; i++ {
    149 		go func() {
    150 			defer wg.Done()
    151 			// This dial never starts to send any SYN
    152 			// segment because of above socket filter and
    153 			// test hook.
    154 			c, err := DialTimeout("tcp", "127.0.0.1:0", T)
    155 			if err == nil {
    156 				t.Errorf("unexpectedly established: tcp:%s->%s", c.LocalAddr(), c.RemoteAddr())
    157 				c.Close()
    158 			}
    159 		}()
    160 	}
    161 	wg.Wait()
    162 	if attempts < N {
    163 		t.Errorf("got %d; want >= %d", attempts, N)
    164 	}
    165 }
    166 
    167 func TestDialerDualStackFDLeak(t *testing.T) {
    168 	switch runtime.GOOS {
    169 	case "plan9":
    170 		t.Skipf("%s does not have full support of socktest", runtime.GOOS)
    171 	case "windows":
    172 		t.Skipf("not implemented a way to cancel dial racers in TCP SYN-SENT state on %s", runtime.GOOS)
    173 	}
    174 	if !supportsIPv4 || !supportsIPv6 {
    175 		t.Skip("both IPv4 and IPv6 are required")
    176 	}
    177 
    178 	origTestHookLookupIP := testHookLookupIP
    179 	defer func() { testHookLookupIP = origTestHookLookupIP }()
    180 	testHookLookupIP = lookupLocalhost
    181 	handler := func(dss *dualStackServer, ln Listener) {
    182 		for {
    183 			c, err := ln.Accept()
    184 			if err != nil {
    185 				return
    186 			}
    187 			c.Close()
    188 		}
    189 	}
    190 	dss, err := newDualStackServer([]streamListener{
    191 		{network: "tcp4", address: "127.0.0.1"},
    192 		{network: "tcp6", address: "::1"},
    193 	})
    194 	if err != nil {
    195 		t.Fatal(err)
    196 	}
    197 	defer dss.teardown()
    198 	if err := dss.buildup(handler); err != nil {
    199 		t.Fatal(err)
    200 	}
    201 
    202 	before := sw.Sockets()
    203 	const T = 100 * time.Millisecond
    204 	const N = 10
    205 	var wg sync.WaitGroup
    206 	wg.Add(N)
    207 	d := &Dialer{DualStack: true, Timeout: T}
    208 	for i := 0; i < N; i++ {
    209 		go func() {
    210 			defer wg.Done()
    211 			c, err := d.Dial("tcp", JoinHostPort("localhost", dss.port))
    212 			if err != nil {
    213 				t.Error(err)
    214 				return
    215 			}
    216 			c.Close()
    217 		}()
    218 	}
    219 	wg.Wait()
    220 	time.Sleep(2 * T) // wait for the dial racers to stop
    221 	after := sw.Sockets()
    222 	if len(after) != len(before) {
    223 		t.Errorf("got %d; want %d", len(after), len(before))
    224 	}
    225 }
    226 
    227 // Define a pair of blackholed (IPv4, IPv6) addresses, for which dialTCP is
    228 // expected to hang until the timeout elapses. These addresses are reserved
    229 // for benchmarking by RFC 6890.
    230 const (
    231 	slowDst4    = "192.18.0.254"
    232 	slowDst6    = "2001:2::254"
    233 	slowTimeout = 1 * time.Second
    234 )
    235 
    236 // In some environments, the slow IPs may be explicitly unreachable, and fail
    237 // more quickly than expected. This test hook prevents dialTCP from returning
    238 // before the deadline.
    239 func slowDialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, error) {
    240 	c, err := dialTCP(net, laddr, raddr, deadline)
    241 	if ParseIP(slowDst4).Equal(raddr.IP) || ParseIP(slowDst6).Equal(raddr.IP) {
    242 		time.Sleep(deadline.Sub(time.Now()))
    243 	}
    244 	return c, err
    245 }
    246 
    247 func dialClosedPort() (actual, expected time.Duration) {
    248 	// Estimate the expected time for this platform.
    249 	// On Windows, dialing a closed port takes roughly 1 second,
    250 	// but other platforms should be instantaneous.
    251 	if runtime.GOOS == "windows" {
    252 		expected = 1500 * time.Millisecond
    253 	} else {
    254 		expected = 95 * time.Millisecond
    255 	}
    256 
    257 	l, err := Listen("tcp", "127.0.0.1:0")
    258 	if err != nil {
    259 		return 999 * time.Hour, expected
    260 	}
    261 	addr := l.Addr().String()
    262 	l.Close()
    263 	// On OpenBSD, interference from TestSelfConnect is mysteriously
    264 	// causing the first attempt to hang for a few seconds, so we throw
    265 	// away the first result and keep the second.
    266 	for i := 1; ; i++ {
    267 		startTime := time.Now()
    268 		c, err := Dial("tcp", addr)
    269 		if err == nil {
    270 			c.Close()
    271 		}
    272 		elapsed := time.Now().Sub(startTime)
    273 		if i == 2 {
    274 			return elapsed, expected
    275 		}
    276 	}
    277 }
    278 
    279 func TestDialParallel(t *testing.T) {
    280 	if testing.Short() || !*testExternal {
    281 		t.Skip("avoid external network")
    282 	}
    283 	if !supportsIPv4 || !supportsIPv6 {
    284 		t.Skip("both IPv4 and IPv6 are required")
    285 	}
    286 
    287 	closedPortDelay, expectClosedPortDelay := dialClosedPort()
    288 	if closedPortDelay > expectClosedPortDelay {
    289 		t.Errorf("got %v; want <= %v", closedPortDelay, expectClosedPortDelay)
    290 	}
    291 
    292 	const instant time.Duration = 0
    293 	const fallbackDelay = 200 * time.Millisecond
    294 
    295 	// Some cases will run quickly when "connection refused" is fast,
    296 	// or trigger the fallbackDelay on Windows.  This value holds the
    297 	// lesser of the two delays.
    298 	var closedPortOrFallbackDelay time.Duration
    299 	if closedPortDelay < fallbackDelay {
    300 		closedPortOrFallbackDelay = closedPortDelay
    301 	} else {
    302 		closedPortOrFallbackDelay = fallbackDelay
    303 	}
    304 
    305 	origTestHookDialTCP := testHookDialTCP
    306 	defer func() { testHookDialTCP = origTestHookDialTCP }()
    307 	testHookDialTCP = slowDialTCP
    308 
    309 	nCopies := func(s string, n int) []string {
    310 		out := make([]string, n)
    311 		for i := 0; i < n; i++ {
    312 			out[i] = s
    313 		}
    314 		return out
    315 	}
    316 
    317 	var testCases = []struct {
    318 		primaries       []string
    319 		fallbacks       []string
    320 		teardownNetwork string
    321 		expectOk        bool
    322 		expectElapsed   time.Duration
    323 	}{
    324 		// These should just work on the first try.
    325 		{[]string{"127.0.0.1"}, []string{}, "", true, instant},
    326 		{[]string{"::1"}, []string{}, "", true, instant},
    327 		{[]string{"127.0.0.1", "::1"}, []string{slowDst6}, "tcp6", true, instant},
    328 		{[]string{"::1", "127.0.0.1"}, []string{slowDst4}, "tcp4", true, instant},
    329 		// Primary is slow; fallback should kick in.
    330 		{[]string{slowDst4}, []string{"::1"}, "", true, fallbackDelay},
    331 		// Skip a "connection refused" in the primary thread.
    332 		{[]string{"127.0.0.1", "::1"}, []string{}, "tcp4", true, closedPortDelay},
    333 		{[]string{"::1", "127.0.0.1"}, []string{}, "tcp6", true, closedPortDelay},
    334 		// Skip a "connection refused" in the fallback thread.
    335 		{[]string{slowDst4, slowDst6}, []string{"::1", "127.0.0.1"}, "tcp6", true, fallbackDelay + closedPortDelay},
    336 		// Primary refused, fallback without delay.
    337 		{[]string{"127.0.0.1"}, []string{"::1"}, "tcp4", true, closedPortOrFallbackDelay},
    338 		{[]string{"::1"}, []string{"127.0.0.1"}, "tcp6", true, closedPortOrFallbackDelay},
    339 		// Everything is refused.
    340 		{[]string{"127.0.0.1"}, []string{}, "tcp4", false, closedPortDelay},
    341 		// Nothing to do; fail instantly.
    342 		{[]string{}, []string{}, "", false, instant},
    343 		// Connecting to tons of addresses should not trip the deadline.
    344 		{nCopies("::1", 1000), []string{}, "", true, instant},
    345 	}
    346 
    347 	handler := func(dss *dualStackServer, ln Listener) {
    348 		for {
    349 			c, err := ln.Accept()
    350 			if err != nil {
    351 				return
    352 			}
    353 			c.Close()
    354 		}
    355 	}
    356 
    357 	// Convert a list of IP strings into TCPAddrs.
    358 	makeAddrs := func(ips []string, port string) addrList {
    359 		var out addrList
    360 		for _, ip := range ips {
    361 			addr, err := ResolveTCPAddr("tcp", JoinHostPort(ip, port))
    362 			if err != nil {
    363 				t.Fatal(err)
    364 			}
    365 			out = append(out, addr)
    366 		}
    367 		return out
    368 	}
    369 
    370 	for i, tt := range testCases {
    371 		dss, err := newDualStackServer([]streamListener{
    372 			{network: "tcp4", address: "127.0.0.1"},
    373 			{network: "tcp6", address: "::1"},
    374 		})
    375 		if err != nil {
    376 			t.Fatal(err)
    377 		}
    378 		defer dss.teardown()
    379 		if err := dss.buildup(handler); err != nil {
    380 			t.Fatal(err)
    381 		}
    382 		if tt.teardownNetwork != "" {
    383 			// Destroy one of the listening sockets, creating an unreachable port.
    384 			dss.teardownNetwork(tt.teardownNetwork)
    385 		}
    386 
    387 		primaries := makeAddrs(tt.primaries, dss.port)
    388 		fallbacks := makeAddrs(tt.fallbacks, dss.port)
    389 		d := Dialer{
    390 			FallbackDelay: fallbackDelay,
    391 			Timeout:       slowTimeout,
    392 		}
    393 		ctx := &dialContext{
    394 			Dialer:        d,
    395 			network:       "tcp",
    396 			address:       "?",
    397 			finalDeadline: d.deadline(time.Now()),
    398 		}
    399 		startTime := time.Now()
    400 		c, err := dialParallel(ctx, primaries, fallbacks)
    401 		elapsed := time.Now().Sub(startTime)
    402 
    403 		if c != nil {
    404 			c.Close()
    405 		}
    406 
    407 		if tt.expectOk && err != nil {
    408 			t.Errorf("#%d: got %v; want nil", i, err)
    409 		} else if !tt.expectOk && err == nil {
    410 			t.Errorf("#%d: got nil; want non-nil", i)
    411 		}
    412 
    413 		expectElapsedMin := tt.expectElapsed - 95*time.Millisecond
    414 		expectElapsedMax := tt.expectElapsed + 95*time.Millisecond
    415 		if !(elapsed >= expectElapsedMin) {
    416 			t.Errorf("#%d: got %v; want >= %v", i, elapsed, expectElapsedMin)
    417 		} else if !(elapsed <= expectElapsedMax) {
    418 			t.Errorf("#%d: got %v; want <= %v", i, elapsed, expectElapsedMax)
    419 		}
    420 	}
    421 	// Wait for any slowDst4/slowDst6 connections to timeout.
    422 	time.Sleep(slowTimeout * 3 / 2)
    423 }
    424 
    425 func lookupSlowFast(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, error) {
    426 	switch host {
    427 	case "slow6loopback4":
    428 		// Returns a slow IPv6 address, and a local IPv4 address.
    429 		return []IPAddr{
    430 			{IP: ParseIP(slowDst6)},
    431 			{IP: ParseIP("127.0.0.1")},
    432 		}, nil
    433 	default:
    434 		return fn(host)
    435 	}
    436 }
    437 
    438 func TestDialerFallbackDelay(t *testing.T) {
    439 	if testing.Short() || !*testExternal {
    440 		t.Skip("avoid external network")
    441 	}
    442 	if !supportsIPv4 || !supportsIPv6 {
    443 		t.Skip("both IPv4 and IPv6 are required")
    444 	}
    445 
    446 	origTestHookLookupIP := testHookLookupIP
    447 	defer func() { testHookLookupIP = origTestHookLookupIP }()
    448 	testHookLookupIP = lookupSlowFast
    449 
    450 	origTestHookDialTCP := testHookDialTCP
    451 	defer func() { testHookDialTCP = origTestHookDialTCP }()
    452 	testHookDialTCP = slowDialTCP
    453 
    454 	var testCases = []struct {
    455 		dualstack     bool
    456 		delay         time.Duration
    457 		expectElapsed time.Duration
    458 	}{
    459 		// Use a very brief delay, which should fallback immediately.
    460 		{true, 1 * time.Nanosecond, 0},
    461 		// Use a 200ms explicit timeout.
    462 		{true, 200 * time.Millisecond, 200 * time.Millisecond},
    463 		// The default is 300ms.
    464 		{true, 0, 300 * time.Millisecond},
    465 		// This case is last, in order to wait for hanging slowDst6 connections.
    466 		{false, 0, slowTimeout},
    467 	}
    468 
    469 	handler := func(dss *dualStackServer, ln Listener) {
    470 		for {
    471 			c, err := ln.Accept()
    472 			if err != nil {
    473 				return
    474 			}
    475 			c.Close()
    476 		}
    477 	}
    478 	dss, err := newDualStackServer([]streamListener{
    479 		{network: "tcp", address: "127.0.0.1"},
    480 	})
    481 	if err != nil {
    482 		t.Fatal(err)
    483 	}
    484 	defer dss.teardown()
    485 	if err := dss.buildup(handler); err != nil {
    486 		t.Fatal(err)
    487 	}
    488 
    489 	for i, tt := range testCases {
    490 		d := &Dialer{DualStack: tt.dualstack, FallbackDelay: tt.delay, Timeout: slowTimeout}
    491 
    492 		startTime := time.Now()
    493 		c, err := d.Dial("tcp", JoinHostPort("slow6loopback4", dss.port))
    494 		elapsed := time.Now().Sub(startTime)
    495 		if err == nil {
    496 			c.Close()
    497 		} else if tt.dualstack {
    498 			t.Error(err)
    499 		}
    500 		expectMin := tt.expectElapsed - 1*time.Millisecond
    501 		expectMax := tt.expectElapsed + 95*time.Millisecond
    502 		if !(elapsed >= expectMin) {
    503 			t.Errorf("#%d: got %v; want >= %v", i, elapsed, expectMin)
    504 		}
    505 		if !(elapsed <= expectMax) {
    506 			t.Errorf("#%d: got %v; want <= %v", i, elapsed, expectMax)
    507 		}
    508 	}
    509 }
    510 
    511 func TestDialSerialAsyncSpuriousConnection(t *testing.T) {
    512 	ln, err := newLocalListener("tcp")
    513 	if err != nil {
    514 		t.Fatal(err)
    515 	}
    516 	defer ln.Close()
    517 
    518 	d := Dialer{}
    519 	ctx := &dialContext{
    520 		Dialer:        d,
    521 		network:       "tcp",
    522 		address:       "?",
    523 		finalDeadline: d.deadline(time.Now()),
    524 	}
    525 
    526 	results := make(chan dialResult)
    527 	cancel := make(chan struct{})
    528 
    529 	// Spawn a connection in the background.
    530 	go dialSerialAsync(ctx, addrList{ln.Addr()}, nil, cancel, results)
    531 
    532 	// Receive it at the server.
    533 	c, err := ln.Accept()
    534 	if err != nil {
    535 		t.Fatal(err)
    536 	}
    537 	defer c.Close()
    538 
    539 	// Tell dialSerialAsync that someone else won the race.
    540 	close(cancel)
    541 
    542 	// The connection should close itself, without sending data.
    543 	c.SetReadDeadline(time.Now().Add(1 * time.Second))
    544 	var b [1]byte
    545 	if _, err := c.Read(b[:]); err != io.EOF {
    546 		t.Errorf("got %v; want %v", err, io.EOF)
    547 	}
    548 }
    549 
    550 func TestDialerPartialDeadline(t *testing.T) {
    551 	now := time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)
    552 	var testCases = []struct {
    553 		now            time.Time
    554 		deadline       time.Time
    555 		addrs          int
    556 		expectDeadline time.Time
    557 		expectErr      error
    558 	}{
    559 		// Regular division.
    560 		{now, now.Add(12 * time.Second), 1, now.Add(12 * time.Second), nil},
    561 		{now, now.Add(12 * time.Second), 2, now.Add(6 * time.Second), nil},
    562 		{now, now.Add(12 * time.Second), 3, now.Add(4 * time.Second), nil},
    563 		// Bump against the 2-second sane minimum.
    564 		{now, now.Add(12 * time.Second), 999, now.Add(2 * time.Second), nil},
    565 		// Total available is now below the sane minimum.
    566 		{now, now.Add(1900 * time.Millisecond), 999, now.Add(1900 * time.Millisecond), nil},
    567 		// Null deadline.
    568 		{now, noDeadline, 1, noDeadline, nil},
    569 		// Step the clock forward and cross the deadline.
    570 		{now.Add(-1 * time.Millisecond), now, 1, now, nil},
    571 		{now.Add(0 * time.Millisecond), now, 1, noDeadline, errTimeout},
    572 		{now.Add(1 * time.Millisecond), now, 1, noDeadline, errTimeout},
    573 	}
    574 	for i, tt := range testCases {
    575 		deadline, err := partialDeadline(tt.now, tt.deadline, tt.addrs)
    576 		if err != tt.expectErr {
    577 			t.Errorf("#%d: got %v; want %v", i, err, tt.expectErr)
    578 		}
    579 		if deadline != tt.expectDeadline {
    580 			t.Errorf("#%d: got %v; want %v", i, deadline, tt.expectDeadline)
    581 		}
    582 	}
    583 }
    584 
    585 func TestDialerLocalAddr(t *testing.T) {
    586 	ch := make(chan error, 1)
    587 	handler := func(ls *localServer, ln Listener) {
    588 		c, err := ln.Accept()
    589 		if err != nil {
    590 			ch <- err
    591 			return
    592 		}
    593 		defer c.Close()
    594 		ch <- nil
    595 	}
    596 	ls, err := newLocalServer("tcp")
    597 	if err != nil {
    598 		t.Fatal(err)
    599 	}
    600 	defer ls.teardown()
    601 	if err := ls.buildup(handler); err != nil {
    602 		t.Fatal(err)
    603 	}
    604 
    605 	laddr, err := ResolveTCPAddr(ls.Listener.Addr().Network(), ls.Listener.Addr().String())
    606 	if err != nil {
    607 		t.Fatal(err)
    608 	}
    609 	laddr.Port = 0
    610 	d := &Dialer{LocalAddr: laddr}
    611 	c, err := d.Dial(ls.Listener.Addr().Network(), ls.Addr().String())
    612 	if err != nil {
    613 		t.Fatal(err)
    614 	}
    615 	defer c.Close()
    616 	c.Read(make([]byte, 1))
    617 	err = <-ch
    618 	if err != nil {
    619 		t.Error(err)
    620 	}
    621 }
    622 
    623 func TestDialerDualStack(t *testing.T) {
    624 	if !supportsIPv4 || !supportsIPv6 {
    625 		t.Skip("both IPv4 and IPv6 are required")
    626 	}
    627 
    628 	closedPortDelay, expectClosedPortDelay := dialClosedPort()
    629 	if closedPortDelay > expectClosedPortDelay {
    630 		t.Errorf("got %v; want <= %v", closedPortDelay, expectClosedPortDelay)
    631 	}
    632 
    633 	origTestHookLookupIP := testHookLookupIP
    634 	defer func() { testHookLookupIP = origTestHookLookupIP }()
    635 	testHookLookupIP = lookupLocalhost
    636 	handler := func(dss *dualStackServer, ln Listener) {
    637 		for {
    638 			c, err := ln.Accept()
    639 			if err != nil {
    640 				return
    641 			}
    642 			c.Close()
    643 		}
    644 	}
    645 
    646 	var timeout = 100*time.Millisecond + closedPortDelay
    647 	for _, dualstack := range []bool{false, true} {
    648 		dss, err := newDualStackServer([]streamListener{
    649 			{network: "tcp4", address: "127.0.0.1"},
    650 			{network: "tcp6", address: "::1"},
    651 		})
    652 		if err != nil {
    653 			t.Fatal(err)
    654 		}
    655 		defer dss.teardown()
    656 		if err := dss.buildup(handler); err != nil {
    657 			t.Fatal(err)
    658 		}
    659 
    660 		d := &Dialer{DualStack: dualstack, Timeout: timeout}
    661 		for range dss.lns {
    662 			c, err := d.Dial("tcp", JoinHostPort("localhost", dss.port))
    663 			if err != nil {
    664 				t.Error(err)
    665 				continue
    666 			}
    667 			switch addr := c.LocalAddr().(*TCPAddr); {
    668 			case addr.IP.To4() != nil:
    669 				dss.teardownNetwork("tcp4")
    670 			case addr.IP.To16() != nil && addr.IP.To4() == nil:
    671 				dss.teardownNetwork("tcp6")
    672 			}
    673 			c.Close()
    674 		}
    675 	}
    676 	time.Sleep(timeout * 3 / 2) // wait for the dial racers to stop
    677 }
    678 
    679 func TestDialerKeepAlive(t *testing.T) {
    680 	handler := func(ls *localServer, ln Listener) {
    681 		for {
    682 			c, err := ln.Accept()
    683 			if err != nil {
    684 				return
    685 			}
    686 			c.Close()
    687 		}
    688 	}
    689 	ls, err := newLocalServer("tcp")
    690 	if err != nil {
    691 		t.Fatal(err)
    692 	}
    693 	defer ls.teardown()
    694 	if err := ls.buildup(handler); err != nil {
    695 		t.Fatal(err)
    696 	}
    697 	defer func() { testHookSetKeepAlive = func() {} }()
    698 
    699 	for _, keepAlive := range []bool{false, true} {
    700 		got := false
    701 		testHookSetKeepAlive = func() { got = true }
    702 		var d Dialer
    703 		if keepAlive {
    704 			d.KeepAlive = 30 * time.Second
    705 		}
    706 		c, err := d.Dial("tcp", ls.Listener.Addr().String())
    707 		if err != nil {
    708 			t.Fatal(err)
    709 		}
    710 		c.Close()
    711 		if got != keepAlive {
    712 			t.Errorf("Dialer.KeepAlive = %v: SetKeepAlive called = %v, want %v", d.KeepAlive, got, !got)
    713 		}
    714 	}
    715 }
    716