| // 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 ( |
| "runtime" |
| "testing" |
| "time" |
| ) |
| |
| func newLocalListener(t *testing.T) Listener { |
| ln, err := Listen("tcp", "127.0.0.1:0") |
| if err != nil { |
| ln, err = Listen("tcp6", "[::1]:0") |
| } |
| if err != nil { |
| t.Fatal(err) |
| } |
| return ln |
| } |
| |
| func TestDialTimeout(t *testing.T) { |
| ln := newLocalListener(t) |
| defer ln.Close() |
| |
| errc := make(chan error) |
| |
| const SOMAXCONN = 0x80 // copied from syscall, but not always available |
| const numConns = SOMAXCONN + 10 |
| |
| // TODO(bradfitz): It's hard to test this in a portable |
| // way. This is unforunate, but works for now. |
| switch runtime.GOOS { |
| case "linux": |
| // The kernel will start accepting TCP connections before userspace |
| // gets a chance to not accept them, so fire off a bunch to fill up |
| // the kernel's backlog. Then we test we get a failure after that. |
| for i := 0; i < numConns; i++ { |
| go func() { |
| _, err := DialTimeout("tcp", ln.Addr().String(), 200*time.Millisecond) |
| errc <- err |
| }() |
| } |
| case "darwin": |
| // At least OS X 10.7 seems to accept any number of |
| // connections, ignoring listen's backlog, so resort |
| // to connecting to a hopefully-dead 127/8 address. |
| go func() { |
| _, err := DialTimeout("tcp", "127.0.71.111:80", 200*time.Millisecond) |
| errc <- err |
| }() |
| default: |
| // TODO(bradfitz): this probably doesn't work on |
| // Windows? SOMAXCONN is huge there. I'm not sure how |
| // listen works there. |
| // OpenBSD may have a reject route to 10/8. |
| // FreeBSD likely works, but is untested. |
| t.Logf("skipping test on %q; untested.", runtime.GOOS) |
| return |
| } |
| |
| connected := 0 |
| for { |
| select { |
| case <-time.After(15 * time.Second): |
| t.Fatal("too slow") |
| case err := <-errc: |
| if err == nil { |
| connected++ |
| if connected == numConns { |
| t.Fatal("all connections connected; expected some to time out") |
| } |
| } else { |
| terr, ok := err.(timeout) |
| if !ok { |
| t.Fatalf("got error %q; want error with timeout interface", err) |
| } |
| if !terr.Timeout() { |
| t.Fatalf("got error %q; not a timeout", err) |
| } |
| // Pass. We saw a timeout error. |
| return |
| } |
| } |
| } |
| } |