blob: 9f4a90d8e1812f8a5bbd663418362d4d9ed54c93 [file] [log] [blame]
Mikio Hara89b7c662015-04-13 23:45:00 +09001// Copyright 2015 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
5package net
6
7import (
8 "fmt"
Mikio Haraec114442015-04-16 23:10:56 +09009 "io"
Mikio Hara89b7c662015-04-13 23:45:00 +090010 "net/internal/socktest"
11 "os"
12 "runtime"
13 "testing"
14)
15
16func isTimeoutError(err error) bool {
17 nerr, ok := err.(Error)
18 return ok && nerr.Timeout()
19}
20
21func isTemporaryError(err error) bool {
22 nerr, ok := err.(Error)
23 return ok && nerr.Temporary()
24}
25
26func (e *OpError) isValid() error {
27 if e.Op == "" {
28 return fmt.Errorf("OpError.Op is empty: %v", e)
29 }
30 if e.Net == "" {
31 return fmt.Errorf("OpError.Net is empty: %v", e)
32 }
33 switch addr := e.Addr.(type) {
34 case *TCPAddr:
35 if addr == nil {
36 return fmt.Errorf("OpError.Addr is empty: %v", e)
37 }
38 case *UDPAddr:
39 if addr == nil {
40 return fmt.Errorf("OpError.Addr is empty: %v", e)
41 }
42 case *IPAddr:
43 if addr == nil {
44 return fmt.Errorf("OpError.Addr is empty: %v", e)
45 }
46 case *IPNet:
47 if addr == nil {
48 return fmt.Errorf("OpError.Addr is empty: %v", e)
49 }
50 case *UnixAddr:
51 if addr == nil {
52 return fmt.Errorf("OpError.Addr is empty: %v", e)
53 }
54 case *pipeAddr:
55 if addr == nil {
56 return fmt.Errorf("OpError.Addr is empty: %v", e)
57 }
58 }
59 if e.Err == nil {
60 return fmt.Errorf("OpError.Err is empty: %v", e)
61 }
62 return nil
63}
64
65// parseDialError parses nestedErr and reports whether it is a valid
66// error value from Dial, Listen functions.
67// It returns nil when nestedErr is valid.
68func parseDialError(nestedErr error) error {
69 if nestedErr == nil {
70 return nil
71 }
72
73 switch err := nestedErr.(type) {
74 case *OpError:
75 if err := err.isValid(); err != nil {
76 return err
77 }
78 nestedErr = err.Err
79 goto second
80 }
81 return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
82
83second:
84 if isPlatformError(nestedErr) {
85 return nil
86 }
87 switch err := nestedErr.(type) {
Mikio Hara6d3a7e72015-04-17 17:56:58 +090088 case *AddrError, addrinfoErrno, *DNSError, InvalidAddrError, *ParseError, *timeoutError, UnknownNetworkError:
Mikio Hara89b7c662015-04-13 23:45:00 +090089 return nil
90 case *DNSConfigError:
91 nestedErr = err.Err
92 goto third
93 case *os.SyscallError:
94 nestedErr = err.Err
95 goto third
96 }
97 switch nestedErr {
98 case errClosing, errMissingAddress:
99 return nil
100 }
101 return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
102
103third:
104 if isPlatformError(nestedErr) {
105 return nil
106 }
107 return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
108}
109
110var dialErrorTests = []struct {
111 network, address string
112}{
113 {"foo", ""},
114 {"bar", "baz"},
115 {"datakit", "mh/astro/r70"},
116 {"tcp", ""},
117 {"tcp", "127.0.0.1:☺"},
118 {"tcp", "no-such-name:80"},
119 {"tcp", "mh/astro/r70:http"},
120
121 {"tcp", "127.0.0.1:0"},
122 {"udp", "127.0.0.1:0"},
123 {"ip:icmp", "127.0.0.1"},
124
125 {"unix", "/path/to/somewhere"},
126 {"unixgram", "/path/to/somewhere"},
127 {"unixpacket", "/path/to/somewhere"},
128}
129
130func TestDialError(t *testing.T) {
131 switch runtime.GOOS {
132 case "plan9":
133 t.Skipf("%s does not have full support of socktest", runtime.GOOS)
134 }
135
136 origTestHookLookupIP := testHookLookupIP
137 defer func() { testHookLookupIP = origTestHookLookupIP }()
138 testHookLookupIP = func(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, error) {
139 return nil, &DNSError{Err: "dial error test", Name: "name", Server: "server", IsTimeout: true}
140 }
141 sw.Set(socktest.FilterConnect, func(so *socktest.Status) (socktest.AfterFilter, error) {
142 return nil, errOpNotSupported
143 })
144 defer sw.Set(socktest.FilterConnect, nil)
145
146 d := Dialer{Timeout: someTimeout}
147 for i, tt := range dialErrorTests {
148 c, err := d.Dial(tt.network, tt.address)
149 if err == nil {
150 t.Errorf("#%d: should fail; %s:%s->%s", i, tt.network, c.LocalAddr(), c.RemoteAddr())
151 c.Close()
152 continue
153 }
Mikio Harad0f31002015-04-17 14:55:07 +0900154 if c != nil {
155 t.Errorf("Dial returned non-nil interface %T(%v) with err != nil", c, c)
156 }
Mikio Hara89b7c662015-04-13 23:45:00 +0900157 if err = parseDialError(err); err != nil {
158 t.Errorf("#%d: %v", i, err)
159 continue
160 }
161 }
162}
163
164var listenErrorTests = []struct {
165 network, address string
166}{
167 {"foo", ""},
168 {"bar", "baz"},
169 {"datakit", "mh/astro/r70"},
170 {"tcp", "127.0.0.1:☺"},
171 {"tcp", "no-such-name:80"},
172 {"tcp", "mh/astro/r70:http"},
Mikio Harad0f31002015-04-17 14:55:07 +0900173
174 {"tcp", "127.0.0.1:0"},
175
176 {"unix", "/path/to/somewhere"},
177 {"unixpacket", "/path/to/somewhere"},
Mikio Hara89b7c662015-04-13 23:45:00 +0900178}
179
180func TestListenError(t *testing.T) {
181 switch runtime.GOOS {
182 case "plan9":
183 t.Skipf("%s does not have full support of socktest", runtime.GOOS)
184 }
185
186 origTestHookLookupIP := testHookLookupIP
187 defer func() { testHookLookupIP = origTestHookLookupIP }()
188 testHookLookupIP = func(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, error) {
189 return nil, &DNSError{Err: "listen error test", Name: "name", Server: "server", IsTimeout: true}
190 }
191 sw.Set(socktest.FilterListen, func(so *socktest.Status) (socktest.AfterFilter, error) {
192 return nil, errOpNotSupported
193 })
194 defer sw.Set(socktest.FilterListen, nil)
195
196 for i, tt := range listenErrorTests {
197 ln, err := Listen(tt.network, tt.address)
198 if err == nil {
199 t.Errorf("#%d: should fail; %s:%s->", i, tt.network, ln.Addr())
200 ln.Close()
201 continue
202 }
Mikio Harad0f31002015-04-17 14:55:07 +0900203 if ln != nil {
204 t.Errorf("Listen returned non-nil interface %T(%v) with err != nil", ln, ln)
205 }
206 if err = parseDialError(err); err != nil {
207 t.Errorf("#%d: %v", i, err)
208 continue
209 }
210 }
211}
212
213var listenPacketErrorTests = []struct {
214 network, address string
215}{
216 {"foo", ""},
217 {"bar", "baz"},
218 {"datakit", "mh/astro/r70"},
219 {"udp", "127.0.0.1:☺"},
220 {"udp", "no-such-name:80"},
221 {"udp", "mh/astro/r70:http"},
222}
223
224func TestListenPacketError(t *testing.T) {
225 switch runtime.GOOS {
226 case "plan9":
227 t.Skipf("%s does not have full support of socktest", runtime.GOOS)
228 }
229
230 origTestHookLookupIP := testHookLookupIP
231 defer func() { testHookLookupIP = origTestHookLookupIP }()
232 testHookLookupIP = func(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, error) {
233 return nil, &DNSError{Err: "listen error test", Name: "name", Server: "server", IsTimeout: true}
234 }
235
236 for i, tt := range listenPacketErrorTests {
237 c, err := ListenPacket(tt.network, tt.address)
238 if err == nil {
239 t.Errorf("#%d: should fail; %s:%s->", i, tt.network, c.LocalAddr())
240 c.Close()
241 continue
242 }
243 if c != nil {
244 t.Errorf("ListenPacket returned non-nil interface %T(%v) with err != nil", c, c)
245 }
Mikio Hara89b7c662015-04-13 23:45:00 +0900246 if err = parseDialError(err); err != nil {
247 t.Errorf("#%d: %v", i, err)
248 continue
249 }
250 }
251}
Mikio Haraec114442015-04-16 23:10:56 +0900252
253// parseReadError parses nestedErr and reports whether it is a valid
254// error value from Read functions.
255// It returns nil when nestedErr is valid.
256func parseReadError(nestedErr error) error {
257 if nestedErr == nil {
258 return nil
259 }
260
261 switch err := nestedErr.(type) {
262 case *OpError:
263 if err := err.isValid(); err != nil {
264 return err
265 }
266 nestedErr = err.Err
267 goto second
268 }
269 if nestedErr == io.EOF {
270 return nil
271 }
272 return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
273
274second:
275 if isPlatformError(nestedErr) {
276 return nil
277 }
278 switch err := nestedErr.(type) {
279 case *os.SyscallError:
280 nestedErr = err.Err
281 goto third
282 }
283 switch nestedErr {
284 case errClosing, errTimeout:
285 return nil
286 }
287 return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
288
289third:
290 if isPlatformError(nestedErr) {
291 return nil
292 }
293 return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
294}
Mikio Hara11b5f982015-04-16 11:26:44 +0900295
296// parseWriteError parses nestedErr and reports whether it is a valid
297// error value from Write functions.
298// It returns nil when nestedErr is valid.
299func parseWriteError(nestedErr error) error {
300 if nestedErr == nil {
301 return nil
302 }
303
304 switch err := nestedErr.(type) {
305 case *OpError:
306 if err := err.isValid(); err != nil {
307 return err
308 }
309 nestedErr = err.Err
310 goto second
311 }
312 return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
313
314second:
315 if isPlatformError(nestedErr) {
316 return nil
317 }
318 switch err := nestedErr.(type) {
319 case *os.SyscallError:
320 nestedErr = err.Err
321 goto third
322 }
323 switch nestedErr {
324 case errClosing, errTimeout, ErrWriteToConnected, io.ErrUnexpectedEOF:
325 return nil
326 }
327 return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
328
329third:
330 if isPlatformError(nestedErr) {
331 return nil
332 }
333 return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
334}
Mikio Hara310db632015-04-17 12:24:42 +0900335
336// parseCloseError parses nestedErr and reports whether it is a valid
337// error value from Close functions.
338// It returns nil when nestedErr is valid.
339func parseCloseError(nestedErr error) error {
340 if nestedErr == nil {
341 return nil
342 }
343
344 switch err := nestedErr.(type) {
345 case *OpError:
346 if err := err.isValid(); err != nil {
347 return err
348 }
349 nestedErr = err.Err
350 goto second
351 }
352 return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
353
354second:
355 if isPlatformError(nestedErr) {
356 return nil
357 }
358 switch err := nestedErr.(type) {
359 case *os.SyscallError:
360 nestedErr = err.Err
361 goto third
362 case *os.PathError: // for Plan 9
363 nestedErr = err.Err
364 goto third
365 }
366 switch nestedErr {
367 case errClosing:
368 return nil
369 }
370 return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
371
372third:
373 if isPlatformError(nestedErr) {
374 return nil
375 }
376 return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
377}
378
379func TestCloseError(t *testing.T) {
380 ln, err := newLocalListener("tcp")
381 if err != nil {
382 t.Fatal(err)
383 }
384 defer ln.Close()
385 c, err := Dial(ln.Addr().Network(), ln.Addr().String())
386 if err != nil {
387 t.Fatal(err)
388 }
389 defer c.Close()
390
391 for i := 0; i < 3; i++ {
392 err = c.(*TCPConn).CloseRead()
393 if perr := parseCloseError(err); perr != nil {
394 t.Errorf("#%d: %v", i, perr)
395 }
396 }
397 for i := 0; i < 3; i++ {
398 err = c.(*TCPConn).CloseWrite()
399 if perr := parseCloseError(err); perr != nil {
400 t.Errorf("#%d: %v", i, perr)
401 }
402 }
403 for i := 0; i < 3; i++ {
404 err = c.Close()
405 if perr := parseCloseError(err); perr != nil {
406 t.Errorf("#%d: %v", i, perr)
407 }
408 err = ln.Close()
409 if perr := parseCloseError(err); perr != nil {
410 t.Errorf("#%d: %v", i, perr)
411 }
412 }
413
414 pc, err := ListenPacket("udp", "127.0.0.1:0")
415 if err != nil {
416 t.Fatal(err)
417 }
418 defer pc.Close()
419
420 for i := 0; i < 3; i++ {
421 err = pc.Close()
422 if perr := parseCloseError(err); perr != nil {
423 t.Errorf("#%d: %v", i, perr)
424 }
425 }
426}