github.com/hbdrawn/golang@v0.0.0-20141214014649-6b835209aba2/src/net/dial_test.go (about)

     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  	"bytes"
     9  	"flag"
    10  	"fmt"
    11  	"io"
    12  	"os"
    13  	"os/exec"
    14  	"reflect"
    15  	"regexp"
    16  	"runtime"
    17  	"strconv"
    18  	"sync"
    19  	"testing"
    20  	"time"
    21  )
    22  
    23  func newLocalListener(t *testing.T) Listener {
    24  	ln, err := Listen("tcp", "127.0.0.1:0")
    25  	if err != nil {
    26  		ln, err = Listen("tcp6", "[::1]:0")
    27  	}
    28  	if err != nil {
    29  		t.Fatal(err)
    30  	}
    31  	return ln
    32  }
    33  
    34  func TestDialTimeout(t *testing.T) {
    35  	origBacklog := listenerBacklog
    36  	defer func() {
    37  		listenerBacklog = origBacklog
    38  	}()
    39  	listenerBacklog = 1
    40  
    41  	ln := newLocalListener(t)
    42  	defer ln.Close()
    43  
    44  	errc := make(chan error)
    45  
    46  	numConns := listenerBacklog + 100
    47  
    48  	// TODO(bradfitz): It's hard to test this in a portable
    49  	// way. This is unfortunate, but works for now.
    50  	switch runtime.GOOS {
    51  	case "linux":
    52  		// The kernel will start accepting TCP connections before userspace
    53  		// gets a chance to not accept them, so fire off a bunch to fill up
    54  		// the kernel's backlog.  Then we test we get a failure after that.
    55  		for i := 0; i < numConns; i++ {
    56  			go func() {
    57  				_, err := DialTimeout("tcp", ln.Addr().String(), 200*time.Millisecond)
    58  				errc <- err
    59  			}()
    60  		}
    61  	case "darwin", "plan9", "windows":
    62  		// At least OS X 10.7 seems to accept any number of
    63  		// connections, ignoring listen's backlog, so resort
    64  		// to connecting to a hopefully-dead 127/8 address.
    65  		// Same for windows.
    66  		//
    67  		// Use an IANA reserved port (49151) instead of 80, because
    68  		// on our 386 builder, this Dial succeeds, connecting
    69  		// to an IIS web server somewhere.  The data center
    70  		// or VM or firewall must be stealing the TCP connection.
    71  		//
    72  		// IANA Service Name and Transport Protocol Port Number Registry
    73  		// <http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml>
    74  		go func() {
    75  			c, err := DialTimeout("tcp", "127.0.71.111:49151", 200*time.Millisecond)
    76  			if err == nil {
    77  				err = fmt.Errorf("unexpected: connected to %s!", c.RemoteAddr())
    78  				c.Close()
    79  			}
    80  			errc <- err
    81  		}()
    82  	default:
    83  		// TODO(bradfitz):
    84  		// OpenBSD may have a reject route to 127/8 except 127.0.0.1/32
    85  		// by default. FreeBSD likely works, but is untested.
    86  		// TODO(rsc):
    87  		// The timeout never happens on Windows.  Why?  Issue 3016.
    88  		t.Skipf("skipping test on %q; untested.", runtime.GOOS)
    89  	}
    90  
    91  	connected := 0
    92  	for {
    93  		select {
    94  		case <-time.After(15 * time.Second):
    95  			t.Fatal("too slow")
    96  		case err := <-errc:
    97  			if err == nil {
    98  				connected++
    99  				if connected == numConns {
   100  					t.Fatal("all connections connected; expected some to time out")
   101  				}
   102  			} else {
   103  				terr, ok := err.(timeout)
   104  				if !ok {
   105  					t.Fatalf("got error %q; want error with timeout interface", err)
   106  				}
   107  				if !terr.Timeout() {
   108  					t.Fatalf("got error %q; not a timeout", err)
   109  				}
   110  				// Pass. We saw a timeout error.
   111  				return
   112  			}
   113  		}
   114  	}
   115  }
   116  
   117  func TestSelfConnect(t *testing.T) {
   118  	if runtime.GOOS == "windows" {
   119  		// TODO(brainman): do not know why it hangs.
   120  		t.Skip("skipping known-broken test on windows")
   121  	}
   122  
   123  	// Test that Dial does not honor self-connects.
   124  	// See the comment in DialTCP.
   125  
   126  	// Find a port that would be used as a local address.
   127  	l, err := Listen("tcp", "127.0.0.1:0")
   128  	if err != nil {
   129  		t.Fatal(err)
   130  	}
   131  	c, err := Dial("tcp", l.Addr().String())
   132  	if err != nil {
   133  		t.Fatal(err)
   134  	}
   135  	addr := c.LocalAddr().String()
   136  	c.Close()
   137  	l.Close()
   138  
   139  	// Try to connect to that address repeatedly.
   140  	n := 100000
   141  	if testing.Short() {
   142  		n = 1000
   143  	}
   144  	switch runtime.GOOS {
   145  	case "darwin", "dragonfly", "freebsd", "netbsd", "openbsd", "plan9", "solaris", "windows":
   146  		// Non-Linux systems take a long time to figure
   147  		// out that there is nothing listening on localhost.
   148  		n = 100
   149  	}
   150  	for i := 0; i < n; i++ {
   151  		c, err := DialTimeout("tcp", addr, time.Millisecond)
   152  		if err == nil {
   153  			if c.LocalAddr().String() == addr {
   154  				t.Errorf("#%d: Dial %q self-connect", i, addr)
   155  			} else {
   156  				t.Logf("#%d: Dial %q succeeded - possibly racing with other listener", i, addr)
   157  			}
   158  			c.Close()
   159  		}
   160  	}
   161  }
   162  
   163  var runErrorTest = flag.Bool("run_error_test", false, "let TestDialError check for dns errors")
   164  
   165  type DialErrorTest struct {
   166  	Net     string
   167  	Raddr   string
   168  	Pattern string
   169  }
   170  
   171  var dialErrorTests = []DialErrorTest{
   172  	{
   173  		"datakit", "mh/astro/r70",
   174  		"dial datakit mh/astro/r70: unknown network datakit",
   175  	},
   176  	{
   177  		"tcp", "127.0.0.1:☺",
   178  		"dial tcp 127.0.0.1:☺: unknown port tcp/☺",
   179  	},
   180  	{
   181  		"tcp", "no-such-name.google.com.:80",
   182  		"dial tcp no-such-name.google.com.:80: lookup no-such-name.google.com.( on .*)?: no (.*)",
   183  	},
   184  	{
   185  		"tcp", "no-such-name.no-such-top-level-domain.:80",
   186  		"dial tcp no-such-name.no-such-top-level-domain.:80: lookup no-such-name.no-such-top-level-domain.( on .*)?: no (.*)",
   187  	},
   188  	{
   189  		"tcp", "no-such-name:80",
   190  		`dial tcp no-such-name:80: lookup no-such-name\.(.*\.)?( on .*)?: no (.*)`,
   191  	},
   192  	{
   193  		"tcp", "mh/astro/r70:http",
   194  		"dial tcp mh/astro/r70:http: lookup mh/astro/r70: invalid domain name",
   195  	},
   196  	{
   197  		"unix", "/etc/file-not-found",
   198  		"dial unix /etc/file-not-found: no such file or directory",
   199  	},
   200  	{
   201  		"unix", "/etc/",
   202  		"dial unix /etc/: (permission denied|socket operation on non-socket|connection refused)",
   203  	},
   204  	{
   205  		"unixpacket", "/etc/file-not-found",
   206  		"dial unixpacket /etc/file-not-found: no such file or directory",
   207  	},
   208  	{
   209  		"unixpacket", "/etc/",
   210  		"dial unixpacket /etc/: (permission denied|socket operation on non-socket|connection refused)",
   211  	},
   212  }
   213  
   214  var duplicateErrorPattern = `dial (.*) dial (.*)`
   215  
   216  func TestDialError(t *testing.T) {
   217  	if !*runErrorTest {
   218  		t.Logf("test disabled; use -run_error_test to enable")
   219  		return
   220  	}
   221  	for i, tt := range dialErrorTests {
   222  		c, err := Dial(tt.Net, tt.Raddr)
   223  		if c != nil {
   224  			c.Close()
   225  		}
   226  		if err == nil {
   227  			t.Errorf("#%d: nil error, want match for %#q", i, tt.Pattern)
   228  			continue
   229  		}
   230  		s := err.Error()
   231  		match, _ := regexp.MatchString(tt.Pattern, s)
   232  		if !match {
   233  			t.Errorf("#%d: %q, want match for %#q", i, s, tt.Pattern)
   234  		}
   235  		match, _ = regexp.MatchString(duplicateErrorPattern, s)
   236  		if match {
   237  			t.Errorf("#%d: %q, duplicate error return from Dial", i, s)
   238  		}
   239  	}
   240  }
   241  
   242  var invalidDialAndListenArgTests = []struct {
   243  	net  string
   244  	addr string
   245  	err  error
   246  }{
   247  	{"foo", "bar", &OpError{Op: "dial", Net: "foo", Addr: nil, Err: UnknownNetworkError("foo")}},
   248  	{"baz", "", &OpError{Op: "listen", Net: "baz", Addr: nil, Err: UnknownNetworkError("baz")}},
   249  	{"tcp", "", &OpError{Op: "dial", Net: "tcp", Addr: nil, Err: errMissingAddress}},
   250  }
   251  
   252  func TestInvalidDialAndListenArgs(t *testing.T) {
   253  	for _, tt := range invalidDialAndListenArgTests {
   254  		var err error
   255  		switch tt.err.(*OpError).Op {
   256  		case "dial":
   257  			_, err = Dial(tt.net, tt.addr)
   258  		case "listen":
   259  			_, err = Listen(tt.net, tt.addr)
   260  		}
   261  		if !reflect.DeepEqual(tt.err, err) {
   262  			t.Fatalf("got %#v; expected %#v", err, tt.err)
   263  		}
   264  	}
   265  }
   266  
   267  func TestDialTimeoutFDLeak(t *testing.T) {
   268  	if runtime.GOOS != "linux" {
   269  		// TODO(bradfitz): test on other platforms
   270  		t.Skipf("skipping test on %q", runtime.GOOS)
   271  	}
   272  
   273  	ln := newLocalListener(t)
   274  	defer ln.Close()
   275  
   276  	type connErr struct {
   277  		conn Conn
   278  		err  error
   279  	}
   280  	dials := listenerBacklog + 100
   281  	// used to be listenerBacklog + 5, but was found to be unreliable, issue 4384.
   282  	maxGoodConnect := listenerBacklog + runtime.NumCPU()*10
   283  	resc := make(chan connErr)
   284  	for i := 0; i < dials; i++ {
   285  		go func() {
   286  			conn, err := DialTimeout("tcp", ln.Addr().String(), 500*time.Millisecond)
   287  			resc <- connErr{conn, err}
   288  		}()
   289  	}
   290  
   291  	var firstErr string
   292  	var ngood int
   293  	var toClose []io.Closer
   294  	for i := 0; i < dials; i++ {
   295  		ce := <-resc
   296  		if ce.err == nil {
   297  			ngood++
   298  			if ngood > maxGoodConnect {
   299  				t.Errorf("%d good connects; expected at most %d", ngood, maxGoodConnect)
   300  			}
   301  			toClose = append(toClose, ce.conn)
   302  			continue
   303  		}
   304  		err := ce.err
   305  		if firstErr == "" {
   306  			firstErr = err.Error()
   307  		} else if err.Error() != firstErr {
   308  			t.Fatalf("inconsistent error messages: first was %q, then later %q", firstErr, err)
   309  		}
   310  	}
   311  	for _, c := range toClose {
   312  		c.Close()
   313  	}
   314  	for i := 0; i < 100; i++ {
   315  		if got := numFD(); got < dials {
   316  			// Test passes.
   317  			return
   318  		}
   319  		time.Sleep(10 * time.Millisecond)
   320  	}
   321  	if got := numFD(); got >= dials {
   322  		t.Errorf("num fds after %d timeouts = %d; want <%d", dials, got, dials)
   323  	}
   324  }
   325  
   326  func numTCP() (ntcp, nopen, nclose int, err error) {
   327  	lsof, err := exec.Command("lsof", "-n", "-p", strconv.Itoa(os.Getpid())).Output()
   328  	if err != nil {
   329  		return 0, 0, 0, err
   330  	}
   331  	ntcp += bytes.Count(lsof, []byte("TCP"))
   332  	for _, state := range []string{"LISTEN", "SYN_SENT", "SYN_RECEIVED", "ESTABLISHED"} {
   333  		nopen += bytes.Count(lsof, []byte(state))
   334  	}
   335  	for _, state := range []string{"CLOSED", "CLOSE_WAIT", "LAST_ACK", "FIN_WAIT_1", "FIN_WAIT_2", "CLOSING", "TIME_WAIT"} {
   336  		nclose += bytes.Count(lsof, []byte(state))
   337  	}
   338  	return ntcp, nopen, nclose, nil
   339  }
   340  
   341  func TestDialMultiFDLeak(t *testing.T) {
   342  	t.Skip("flaky test - golang.org/issue/8764")
   343  
   344  	if !supportsIPv4 || !supportsIPv6 {
   345  		t.Skip("neither ipv4 nor ipv6 is supported")
   346  	}
   347  
   348  	halfDeadServer := func(dss *dualStackServer, ln Listener) {
   349  		for {
   350  			if c, err := ln.Accept(); err != nil {
   351  				return
   352  			} else {
   353  				// It just keeps established
   354  				// connections like a half-dead server
   355  				// does.
   356  				dss.putConn(c)
   357  			}
   358  		}
   359  	}
   360  	dss, err := newDualStackServer([]streamListener{
   361  		{net: "tcp4", addr: "127.0.0.1"},
   362  		{net: "tcp6", addr: "[::1]"},
   363  	})
   364  	if err != nil {
   365  		t.Fatalf("newDualStackServer failed: %v", err)
   366  	}
   367  	defer dss.teardown()
   368  	if err := dss.buildup(halfDeadServer); err != nil {
   369  		t.Fatalf("dualStackServer.buildup failed: %v", err)
   370  	}
   371  
   372  	_, before, _, err := numTCP()
   373  	if err != nil {
   374  		t.Skipf("skipping test; error finding or running lsof: %v", err)
   375  	}
   376  
   377  	var wg sync.WaitGroup
   378  	portnum, _, _ := dtoi(dss.port, 0)
   379  	ras := addrList{
   380  		// Losers that will fail to connect, see RFC 6890.
   381  		&TCPAddr{IP: IPv4(198, 18, 0, 254), Port: portnum},
   382  		&TCPAddr{IP: ParseIP("2001:2::254"), Port: portnum},
   383  
   384  		// Winner candidates of this race.
   385  		&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: portnum},
   386  		&TCPAddr{IP: IPv6loopback, Port: portnum},
   387  
   388  		// Losers that will have established connections.
   389  		&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: portnum},
   390  		&TCPAddr{IP: IPv6loopback, Port: portnum},
   391  	}
   392  	const T1 = 10 * time.Millisecond
   393  	const T2 = 2 * T1
   394  	const N = 10
   395  	for i := 0; i < N; i++ {
   396  		wg.Add(1)
   397  		go func() {
   398  			defer wg.Done()
   399  			if c, err := dialMulti("tcp", "fast failover test", nil, ras, time.Now().Add(T1)); err == nil {
   400  				c.Close()
   401  			}
   402  		}()
   403  	}
   404  	wg.Wait()
   405  	time.Sleep(T2)
   406  
   407  	ntcp, after, nclose, err := numTCP()
   408  	if err != nil {
   409  		t.Skipf("skipping test; error finding or running lsof: %v", err)
   410  	}
   411  	t.Logf("tcp sessions: %v, open sessions: %v, closing sessions: %v", ntcp, after, nclose)
   412  
   413  	if after != before {
   414  		t.Fatalf("got %v open sessions; expected %v", after, before)
   415  	}
   416  }
   417  
   418  func numFD() int {
   419  	if runtime.GOOS == "linux" {
   420  		f, err := os.Open("/proc/self/fd")
   421  		if err != nil {
   422  			panic(err)
   423  		}
   424  		defer f.Close()
   425  		names, err := f.Readdirnames(0)
   426  		if err != nil {
   427  			panic(err)
   428  		}
   429  		return len(names)
   430  	}
   431  	// All tests using this should be skipped anyway, but:
   432  	panic("numFDs not implemented on " + runtime.GOOS)
   433  }
   434  
   435  func TestDialer(t *testing.T) {
   436  	ln, err := Listen("tcp4", "127.0.0.1:0")
   437  	if err != nil {
   438  		t.Fatalf("Listen failed: %v", err)
   439  	}
   440  	defer ln.Close()
   441  	ch := make(chan error, 1)
   442  	go func() {
   443  		c, err := ln.Accept()
   444  		if err != nil {
   445  			ch <- fmt.Errorf("Accept failed: %v", err)
   446  			return
   447  		}
   448  		defer c.Close()
   449  		ch <- nil
   450  	}()
   451  
   452  	laddr, err := ResolveTCPAddr("tcp4", "127.0.0.1:0")
   453  	if err != nil {
   454  		t.Fatalf("ResolveTCPAddr failed: %v", err)
   455  	}
   456  	d := &Dialer{LocalAddr: laddr}
   457  	c, err := d.Dial("tcp4", ln.Addr().String())
   458  	if err != nil {
   459  		t.Fatalf("Dial failed: %v", err)
   460  	}
   461  	defer c.Close()
   462  	c.Read(make([]byte, 1))
   463  	err = <-ch
   464  	if err != nil {
   465  		t.Error(err)
   466  	}
   467  }
   468  
   469  func TestDialDualStackLocalhost(t *testing.T) {
   470  	switch runtime.GOOS {
   471  	case "nacl":
   472  		t.Skipf("skipping test on %q", runtime.GOOS)
   473  	}
   474  
   475  	if ips, err := LookupIP("localhost"); err != nil {
   476  		t.Fatalf("LookupIP failed: %v", err)
   477  	} else if len(ips) < 2 || !supportsIPv4 || !supportsIPv6 {
   478  		t.Skip("localhost doesn't have a pair of different address family IP addresses")
   479  	}
   480  
   481  	touchAndByeServer := func(dss *dualStackServer, ln Listener) {
   482  		for {
   483  			if c, err := ln.Accept(); err != nil {
   484  				return
   485  			} else {
   486  				c.Close()
   487  			}
   488  		}
   489  	}
   490  	dss, err := newDualStackServer([]streamListener{
   491  		{net: "tcp4", addr: "127.0.0.1"},
   492  		{net: "tcp6", addr: "[::1]"},
   493  	})
   494  	if err != nil {
   495  		t.Fatalf("newDualStackServer failed: %v", err)
   496  	}
   497  	defer dss.teardown()
   498  	if err := dss.buildup(touchAndByeServer); err != nil {
   499  		t.Fatalf("dualStackServer.buildup failed: %v", err)
   500  	}
   501  
   502  	d := &Dialer{DualStack: true}
   503  	for range dss.lns {
   504  		if c, err := d.Dial("tcp", "localhost:"+dss.port); err != nil {
   505  			t.Errorf("Dial failed: %v", err)
   506  		} else {
   507  			if addr := c.LocalAddr().(*TCPAddr); addr.IP.To4() != nil {
   508  				dss.teardownNetwork("tcp4")
   509  			} else if addr.IP.To16() != nil && addr.IP.To4() == nil {
   510  				dss.teardownNetwork("tcp6")
   511  			}
   512  			c.Close()
   513  		}
   514  	}
   515  }
   516  
   517  func TestDialerKeepAlive(t *testing.T) {
   518  	ln := newLocalListener(t)
   519  	defer ln.Close()
   520  	defer func() {
   521  		testHookSetKeepAlive = func() {}
   522  	}()
   523  	go func() {
   524  		for {
   525  			c, err := ln.Accept()
   526  			if err != nil {
   527  				return
   528  			}
   529  			c.Close()
   530  		}
   531  	}()
   532  	for _, keepAlive := range []bool{false, true} {
   533  		got := false
   534  		testHookSetKeepAlive = func() { got = true }
   535  		var d Dialer
   536  		if keepAlive {
   537  			d.KeepAlive = 30 * time.Second
   538  		}
   539  		c, err := d.Dial("tcp", ln.Addr().String())
   540  		if err != nil {
   541  			t.Fatal(err)
   542  		}
   543  		c.Close()
   544  		if got != keepAlive {
   545  			t.Errorf("Dialer.KeepAlive = %v: SetKeepAlive called = %v, want %v", d.KeepAlive, got, !got)
   546  		}
   547  	}
   548  }