123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993 |
- // 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.
- //go:build !js
- package net
- import (
- "bufio"
- "context"
- "internal/testenv"
- "io"
- "os"
- "runtime"
- "strings"
- "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 := newLocalListener(t, "tcp")
- 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")
- }
- 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: 5 * time.Second}
- 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, network string, laddr, raddr *TCPAddr) (*TCPConn, error) {
- sd := &sysDialer{network: network, address: raddr.String()}
- c, err := sd.doDialTCP(ctx, 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(t *testing.T) (dialLatency time.Duration) {
- // On most platforms, dialing a closed port should be nearly instantaneous —
- // less than a few hundred milliseconds. However, on some platforms it may be
- // much slower: on Windows and OpenBSD, it has been observed to take up to a
- // few seconds.
- l, err := Listen("tcp", "127.0.0.1:0")
- if err != nil {
- t.Fatalf("dialClosedPort: Listen failed: %v", err)
- }
- addr := l.Addr().String()
- l.Close()
- startTime := time.Now()
- c, err := Dial("tcp", addr)
- if err == nil {
- c.Close()
- }
- elapsed := time.Now().Sub(startTime)
- t.Logf("dialClosedPort: measured delay %v", elapsed)
- return elapsed
- }
- func TestDialParallel(t *testing.T) {
- testenv.MustHaveExternalNetwork(t)
- if !supportsIPv4() || !supportsIPv6() {
- t.Skip("both IPv4 and IPv6 are required")
- }
- closedPortDelay := dialClosedPort(t)
- 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()
- sd := &sysDialer{
- Dialer: d,
- network: "tcp",
- address: "?",
- }
- c, err := sd.dialParallel(context.Background(), 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)
- }
- // We used to always use 95 milliseconds as the slop,
- // but that was flaky on Windows. See issue 35616.
- slop := 95 * time.Millisecond
- if fifth := tt.expectElapsed / 5; fifth > slop {
- slop = fifth
- }
- expectElapsedMin := tt.expectElapsed - slop
- expectElapsedMax := tt.expectElapsed + slop
- 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 = sd.dialParallel(ctx, 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, string) ([]IPAddr, error), network, 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, network, 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 readDeadline time.Time
- if td, ok := t.Deadline(); ok {
- const arbitraryCleanupMargin = 1 * time.Second
- readDeadline = td.Add(-arbitraryCleanupMargin)
- } else {
- readDeadline = time.Now().Add(5 * time.Second)
- }
- var closed sync.WaitGroup
- closed.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(readDeadline)
- var b [1]byte
- if _, err := c.Read(b[:]); err != io.EOF {
- t.Errorf("got %v; want %v", err, io.EOF)
- }
- c.Close()
- closed.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
- var dialing sync.WaitGroup
- dialing.Add(2)
- origTestHookDialTCP := testHookDialTCP
- defer func() { testHookDialTCP = origTestHookDialTCP }()
- testHookDialTCP = func(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
- // Wait until Happy Eyeballs kicks in and both connections are dialing,
- // and inhibit cancellation.
- // This forces dialParallel to juggle two successful connections.
- dialing.Done()
- dialing.Wait()
- // 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:
- sd := &sysDialer{network: net, address: raddr.String()}
- return sd.doDialTCP(context.Background(), laddr, raddr)
- }
- d := Dialer{
- FallbackDelay: fallbackDelay,
- }
- sd := &sysDialer{
- 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 := sd.dialParallel(context.Background(), makeAddr("127.0.0.1"), makeAddr("::1"))
- if err != nil {
- t.Fatal(err)
- }
- c.Close()
- // The server should've seen both connections.
- closed.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, errTimeout},
- {now.Add(1 * time.Millisecond), now, 1, noDeadline, 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)
- }
- }
- }
- // isEADDRINUSE reports whether err is syscall.EADDRINUSE.
- var isEADDRINUSE = func(err error) bool { return false }
- 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"}},
- }
- issue34264Index := -1
- if supportsIPv4map() {
- issue34264Index = len(tests)
- 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 lss [2]*localServer
- for i, network := range []string{"tcp4", "tcp6"} {
- lss[i] = newLocalServer(t, network)
- defer lss[i].teardown()
- if err := lss[i].buildup(handler); err != nil {
- t.Fatal(err)
- }
- }
- for i, 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 {
- if i == issue34264Index && runtime.GOOS == "freebsd" && isEADDRINUSE(err) {
- // https://golang.org/issue/34264: FreeBSD through at least version 12.2
- // has been observed to fail with EADDRINUSE when dialing from an IPv6
- // local address to an IPv4 remote address.
- t.Logf("%s %v->%s: got %v; want %v", tt.network, tt.laddr, tt.raddr, err, tt.error)
- t.Logf("(spurious EADDRINUSE ignored on freebsd: see https://golang.org/issue/34264)")
- } else {
- 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 := dialClosedPort(t)
- 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 := newLocalServer(t, "tcp")
- defer ls.teardown()
- if err := ls.buildup(handler); err != nil {
- t.Fatal(err)
- }
- defer func() { testHookSetKeepAlive = func(time.Duration) {} }()
- tests := []struct {
- ka time.Duration
- expected time.Duration
- }{
- {-1, -1},
- {0, 15 * time.Second},
- {5 * time.Second, 5 * time.Second},
- {30 * time.Second, 30 * time.Second},
- }
- for _, test := range tests {
- var got time.Duration = -1
- testHookSetKeepAlive = func(d time.Duration) { got = d }
- d := Dialer{KeepAlive: test.ka}
- c, err := d.Dial("tcp", ls.Listener.Addr().String())
- if err != nil {
- t.Fatal(err)
- }
- c.Close()
- if got != test.expected {
- t.Errorf("Dialer.KeepAlive = %v: SetKeepAlive set to %v, want %v", d.KeepAlive, got, test.expected)
- }
- }
- }
- func TestDialCancel(t *testing.T) {
- mustHaveExternalNetwork(t)
- 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 {
- // Using strings.Contains is ugly but
- // may work on plan9 and windows.
- if strings.Contains(err.Error(), "connection refused") {
- t.Skipf("connection to %v failed fast with %v", blackholeIPPort, err)
- }
- 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 := newLocalListener(t, "tcp")
- 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 cancellation 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) {
- 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()
- }
- func TestDialerControl(t *testing.T) {
- switch runtime.GOOS {
- case "plan9":
- t.Skipf("not supported on %s", runtime.GOOS)
- }
- t.Run("StreamDial", func(t *testing.T) {
- for _, network := range []string{"tcp", "tcp4", "tcp6", "unix", "unixpacket"} {
- if !testableNetwork(network) {
- continue
- }
- ln := newLocalListener(t, network)
- defer ln.Close()
- d := Dialer{Control: controlOnConnSetup}
- c, err := d.Dial(network, ln.Addr().String())
- if err != nil {
- t.Error(err)
- continue
- }
- c.Close()
- }
- })
- t.Run("PacketDial", func(t *testing.T) {
- for _, network := range []string{"udp", "udp4", "udp6", "unixgram"} {
- if !testableNetwork(network) {
- continue
- }
- c1 := newLocalPacketListener(t, network)
- if network == "unixgram" {
- defer os.Remove(c1.LocalAddr().String())
- }
- defer c1.Close()
- d := Dialer{Control: controlOnConnSetup}
- c2, err := d.Dial(network, c1.LocalAddr().String())
- if err != nil {
- t.Error(err)
- continue
- }
- c2.Close()
- }
- })
- }
- // mustHaveExternalNetwork is like testenv.MustHaveExternalNetwork
- // except that it won't skip testing on non-mobile builders.
- func mustHaveExternalNetwork(t *testing.T) {
- t.Helper()
- mobile := runtime.GOOS == "android" || runtime.GOOS == "ios"
- if testenv.Builder() == "" || mobile {
- testenv.MustHaveExternalNetwork(t)
- }
- }
- type contextWithNonZeroDeadline struct {
- context.Context
- }
- func (contextWithNonZeroDeadline) Deadline() (time.Time, bool) {
- // Return non-zero time.Time value with false indicating that no deadline is set.
- return time.Unix(0, 0), false
- }
- func TestDialWithNonZeroDeadline(t *testing.T) {
- ln := newLocalListener(t, "tcp")
- defer ln.Close()
- _, port, err := SplitHostPort(ln.Addr().String())
- if err != nil {
- t.Fatal(err)
- }
- ctx := contextWithNonZeroDeadline{Context: context.Background()}
- var dialer Dialer
- c, err := dialer.DialContext(ctx, "tcp", JoinHostPort("", port))
- if err != nil {
- t.Fatal(err)
- }
- c.Close()
- }
|