libgo: Merge to master revision 19184.
[official-gcc.git] / libgo / go / net / dial_test.go
blob08a0567ca762321096073f5878197b7eec3f9bf4
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.
5 package net
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"
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")
28 if err != nil {
29 t.Fatal(err)
31 return ln
34 func TestDialTimeout(t *testing.T) {
35 origBacklog := listenerBacklog
36 defer func() {
37 listenerBacklog = origBacklog
38 }()
39 listenerBacklog = 1
41 ln := newLocalListener(t)
42 defer ln.Close()
44 errc := make(chan error)
46 numConns := listenerBacklog + 100
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 }()
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.
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.
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()
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)
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")
102 } else {
103 terr, ok := err.(timeout)
104 if !ok {
105 t.Fatalf("got error %q; want error with timeout interface", err)
107 if !terr.Timeout() {
108 t.Fatalf("got error %q; not a timeout", err)
110 // Pass. We saw a timeout error.
111 return
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")
122 // Test that Dial does not honor self-connects.
123 // See the comment in DialTCP.
125 // Find a port that would be used as a local address.
126 l, err := Listen("tcp", "127.0.0.1:0")
127 if err != nil {
128 t.Fatal(err)
130 c, err := Dial("tcp", l.Addr().String())
131 if err != nil {
132 t.Fatal(err)
134 addr := c.LocalAddr().String()
135 c.Close()
136 l.Close()
138 // Try to connect to that address repeatedly.
139 n := 100000
140 if testing.Short() {
141 n = 1000
143 switch runtime.GOOS {
144 case "darwin", "dragonfly", "freebsd", "netbsd", "openbsd", "plan9", "solaris", "windows":
145 // Non-Linux systems take a long time to figure
146 // out that there is nothing listening on localhost.
147 n = 100
149 for i := 0; i < n; i++ {
150 c, err := DialTimeout("tcp", addr, time.Millisecond)
151 if err == nil {
152 c.Close()
153 t.Errorf("#%d: Dial %q succeeded", i, addr)
158 var runErrorTest = flag.Bool("run_error_test", false, "let TestDialError check for dns errors")
160 type DialErrorTest struct {
161 Net string
162 Raddr string
163 Pattern string
166 var dialErrorTests = []DialErrorTest{
168 "datakit", "mh/astro/r70",
169 "dial datakit mh/astro/r70: unknown network datakit",
172 "tcp", "127.0.0.1:☺",
173 "dial tcp 127.0.0.1:☺: unknown port tcp/☺",
176 "tcp", "no-such-name.google.com.:80",
177 "dial tcp no-such-name.google.com.:80: lookup no-such-name.google.com.( on .*)?: no (.*)",
180 "tcp", "no-such-name.no-such-top-level-domain.:80",
181 "dial tcp no-such-name.no-such-top-level-domain.:80: lookup no-such-name.no-such-top-level-domain.( on .*)?: no (.*)",
184 "tcp", "no-such-name:80",
185 `dial tcp no-such-name:80: lookup no-such-name\.(.*\.)?( on .*)?: no (.*)`,
188 "tcp", "mh/astro/r70:http",
189 "dial tcp mh/astro/r70:http: lookup mh/astro/r70: invalid domain name",
192 "unix", "/etc/file-not-found",
193 "dial unix /etc/file-not-found: no such file or directory",
196 "unix", "/etc/",
197 "dial unix /etc/: (permission denied|socket operation on non-socket|connection refused)",
200 "unixpacket", "/etc/file-not-found",
201 "dial unixpacket /etc/file-not-found: no such file or directory",
204 "unixpacket", "/etc/",
205 "dial unixpacket /etc/: (permission denied|socket operation on non-socket|connection refused)",
209 var duplicateErrorPattern = `dial (.*) dial (.*)`
211 func TestDialError(t *testing.T) {
212 if !*runErrorTest {
213 t.Logf("test disabled; use -run_error_test to enable")
214 return
216 for i, tt := range dialErrorTests {
217 c, err := Dial(tt.Net, tt.Raddr)
218 if c != nil {
219 c.Close()
221 if err == nil {
222 t.Errorf("#%d: nil error, want match for %#q", i, tt.Pattern)
223 continue
225 s := err.Error()
226 match, _ := regexp.MatchString(tt.Pattern, s)
227 if !match {
228 t.Errorf("#%d: %q, want match for %#q", i, s, tt.Pattern)
230 match, _ = regexp.MatchString(duplicateErrorPattern, s)
231 if match {
232 t.Errorf("#%d: %q, duplicate error return from Dial", i, s)
237 var invalidDialAndListenArgTests = []struct {
238 net string
239 addr string
240 err error
242 {"foo", "bar", &OpError{Op: "dial", Net: "foo", Addr: nil, Err: UnknownNetworkError("foo")}},
243 {"baz", "", &OpError{Op: "listen", Net: "baz", Addr: nil, Err: UnknownNetworkError("baz")}},
244 {"tcp", "", &OpError{Op: "dial", Net: "tcp", Addr: nil, Err: errMissingAddress}},
247 func TestInvalidDialAndListenArgs(t *testing.T) {
248 for _, tt := range invalidDialAndListenArgTests {
249 var err error
250 switch tt.err.(*OpError).Op {
251 case "dial":
252 _, err = Dial(tt.net, tt.addr)
253 case "listen":
254 _, err = Listen(tt.net, tt.addr)
256 if !reflect.DeepEqual(tt.err, err) {
257 t.Fatalf("got %#v; expected %#v", err, tt.err)
262 func TestDialTimeoutFDLeak(t *testing.T) {
263 if runtime.GOOS != "linux" {
264 // TODO(bradfitz): test on other platforms
265 t.Skipf("skipping test on %q", runtime.GOOS)
268 ln := newLocalListener(t)
269 defer ln.Close()
271 type connErr struct {
272 conn Conn
273 err error
275 dials := listenerBacklog + 100
276 // used to be listenerBacklog + 5, but was found to be unreliable, issue 4384.
277 maxGoodConnect := listenerBacklog + runtime.NumCPU()*10
278 resc := make(chan connErr)
279 for i := 0; i < dials; i++ {
280 go func() {
281 conn, err := DialTimeout("tcp", ln.Addr().String(), 500*time.Millisecond)
282 resc <- connErr{conn, err}
286 var firstErr string
287 var ngood int
288 var toClose []io.Closer
289 for i := 0; i < dials; i++ {
290 ce := <-resc
291 if ce.err == nil {
292 ngood++
293 if ngood > maxGoodConnect {
294 t.Errorf("%d good connects; expected at most %d", ngood, maxGoodConnect)
296 toClose = append(toClose, ce.conn)
297 continue
299 err := ce.err
300 if firstErr == "" {
301 firstErr = err.Error()
302 } else if err.Error() != firstErr {
303 t.Fatalf("inconsistent error messages: first was %q, then later %q", firstErr, err)
306 for _, c := range toClose {
307 c.Close()
309 for i := 0; i < 100; i++ {
310 if got := numFD(); got < dials {
311 // Test passes.
312 return
314 time.Sleep(10 * time.Millisecond)
316 if got := numFD(); got >= dials {
317 t.Errorf("num fds after %d timeouts = %d; want <%d", dials, got, dials)
321 func numTCP() (ntcp, nopen, nclose int, err error) {
322 lsof, err := exec.Command("lsof", "-n", "-p", strconv.Itoa(os.Getpid())).Output()
323 if err != nil {
324 return 0, 0, 0, err
326 ntcp += bytes.Count(lsof, []byte("TCP"))
327 for _, state := range []string{"LISTEN", "SYN_SENT", "SYN_RECEIVED", "ESTABLISHED"} {
328 nopen += bytes.Count(lsof, []byte(state))
330 for _, state := range []string{"CLOSED", "CLOSE_WAIT", "LAST_ACK", "FIN_WAIT_1", "FIN_WAIT_2", "CLOSING", "TIME_WAIT"} {
331 nclose += bytes.Count(lsof, []byte(state))
333 return ntcp, nopen, nclose, nil
336 func TestDialMultiFDLeak(t *testing.T) {
337 if !supportsIPv4 || !supportsIPv6 {
338 t.Skip("neither ipv4 nor ipv6 is supported")
341 halfDeadServer := func(dss *dualStackServer, ln Listener) {
342 for {
343 if c, err := ln.Accept(); err != nil {
344 return
345 } else {
346 // It just keeps established
347 // connections like a half-dead server
348 // does.
349 dss.putConn(c)
353 dss, err := newDualStackServer([]streamListener{
354 {net: "tcp4", addr: "127.0.0.1"},
355 {net: "tcp6", addr: "[::1]"},
357 if err != nil {
358 t.Fatalf("newDualStackServer failed: %v", err)
360 defer dss.teardown()
361 if err := dss.buildup(halfDeadServer); err != nil {
362 t.Fatalf("dualStackServer.buildup failed: %v", err)
365 _, before, _, err := numTCP()
366 if err != nil {
367 t.Skipf("skipping test; error finding or running lsof: %v", err)
370 var wg sync.WaitGroup
371 portnum, _, _ := dtoi(dss.port, 0)
372 ras := addrList{
373 // Losers that will fail to connect, see RFC 6890.
374 &TCPAddr{IP: IPv4(198, 18, 0, 254), Port: portnum},
375 &TCPAddr{IP: ParseIP("2001:2::254"), Port: portnum},
377 // Winner candidates of this race.
378 &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: portnum},
379 &TCPAddr{IP: IPv6loopback, Port: portnum},
381 // Losers that will have established connections.
382 &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: portnum},
383 &TCPAddr{IP: IPv6loopback, Port: portnum},
385 const T1 = 10 * time.Millisecond
386 const T2 = 2 * T1
387 const N = 10
388 for i := 0; i < N; i++ {
389 wg.Add(1)
390 go func() {
391 defer wg.Done()
392 if c, err := dialMulti("tcp", "fast failover test", nil, ras, time.Now().Add(T1)); err == nil {
393 c.Close()
397 wg.Wait()
398 time.Sleep(T2)
400 ntcp, after, nclose, err := numTCP()
401 if err != nil {
402 t.Skipf("skipping test; error finding or running lsof: %v", err)
404 t.Logf("tcp sessions: %v, open sessions: %v, closing sessions: %v", ntcp, after, nclose)
406 if after != before {
407 t.Fatalf("got %v open sessions; expected %v", after, before)
411 func numFD() int {
412 if runtime.GOOS == "linux" {
413 f, err := os.Open("/proc/self/fd")
414 if err != nil {
415 panic(err)
417 defer f.Close()
418 names, err := f.Readdirnames(0)
419 if err != nil {
420 panic(err)
422 return len(names)
424 // All tests using this should be skipped anyway, but:
425 panic("numFDs not implemented on " + runtime.GOOS)
428 // Assert that a failed Dial attempt does not leak
429 // runtime.PollDesc structures
430 func TestDialFailPDLeak(t *testing.T) {
431 if testing.Short() {
432 t.Skip("skipping test in short mode")
434 if runtime.GOOS == "windows" && runtime.GOARCH == "386" {
435 // Just skip the test because it takes too long.
436 t.Skipf("skipping test on %q/%q", runtime.GOOS, runtime.GOARCH)
439 maxprocs := runtime.GOMAXPROCS(0)
440 loops := 10 + maxprocs
441 // 500 is enough to turn over the chunk of pollcache.
442 // See allocPollDesc in runtime/netpoll.goc.
443 const count = 500
444 var old runtime.MemStats // used by sysdelta
445 runtime.ReadMemStats(&old)
446 sysdelta := func() uint64 {
447 var new runtime.MemStats
448 runtime.ReadMemStats(&new)
449 delta := old.Sys - new.Sys
450 old = new
451 return delta
453 d := &Dialer{Timeout: time.Nanosecond} // don't bother TCP with handshaking
454 failcount := 0
455 for i := 0; i < loops; i++ {
456 var wg sync.WaitGroup
457 for i := 0; i < count; i++ {
458 wg.Add(1)
459 go func() {
460 defer wg.Done()
461 if c, err := d.Dial("tcp", "127.0.0.1:1"); err == nil {
462 t.Error("dial should not succeed")
463 c.Close()
467 wg.Wait()
468 if t.Failed() {
469 t.FailNow()
471 if delta := sysdelta(); delta > 0 {
472 failcount++
474 // there are always some allocations on the first loop
475 if failcount > maxprocs+2 {
476 t.Error("detected possible memory leak in runtime")
477 t.FailNow()
482 func TestDialer(t *testing.T) {
483 ln, err := Listen("tcp4", "127.0.0.1:0")
484 if err != nil {
485 t.Fatalf("Listen failed: %v", err)
487 defer ln.Close()
488 ch := make(chan error, 1)
489 go func() {
490 c, err := ln.Accept()
491 if err != nil {
492 ch <- fmt.Errorf("Accept failed: %v", err)
493 return
495 defer c.Close()
496 ch <- nil
499 laddr, err := ResolveTCPAddr("tcp4", "127.0.0.1:0")
500 if err != nil {
501 t.Fatalf("ResolveTCPAddr failed: %v", err)
503 d := &Dialer{LocalAddr: laddr}
504 c, err := d.Dial("tcp4", ln.Addr().String())
505 if err != nil {
506 t.Fatalf("Dial failed: %v", err)
508 defer c.Close()
509 c.Read(make([]byte, 1))
510 err = <-ch
511 if err != nil {
512 t.Error(err)
516 func TestDialDualStackLocalhost(t *testing.T) {
517 if ips, err := LookupIP("localhost"); err != nil {
518 t.Fatalf("LookupIP failed: %v", err)
519 } else if len(ips) < 2 || !supportsIPv4 || !supportsIPv6 {
520 t.Skip("localhost doesn't have a pair of different address family IP addresses")
523 touchAndByeServer := func(dss *dualStackServer, ln Listener) {
524 for {
525 if c, err := ln.Accept(); err != nil {
526 return
527 } else {
528 c.Close()
532 dss, err := newDualStackServer([]streamListener{
533 {net: "tcp4", addr: "127.0.0.1"},
534 {net: "tcp6", addr: "[::1]"},
536 if err != nil {
537 t.Fatalf("newDualStackServer failed: %v", err)
539 defer dss.teardown()
540 if err := dss.buildup(touchAndByeServer); err != nil {
541 t.Fatalf("dualStackServer.buildup failed: %v", err)
544 d := &Dialer{DualStack: true}
545 for _ = range dss.lns {
546 if c, err := d.Dial("tcp", "localhost:"+dss.port); err != nil {
547 t.Errorf("Dial failed: %v", err)
548 } else {
549 if addr := c.LocalAddr().(*TCPAddr); addr.IP.To4() != nil {
550 dss.teardownNetwork("tcp4")
551 } else if addr.IP.To16() != nil && addr.IP.To4() == nil {
552 dss.teardownNetwork("tcp6")
554 c.Close()