]>
Commit | Line | Data |
---|---|---|
df4aa89a ILT |
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 ( | |
593f74bb | 8 | "flag" |
456fba26 | 9 | "fmt" |
593f74bb | 10 | "regexp" |
df4aa89a ILT |
11 | "runtime" |
12 | "testing" | |
13 | "time" | |
14 | ) | |
15 | ||
16 | func newLocalListener(t *testing.T) Listener { | |
17 | ln, err := Listen("tcp", "127.0.0.1:0") | |
18 | if err != nil { | |
19 | ln, err = Listen("tcp6", "[::1]:0") | |
20 | } | |
21 | if err != nil { | |
22 | t.Fatal(err) | |
23 | } | |
24 | return ln | |
25 | } | |
26 | ||
27 | func TestDialTimeout(t *testing.T) { | |
28 | ln := newLocalListener(t) | |
29 | defer ln.Close() | |
30 | ||
31 | errc := make(chan error) | |
32 | ||
cbb6491d | 33 | numConns := listenerBacklog + 10 |
df4aa89a ILT |
34 | |
35 | // TODO(bradfitz): It's hard to test this in a portable | |
456fba26 | 36 | // way. This is unfortunate, but works for now. |
df4aa89a ILT |
37 | switch runtime.GOOS { |
38 | case "linux": | |
39 | // The kernel will start accepting TCP connections before userspace | |
40 | // gets a chance to not accept them, so fire off a bunch to fill up | |
41 | // the kernel's backlog. Then we test we get a failure after that. | |
42 | for i := 0; i < numConns; i++ { | |
43 | go func() { | |
44 | _, err := DialTimeout("tcp", ln.Addr().String(), 200*time.Millisecond) | |
45 | errc <- err | |
46 | }() | |
47 | } | |
456fba26 | 48 | case "darwin", "windows": |
df4aa89a ILT |
49 | // At least OS X 10.7 seems to accept any number of |
50 | // connections, ignoring listen's backlog, so resort | |
51 | // to connecting to a hopefully-dead 127/8 address. | |
94252f4b | 52 | // Same for windows. |
456fba26 ILT |
53 | // |
54 | // Use an IANA reserved port (49151) instead of 80, because | |
55 | // on our 386 builder, this Dial succeeds, connecting | |
56 | // to an IIS web server somewhere. The data center | |
57 | // or VM or firewall must be stealing the TCP connection. | |
58 | // | |
59 | // IANA Service Name and Transport Protocol Port Number Registry | |
60 | // <http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml> | |
df4aa89a | 61 | go func() { |
456fba26 ILT |
62 | c, err := DialTimeout("tcp", "127.0.71.111:49151", 200*time.Millisecond) |
63 | if err == nil { | |
64 | err = fmt.Errorf("unexpected: connected to %s!", c.RemoteAddr()) | |
65 | c.Close() | |
66 | } | |
df4aa89a ILT |
67 | errc <- err |
68 | }() | |
69 | default: | |
94252f4b | 70 | // TODO(bradfitz): |
cbb6491d ILT |
71 | // OpenBSD may have a reject route to 127/8 except 127.0.0.1/32 |
72 | // by default. FreeBSD likely works, but is untested. | |
73 | // TODO(rsc): | |
74 | // The timeout never happens on Windows. Why? Issue 3016. | |
df4aa89a ILT |
75 | t.Logf("skipping test on %q; untested.", runtime.GOOS) |
76 | return | |
77 | } | |
78 | ||
79 | connected := 0 | |
80 | for { | |
81 | select { | |
82 | case <-time.After(15 * time.Second): | |
83 | t.Fatal("too slow") | |
84 | case err := <-errc: | |
85 | if err == nil { | |
86 | connected++ | |
87 | if connected == numConns { | |
88 | t.Fatal("all connections connected; expected some to time out") | |
89 | } | |
90 | } else { | |
91 | terr, ok := err.(timeout) | |
92 | if !ok { | |
93 | t.Fatalf("got error %q; want error with timeout interface", err) | |
94 | } | |
95 | if !terr.Timeout() { | |
96 | t.Fatalf("got error %q; not a timeout", err) | |
97 | } | |
98 | // Pass. We saw a timeout error. | |
99 | return | |
100 | } | |
101 | } | |
102 | } | |
103 | } | |
cbb6491d ILT |
104 | |
105 | func TestSelfConnect(t *testing.T) { | |
106 | if runtime.GOOS == "windows" { | |
107 | // TODO(brainman): do not know why it hangs. | |
108 | t.Logf("skipping known-broken test on windows") | |
109 | return | |
110 | } | |
111 | // Test that Dial does not honor self-connects. | |
112 | // See the comment in DialTCP. | |
113 | ||
114 | // Find a port that would be used as a local address. | |
115 | l, err := Listen("tcp", "127.0.0.1:0") | |
116 | if err != nil { | |
117 | t.Fatal(err) | |
118 | } | |
119 | c, err := Dial("tcp", l.Addr().String()) | |
120 | if err != nil { | |
121 | t.Fatal(err) | |
122 | } | |
123 | addr := c.LocalAddr().String() | |
124 | c.Close() | |
125 | l.Close() | |
126 | ||
127 | // Try to connect to that address repeatedly. | |
128 | n := 100000 | |
129 | if testing.Short() { | |
130 | n = 1000 | |
131 | } | |
132 | switch runtime.GOOS { | |
4ccad563 | 133 | case "darwin", "freebsd", "netbsd", "openbsd", "plan9", "solaris", "windows": |
cbb6491d ILT |
134 | // Non-Linux systems take a long time to figure |
135 | // out that there is nothing listening on localhost. | |
136 | n = 100 | |
137 | } | |
138 | for i := 0; i < n; i++ { | |
139 | c, err := Dial("tcp", addr) | |
140 | if err == nil { | |
141 | c.Close() | |
142 | t.Errorf("#%d: Dial %q succeeded", i, addr) | |
143 | } | |
144 | } | |
145 | } | |
593f74bb ILT |
146 | |
147 | var runErrorTest = flag.Bool("run_error_test", false, "let TestDialError check for dns errors") | |
148 | ||
149 | type DialErrorTest struct { | |
150 | Net string | |
151 | Raddr string | |
152 | Pattern string | |
153 | } | |
154 | ||
155 | var dialErrorTests = []DialErrorTest{ | |
156 | { | |
157 | "datakit", "mh/astro/r70", | |
158 | "dial datakit mh/astro/r70: unknown network datakit", | |
159 | }, | |
160 | { | |
161 | "tcp", "127.0.0.1:☺", | |
162 | "dial tcp 127.0.0.1:☺: unknown port tcp/☺", | |
163 | }, | |
164 | { | |
165 | "tcp", "no-such-name.google.com.:80", | |
166 | "dial tcp no-such-name.google.com.:80: lookup no-such-name.google.com.( on .*)?: no (.*)", | |
167 | }, | |
168 | { | |
169 | "tcp", "no-such-name.no-such-top-level-domain.:80", | |
170 | "dial tcp no-such-name.no-such-top-level-domain.:80: lookup no-such-name.no-such-top-level-domain.( on .*)?: no (.*)", | |
171 | }, | |
172 | { | |
173 | "tcp", "no-such-name:80", | |
174 | `dial tcp no-such-name:80: lookup no-such-name\.(.*\.)?( on .*)?: no (.*)`, | |
175 | }, | |
176 | { | |
177 | "tcp", "mh/astro/r70:http", | |
178 | "dial tcp mh/astro/r70:http: lookup mh/astro/r70: invalid domain name", | |
179 | }, | |
180 | { | |
181 | "unix", "/etc/file-not-found", | |
182 | "dial unix /etc/file-not-found: no such file or directory", | |
183 | }, | |
184 | { | |
185 | "unix", "/etc/", | |
186 | "dial unix /etc/: (permission denied|socket operation on non-socket|connection refused)", | |
187 | }, | |
188 | { | |
189 | "unixpacket", "/etc/file-not-found", | |
190 | "dial unixpacket /etc/file-not-found: no such file or directory", | |
191 | }, | |
192 | { | |
193 | "unixpacket", "/etc/", | |
194 | "dial unixpacket /etc/: (permission denied|socket operation on non-socket|connection refused)", | |
195 | }, | |
196 | } | |
197 | ||
198 | var duplicateErrorPattern = `dial (.*) dial (.*)` | |
199 | ||
200 | func TestDialError(t *testing.T) { | |
201 | if !*runErrorTest { | |
202 | t.Logf("test disabled; use -run_error_test to enable") | |
203 | return | |
204 | } | |
205 | for i, tt := range dialErrorTests { | |
206 | c, err := Dial(tt.Net, tt.Raddr) | |
207 | if c != nil { | |
208 | c.Close() | |
209 | } | |
210 | if err == nil { | |
211 | t.Errorf("#%d: nil error, want match for %#q", i, tt.Pattern) | |
212 | continue | |
213 | } | |
214 | s := err.Error() | |
215 | match, _ := regexp.MatchString(tt.Pattern, s) | |
216 | if !match { | |
217 | t.Errorf("#%d: %q, want match for %#q", i, s, tt.Pattern) | |
218 | } | |
219 | match, _ = regexp.MatchString(duplicateErrorPattern, s) | |
220 | if match { | |
221 | t.Errorf("#%d: %q, duplicate error return from Dial", i, s) | |
222 | } | |
223 | } | |
224 | } |