Skip to content

Instantly share code, notes, and snippets.

@akutz
Last active October 24, 2017 18:48
Show Gist options
  • Save akutz/b2c2c14125478854076b8b7729cc377e to your computer and use it in GitHub Desktop.
Save akutz/b2c2c14125478854076b8b7729cc377e to your computer and use it in GitHub Desktop.
Building Go on macOS High Sierra

When Go is built from source on macOS High Seirra, two errors may occur during the test phase:

  1. Network Timeout
  2. X509 Chain Count

Network Timeout

This bug is a known issue, golang#22062.

--- FAIL: TestDialerDualStackFDLeak (0.20s)
	dial_test.go:90: got 100.766057ms; want <= 95ms

This gist includes the patched source for Go 1.9.1 as well as the patch itself.

X509 Chain Count

This is also a known issue, golang#21416.

--- FAIL: TestSystemRoots (1.41s)
root_darwin_test.go:31: cgo sys roots: 443.728642ms
root_darwin_test.go:32: non-cgo sys roots: 964.424609ms
root_darwin_test.go:44: got 169 roots
root_darwin_test.go:44: got 453 roots
root_darwin_test.go:73: insufficient overlap between cgo and non-cgo roots; want at least 226, have 168

Due to the sensitive nature of the package involved, instead of copying the patch to this Gist, here is the link to the original patch. Many thanks to reddit user dlsniper for pointing this out!

// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package net
import (
"bufio"
"context"
"internal/poll"
"internal/testenv"
"io"
"runtime"
"sync"
"testing"
"time"
)
var prohibitionaryDialArgTests = []struct {
network string
address string
}{
{"tcp6", "127.0.0.1"},
{"tcp6", "::ffff:127.0.0.1"},
}
func TestProhibitionaryDialArg(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
switch runtime.GOOS {
case "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
if !supportsIPv4map() {
t.Skip("mapping ipv4 address inside ipv6 address not supported")
}
ln, err := Listen("tcp", "[::]:0")
if err != nil {
t.Fatal(err)
}
defer ln.Close()
_, port, err := SplitHostPort(ln.Addr().String())
if err != nil {
t.Fatal(err)
}
for i, tt := range prohibitionaryDialArgTests {
c, err := Dial(tt.network, JoinHostPort(tt.address, port))
if err == nil {
c.Close()
t.Errorf("#%d: %v", i, err)
}
}
}
func TestDialLocal(t *testing.T) {
ln, err := newLocalListener("tcp")
if err != nil {
t.Fatal(err)
}
defer ln.Close()
_, port, err := SplitHostPort(ln.Addr().String())
if err != nil {
t.Fatal(err)
}
c, err := Dial("tcp", JoinHostPort("", port))
if err != nil {
t.Fatal(err)
}
c.Close()
}
func TestDialerDualStackFDLeak(t *testing.T) {
switch runtime.GOOS {
case "plan9":
t.Skipf("%s does not have full support of socktest", runtime.GOOS)
case "windows":
t.Skipf("not implemented a way to cancel dial racers in TCP SYN-SENT state on %s", runtime.GOOS)
case "openbsd":
testenv.SkipFlaky(t, 15157)
}
if !supportsIPv4() || !supportsIPv6() {
t.Skip("both IPv4 and IPv6 are required")
}
closedPortDelay, expectClosedPortDelay := dialClosedPort()
if closedPortDelay > expectClosedPortDelay {
t.Errorf("got %v; want <= %v", closedPortDelay, expectClosedPortDelay)
}
before := sw.Sockets()
origTestHookLookupIP := testHookLookupIP
defer func() { testHookLookupIP = origTestHookLookupIP }()
testHookLookupIP = lookupLocalhost
handler := func(dss *dualStackServer, ln Listener) {
for {
c, err := ln.Accept()
if err != nil {
return
}
c.Close()
}
}
dss, err := newDualStackServer()
if err != nil {
t.Fatal(err)
}
if err := dss.buildup(handler); err != nil {
dss.teardown()
t.Fatal(err)
}
const N = 10
var wg sync.WaitGroup
wg.Add(N)
d := &Dialer{DualStack: true, Timeout: 100*time.Millisecond + closedPortDelay}
for i := 0; i < N; i++ {
go func() {
defer wg.Done()
c, err := d.Dial("tcp", JoinHostPort("localhost", dss.port))
if err != nil {
t.Error(err)
return
}
c.Close()
}()
}
wg.Wait()
dss.teardown()
after := sw.Sockets()
if len(after) != len(before) {
t.Errorf("got %d; want %d", len(after), len(before))
}
}
// Define a pair of blackholed (IPv4, IPv6) addresses, for which dialTCP is
// expected to hang until the timeout elapses. These addresses are reserved
// for benchmarking by RFC 6890.
const (
slowDst4 = "198.18.0.254"
slowDst6 = "2001:2::254"
)
// In some environments, the slow IPs may be explicitly unreachable, and fail
// more quickly than expected. This test hook prevents dialTCP from returning
// before the deadline.
func slowDialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
c, err := doDialTCP(ctx, net, laddr, raddr)
if ParseIP(slowDst4).Equal(raddr.IP) || ParseIP(slowDst6).Equal(raddr.IP) {
// Wait for the deadline, or indefinitely if none exists.
<-ctx.Done()
}
return c, err
}
func dialClosedPort() (actual, expected time.Duration) {
// Estimate the expected time for this platform.
// On Windows, dialing a closed port takes roughly 1 second,
// but other platforms should be instantaneous.
if runtime.GOOS == "windows" {
expected = 1500 * time.Millisecond
} else if runtime.GOOS == "darwin" {
expected = 150 * time.Millisecond
} else {
expected = 95 * time.Millisecond
}
l, err := Listen("tcp", "127.0.0.1:0")
if err != nil {
return 999 * time.Hour, expected
}
addr := l.Addr().String()
l.Close()
// On OpenBSD, interference from TestSelfConnect is mysteriously
// causing the first attempt to hang for a few seconds, so we throw
// away the first result and keep the second.
for i := 1; ; i++ {
startTime := time.Now()
c, err := Dial("tcp", addr)
if err == nil {
c.Close()
}
elapsed := time.Now().Sub(startTime)
if i == 2 {
return elapsed, expected
}
}
}
func TestDialParallel(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
if !supportsIPv4() || !supportsIPv6() {
t.Skip("both IPv4 and IPv6 are required")
}
closedPortDelay, expectClosedPortDelay := dialClosedPort()
if closedPortDelay > expectClosedPortDelay {
t.Errorf("got %v; want <= %v", closedPortDelay, expectClosedPortDelay)
}
const instant time.Duration = 0
const fallbackDelay = 200 * time.Millisecond
// Some cases will run quickly when "connection refused" is fast,
// or trigger the fallbackDelay on Windows. This value holds the
// lesser of the two delays.
var closedPortOrFallbackDelay time.Duration
if closedPortDelay < fallbackDelay {
closedPortOrFallbackDelay = closedPortDelay
} else {
closedPortOrFallbackDelay = fallbackDelay
}
origTestHookDialTCP := testHookDialTCP
defer func() { testHookDialTCP = origTestHookDialTCP }()
testHookDialTCP = slowDialTCP
nCopies := func(s string, n int) []string {
out := make([]string, n)
for i := 0; i < n; i++ {
out[i] = s
}
return out
}
var testCases = []struct {
primaries []string
fallbacks []string
teardownNetwork string
expectOk bool
expectElapsed time.Duration
}{
// These should just work on the first try.
{[]string{"127.0.0.1"}, []string{}, "", true, instant},
{[]string{"::1"}, []string{}, "", true, instant},
{[]string{"127.0.0.1", "::1"}, []string{slowDst6}, "tcp6", true, instant},
{[]string{"::1", "127.0.0.1"}, []string{slowDst4}, "tcp4", true, instant},
// Primary is slow; fallback should kick in.
{[]string{slowDst4}, []string{"::1"}, "", true, fallbackDelay},
// Skip a "connection refused" in the primary thread.
{[]string{"127.0.0.1", "::1"}, []string{}, "tcp4", true, closedPortDelay},
{[]string{"::1", "127.0.0.1"}, []string{}, "tcp6", true, closedPortDelay},
// Skip a "connection refused" in the fallback thread.
{[]string{slowDst4, slowDst6}, []string{"::1", "127.0.0.1"}, "tcp6", true, fallbackDelay + closedPortDelay},
// Primary refused, fallback without delay.
{[]string{"127.0.0.1"}, []string{"::1"}, "tcp4", true, closedPortOrFallbackDelay},
{[]string{"::1"}, []string{"127.0.0.1"}, "tcp6", true, closedPortOrFallbackDelay},
// Everything is refused.
{[]string{"127.0.0.1"}, []string{}, "tcp4", false, closedPortDelay},
// Nothing to do; fail instantly.
{[]string{}, []string{}, "", false, instant},
// Connecting to tons of addresses should not trip the deadline.
{nCopies("::1", 1000), []string{}, "", true, instant},
}
handler := func(dss *dualStackServer, ln Listener) {
for {
c, err := ln.Accept()
if err != nil {
return
}
c.Close()
}
}
// Convert a list of IP strings into TCPAddrs.
makeAddrs := func(ips []string, port string) addrList {
var out addrList
for _, ip := range ips {
addr, err := ResolveTCPAddr("tcp", JoinHostPort(ip, port))
if err != nil {
t.Fatal(err)
}
out = append(out, addr)
}
return out
}
for i, tt := range testCases {
dss, err := newDualStackServer()
if err != nil {
t.Fatal(err)
}
defer dss.teardown()
if err := dss.buildup(handler); err != nil {
t.Fatal(err)
}
if tt.teardownNetwork != "" {
// Destroy one of the listening sockets, creating an unreachable port.
dss.teardownNetwork(tt.teardownNetwork)
}
primaries := makeAddrs(tt.primaries, dss.port)
fallbacks := makeAddrs(tt.fallbacks, dss.port)
d := Dialer{
FallbackDelay: fallbackDelay,
}
startTime := time.Now()
dp := &dialParam{
Dialer: d,
network: "tcp",
address: "?",
}
c, err := dialParallel(context.Background(), dp, primaries, fallbacks)
elapsed := time.Since(startTime)
if c != nil {
c.Close()
}
if tt.expectOk && err != nil {
t.Errorf("#%d: got %v; want nil", i, err)
} else if !tt.expectOk && err == nil {
t.Errorf("#%d: got nil; want non-nil", i)
}
expectElapsedMin := tt.expectElapsed - 95*time.Millisecond
expectElapsedMax := tt.expectElapsed + 95*time.Millisecond
if !(elapsed >= expectElapsedMin) {
t.Errorf("#%d: got %v; want >= %v", i, elapsed, expectElapsedMin)
} else if !(elapsed <= expectElapsedMax) {
t.Errorf("#%d: got %v; want <= %v", i, elapsed, expectElapsedMax)
}
// Repeat each case, ensuring that it can be canceled quickly.
ctx, cancel := context.WithCancel(context.Background())
var wg sync.WaitGroup
wg.Add(1)
go func() {
time.Sleep(5 * time.Millisecond)
cancel()
wg.Done()
}()
startTime = time.Now()
c, err = dialParallel(ctx, dp, primaries, fallbacks)
if c != nil {
c.Close()
}
elapsed = time.Now().Sub(startTime)
if elapsed > 100*time.Millisecond {
t.Errorf("#%d (cancel): got %v; want <= 100ms", i, elapsed)
}
wg.Wait()
}
}
func lookupSlowFast(ctx context.Context, fn func(context.Context, string) ([]IPAddr, error), host string) ([]IPAddr, error) {
switch host {
case "slow6loopback4":
// Returns a slow IPv6 address, and a local IPv4 address.
return []IPAddr{
{IP: ParseIP(slowDst6)},
{IP: ParseIP("127.0.0.1")},
}, nil
default:
return fn(ctx, host)
}
}
func TestDialerFallbackDelay(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
if !supportsIPv4() || !supportsIPv6() {
t.Skip("both IPv4 and IPv6 are required")
}
origTestHookLookupIP := testHookLookupIP
defer func() { testHookLookupIP = origTestHookLookupIP }()
testHookLookupIP = lookupSlowFast
origTestHookDialTCP := testHookDialTCP
defer func() { testHookDialTCP = origTestHookDialTCP }()
testHookDialTCP = slowDialTCP
var testCases = []struct {
dualstack bool
delay time.Duration
expectElapsed time.Duration
}{
// Use a very brief delay, which should fallback immediately.
{true, 1 * time.Nanosecond, 0},
// Use a 200ms explicit timeout.
{true, 200 * time.Millisecond, 200 * time.Millisecond},
// The default is 300ms.
{true, 0, 300 * time.Millisecond},
}
handler := func(dss *dualStackServer, ln Listener) {
for {
c, err := ln.Accept()
if err != nil {
return
}
c.Close()
}
}
dss, err := newDualStackServer()
if err != nil {
t.Fatal(err)
}
defer dss.teardown()
if err := dss.buildup(handler); err != nil {
t.Fatal(err)
}
for i, tt := range testCases {
d := &Dialer{DualStack: tt.dualstack, FallbackDelay: tt.delay}
startTime := time.Now()
c, err := d.Dial("tcp", JoinHostPort("slow6loopback4", dss.port))
elapsed := time.Now().Sub(startTime)
if err == nil {
c.Close()
} else if tt.dualstack {
t.Error(err)
}
expectMin := tt.expectElapsed - 1*time.Millisecond
expectMax := tt.expectElapsed + 95*time.Millisecond
if !(elapsed >= expectMin) {
t.Errorf("#%d: got %v; want >= %v", i, elapsed, expectMin)
}
if !(elapsed <= expectMax) {
t.Errorf("#%d: got %v; want <= %v", i, elapsed, expectMax)
}
}
}
func TestDialParallelSpuriousConnection(t *testing.T) {
if !supportsIPv4() || !supportsIPv6() {
t.Skip("both IPv4 and IPv6 are required")
}
var wg sync.WaitGroup
wg.Add(2)
handler := func(dss *dualStackServer, ln Listener) {
// Accept one connection per address.
c, err := ln.Accept()
if err != nil {
t.Fatal(err)
}
// The client should close itself, without sending data.
c.SetReadDeadline(time.Now().Add(1 * time.Second))
var b [1]byte
if _, err := c.Read(b[:]); err != io.EOF {
t.Errorf("got %v; want %v", err, io.EOF)
}
c.Close()
wg.Done()
}
dss, err := newDualStackServer()
if err != nil {
t.Fatal(err)
}
defer dss.teardown()
if err := dss.buildup(handler); err != nil {
t.Fatal(err)
}
const fallbackDelay = 100 * time.Millisecond
origTestHookDialTCP := testHookDialTCP
defer func() { testHookDialTCP = origTestHookDialTCP }()
testHookDialTCP = func(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
// Sleep long enough for Happy Eyeballs to kick in, and inhibit cancelation.
// This forces dialParallel to juggle two successful connections.
time.Sleep(fallbackDelay * 2)
// Now ignore the provided context (which will be canceled) and use a
// different one to make sure this completes with a valid connection,
// which we hope to be closed below:
return doDialTCP(context.Background(), net, laddr, raddr)
}
d := Dialer{
FallbackDelay: fallbackDelay,
}
dp := &dialParam{
Dialer: d,
network: "tcp",
address: "?",
}
makeAddr := func(ip string) addrList {
addr, err := ResolveTCPAddr("tcp", JoinHostPort(ip, dss.port))
if err != nil {
t.Fatal(err)
}
return addrList{addr}
}
// dialParallel returns one connection (and closes the other.)
c, err := dialParallel(context.Background(), dp, makeAddr("127.0.0.1"), makeAddr("::1"))
if err != nil {
t.Fatal(err)
}
c.Close()
// The server should've seen both connections.
wg.Wait()
}
func TestDialerPartialDeadline(t *testing.T) {
now := time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)
var testCases = []struct {
now time.Time
deadline time.Time
addrs int
expectDeadline time.Time
expectErr error
}{
// Regular division.
{now, now.Add(12 * time.Second), 1, now.Add(12 * time.Second), nil},
{now, now.Add(12 * time.Second), 2, now.Add(6 * time.Second), nil},
{now, now.Add(12 * time.Second), 3, now.Add(4 * time.Second), nil},
// Bump against the 2-second sane minimum.
{now, now.Add(12 * time.Second), 999, now.Add(2 * time.Second), nil},
// Total available is now below the sane minimum.
{now, now.Add(1900 * time.Millisecond), 999, now.Add(1900 * time.Millisecond), nil},
// Null deadline.
{now, noDeadline, 1, noDeadline, nil},
// Step the clock forward and cross the deadline.
{now.Add(-1 * time.Millisecond), now, 1, now, nil},
{now.Add(0 * time.Millisecond), now, 1, noDeadline, poll.ErrTimeout},
{now.Add(1 * time.Millisecond), now, 1, noDeadline, poll.ErrTimeout},
}
for i, tt := range testCases {
deadline, err := partialDeadline(tt.now, tt.deadline, tt.addrs)
if err != tt.expectErr {
t.Errorf("#%d: got %v; want %v", i, err, tt.expectErr)
}
if !deadline.Equal(tt.expectDeadline) {
t.Errorf("#%d: got %v; want %v", i, deadline, tt.expectDeadline)
}
}
}
func TestDialerLocalAddr(t *testing.T) {
if !supportsIPv4() || !supportsIPv6() {
t.Skip("both IPv4 and IPv6 are required")
}
type test struct {
network, raddr string
laddr Addr
error
}
var tests = []test{
{"tcp4", "127.0.0.1", nil, nil},
{"tcp4", "127.0.0.1", &TCPAddr{}, nil},
{"tcp4", "127.0.0.1", &TCPAddr{IP: ParseIP("0.0.0.0")}, nil},
{"tcp4", "127.0.0.1", &TCPAddr{IP: ParseIP("0.0.0.0").To4()}, nil},
{"tcp4", "127.0.0.1", &TCPAddr{IP: ParseIP("::")}, &AddrError{Err: "some error"}},
{"tcp4", "127.0.0.1", &TCPAddr{IP: ParseIP("127.0.0.1").To4()}, nil},
{"tcp4", "127.0.0.1", &TCPAddr{IP: ParseIP("127.0.0.1").To16()}, nil},
{"tcp4", "127.0.0.1", &TCPAddr{IP: IPv6loopback}, errNoSuitableAddress},
{"tcp4", "127.0.0.1", &UDPAddr{}, &AddrError{Err: "some error"}},
{"tcp4", "127.0.0.1", &UnixAddr{}, &AddrError{Err: "some error"}},
{"tcp6", "::1", nil, nil},
{"tcp6", "::1", &TCPAddr{}, nil},
{"tcp6", "::1", &TCPAddr{IP: ParseIP("0.0.0.0")}, nil},
{"tcp6", "::1", &TCPAddr{IP: ParseIP("0.0.0.0").To4()}, nil},
{"tcp6", "::1", &TCPAddr{IP: ParseIP("::")}, nil},
{"tcp6", "::1", &TCPAddr{IP: ParseIP("127.0.0.1").To4()}, errNoSuitableAddress},
{"tcp6", "::1", &TCPAddr{IP: ParseIP("127.0.0.1").To16()}, errNoSuitableAddress},
{"tcp6", "::1", &TCPAddr{IP: IPv6loopback}, nil},
{"tcp6", "::1", &UDPAddr{}, &AddrError{Err: "some error"}},
{"tcp6", "::1", &UnixAddr{}, &AddrError{Err: "some error"}},
{"tcp", "127.0.0.1", nil, nil},
{"tcp", "127.0.0.1", &TCPAddr{}, nil},
{"tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("0.0.0.0")}, nil},
{"tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("0.0.0.0").To4()}, nil},
{"tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("127.0.0.1").To4()}, nil},
{"tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("127.0.0.1").To16()}, nil},
{"tcp", "127.0.0.1", &TCPAddr{IP: IPv6loopback}, errNoSuitableAddress},
{"tcp", "127.0.0.1", &UDPAddr{}, &AddrError{Err: "some error"}},
{"tcp", "127.0.0.1", &UnixAddr{}, &AddrError{Err: "some error"}},
{"tcp", "::1", nil, nil},
{"tcp", "::1", &TCPAddr{}, nil},
{"tcp", "::1", &TCPAddr{IP: ParseIP("0.0.0.0")}, nil},
{"tcp", "::1", &TCPAddr{IP: ParseIP("0.0.0.0").To4()}, nil},
{"tcp", "::1", &TCPAddr{IP: ParseIP("::")}, nil},
{"tcp", "::1", &TCPAddr{IP: ParseIP("127.0.0.1").To4()}, errNoSuitableAddress},
{"tcp", "::1", &TCPAddr{IP: ParseIP("127.0.0.1").To16()}, errNoSuitableAddress},
{"tcp", "::1", &TCPAddr{IP: IPv6loopback}, nil},
{"tcp", "::1", &UDPAddr{}, &AddrError{Err: "some error"}},
{"tcp", "::1", &UnixAddr{}, &AddrError{Err: "some error"}},
}
if supportsIPv4map() {
tests = append(tests, test{
"tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("::")}, nil,
})
} else {
tests = append(tests, test{
"tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("::")}, &AddrError{Err: "some error"},
})
}
origTestHookLookupIP := testHookLookupIP
defer func() { testHookLookupIP = origTestHookLookupIP }()
testHookLookupIP = lookupLocalhost
handler := func(ls *localServer, ln Listener) {
for {
c, err := ln.Accept()
if err != nil {
return
}
c.Close()
}
}
var err error
var lss [2]*localServer
for i, network := range []string{"tcp4", "tcp6"} {
lss[i], err = newLocalServer(network)
if err != nil {
t.Fatal(err)
}
defer lss[i].teardown()
if err := lss[i].buildup(handler); err != nil {
t.Fatal(err)
}
}
for _, tt := range tests {
d := &Dialer{LocalAddr: tt.laddr}
var addr string
ip := ParseIP(tt.raddr)
if ip.To4() != nil {
addr = lss[0].Listener.Addr().String()
}
if ip.To16() != nil && ip.To4() == nil {
addr = lss[1].Listener.Addr().String()
}
c, err := d.Dial(tt.network, addr)
if err == nil && tt.error != nil || err != nil && tt.error == nil {
t.Errorf("%s %v->%s: got %v; want %v", tt.network, tt.laddr, tt.raddr, err, tt.error)
}
if err != nil {
if perr := parseDialError(err); perr != nil {
t.Error(perr)
}
continue
}
c.Close()
}
}
func TestDialerDualStack(t *testing.T) {
testenv.SkipFlaky(t, 13324)
if !supportsIPv4() || !supportsIPv6() {
t.Skip("both IPv4 and IPv6 are required")
}
closedPortDelay, expectClosedPortDelay := dialClosedPort()
if closedPortDelay > expectClosedPortDelay {
t.Errorf("got %v; want <= %v", closedPortDelay, expectClosedPortDelay)
}
origTestHookLookupIP := testHookLookupIP
defer func() { testHookLookupIP = origTestHookLookupIP }()
testHookLookupIP = lookupLocalhost
handler := func(dss *dualStackServer, ln Listener) {
for {
c, err := ln.Accept()
if err != nil {
return
}
c.Close()
}
}
var timeout = 150*time.Millisecond + closedPortDelay
for _, dualstack := range []bool{false, true} {
dss, err := newDualStackServer()
if err != nil {
t.Fatal(err)
}
defer dss.teardown()
if err := dss.buildup(handler); err != nil {
t.Fatal(err)
}
d := &Dialer{DualStack: dualstack, Timeout: timeout}
for range dss.lns {
c, err := d.Dial("tcp", JoinHostPort("localhost", dss.port))
if err != nil {
t.Error(err)
continue
}
switch addr := c.LocalAddr().(*TCPAddr); {
case addr.IP.To4() != nil:
dss.teardownNetwork("tcp4")
case addr.IP.To16() != nil && addr.IP.To4() == nil:
dss.teardownNetwork("tcp6")
}
c.Close()
}
}
}
func TestDialerKeepAlive(t *testing.T) {
handler := func(ls *localServer, ln Listener) {
for {
c, err := ln.Accept()
if err != nil {
return
}
c.Close()
}
}
ls, err := newLocalServer("tcp")
if err != nil {
t.Fatal(err)
}
defer ls.teardown()
if err := ls.buildup(handler); err != nil {
t.Fatal(err)
}
defer func() { testHookSetKeepAlive = func() {} }()
for _, keepAlive := range []bool{false, true} {
got := false
testHookSetKeepAlive = func() { got = true }
var d Dialer
if keepAlive {
d.KeepAlive = 30 * time.Second
}
c, err := d.Dial("tcp", ls.Listener.Addr().String())
if err != nil {
t.Fatal(err)
}
c.Close()
if got != keepAlive {
t.Errorf("Dialer.KeepAlive = %v: SetKeepAlive called = %v, want %v", d.KeepAlive, got, !got)
}
}
}
func TestDialCancel(t *testing.T) {
switch testenv.Builder() {
case "linux-arm64-buildlet":
t.Skip("skipping on linux-arm64-buildlet; incompatible network config? issue 15191")
case "":
testenv.MustHaveExternalNetwork(t)
}
if runtime.GOOS == "nacl" {
// nacl doesn't have external network access.
t.Skipf("skipping on %s", runtime.GOOS)
}
blackholeIPPort := JoinHostPort(slowDst4, "1234")
if !supportsIPv4() {
blackholeIPPort = JoinHostPort(slowDst6, "1234")
}
ticker := time.NewTicker(10 * time.Millisecond)
defer ticker.Stop()
const cancelTick = 5 // the timer tick we cancel the dial at
const timeoutTick = 100
var d Dialer
cancel := make(chan struct{})
d.Cancel = cancel
errc := make(chan error, 1)
connc := make(chan Conn, 1)
go func() {
if c, err := d.Dial("tcp", blackholeIPPort); err != nil {
errc <- err
} else {
connc <- c
}
}()
ticks := 0
for {
select {
case <-ticker.C:
ticks++
if ticks == cancelTick {
close(cancel)
}
if ticks == timeoutTick {
t.Fatal("timeout waiting for dial to fail")
}
case c := <-connc:
c.Close()
t.Fatal("unexpected successful connection")
case err := <-errc:
if perr := parseDialError(err); perr != nil {
t.Error(perr)
}
if ticks < cancelTick {
t.Fatalf("dial error after %d ticks (%d before cancel sent): %v",
ticks, cancelTick-ticks, err)
}
if oe, ok := err.(*OpError); !ok || oe.Err != errCanceled {
t.Fatalf("dial error = %v (%T); want OpError with Err == errCanceled", err, err)
}
return // success.
}
}
}
func TestCancelAfterDial(t *testing.T) {
if testing.Short() {
t.Skip("avoiding time.Sleep")
}
ln, err := newLocalListener("tcp")
if err != nil {
t.Fatal(err)
}
var wg sync.WaitGroup
wg.Add(1)
defer func() {
ln.Close()
wg.Wait()
}()
// Echo back the first line of each incoming connection.
go func() {
for {
c, err := ln.Accept()
if err != nil {
break
}
rb := bufio.NewReader(c)
line, err := rb.ReadString('\n')
if err != nil {
t.Error(err)
c.Close()
continue
}
if _, err := c.Write([]byte(line)); err != nil {
t.Error(err)
}
c.Close()
}
wg.Done()
}()
try := func() {
cancel := make(chan struct{})
d := &Dialer{Cancel: cancel}
c, err := d.Dial("tcp", ln.Addr().String())
// Immediately after dialing, request cancelation and sleep.
// Before Issue 15078 was fixed, this would cause subsequent operations
// to fail with an i/o timeout roughly 50% of the time.
close(cancel)
time.Sleep(10 * time.Millisecond)
if err != nil {
t.Fatal(err)
}
defer c.Close()
// Send some data to confirm that the connection is still alive.
const message = "echo!\n"
if _, err := c.Write([]byte(message)); err != nil {
t.Fatal(err)
}
// The server should echo the line, and close the connection.
rb := bufio.NewReader(c)
line, err := rb.ReadString('\n')
if err != nil {
t.Fatal(err)
}
if line != message {
t.Errorf("got %q; want %q", line, message)
}
if _, err := rb.ReadByte(); err != io.EOF {
t.Errorf("got %v; want %v", err, io.EOF)
}
}
// This bug manifested about 50% of the time, so try it a few times.
for i := 0; i < 10; i++ {
try()
}
}
// Issue 18806: it should always be possible to net.Dial a
// net.Listener().Addr().String when the listen address was ":n", even
// if the machine has halfway configured IPv6 such that it can bind on
// "::" not connect back to that same address.
func TestDialListenerAddr(t *testing.T) {
if testenv.Builder() == "" {
testenv.MustHaveExternalNetwork(t)
}
ln, err := Listen("tcp", ":0")
if err != nil {
t.Fatal(err)
}
defer ln.Close()
addr := ln.Addr().String()
c, err := Dial("tcp", addr)
if err != nil {
t.Fatalf("for addr %q, dial error: %v", addr, err)
}
c.Close()
}
164c164,166
< } else {
---
> } else if runtime.GOOS == "darwin" {
> expected = 150 * time.Millisecond
> } else {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment