Source file src/net/error_test.go

     1  // 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  
     5  package net
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"fmt"
    11  	"internal/poll"
    12  	"io"
    13  	"io/fs"
    14  	"net/internal/socktest"
    15  	"os"
    16  	"runtime"
    17  	"strings"
    18  	"testing"
    19  	"time"
    20  )
    21  
    22  func (e *OpError) isValid() error {
    23  	if e.Op == "" {
    24  		return fmt.Errorf("OpError.Op is empty: %v", e)
    25  	}
    26  	if e.Net == "" {
    27  		return fmt.Errorf("OpError.Net is empty: %v", e)
    28  	}
    29  	for _, addr := range []Addr{e.Source, e.Addr} {
    30  		switch addr := addr.(type) {
    31  		case nil:
    32  		case *TCPAddr:
    33  			if addr == nil {
    34  				return fmt.Errorf("OpError.Source or Addr is non-nil interface: %#v, %v", addr, e)
    35  			}
    36  		case *UDPAddr:
    37  			if addr == nil {
    38  				return fmt.Errorf("OpError.Source or Addr is non-nil interface: %#v, %v", addr, e)
    39  			}
    40  		case *IPAddr:
    41  			if addr == nil {
    42  				return fmt.Errorf("OpError.Source or Addr is non-nil interface: %#v, %v", addr, e)
    43  			}
    44  		case *IPNet:
    45  			if addr == nil {
    46  				return fmt.Errorf("OpError.Source or Addr is non-nil interface: %#v, %v", addr, e)
    47  			}
    48  		case *UnixAddr:
    49  			if addr == nil {
    50  				return fmt.Errorf("OpError.Source or Addr is non-nil interface: %#v, %v", addr, e)
    51  			}
    52  		case *pipeAddr:
    53  			if addr == nil {
    54  				return fmt.Errorf("OpError.Source or Addr is non-nil interface: %#v, %v", addr, e)
    55  			}
    56  		case fileAddr:
    57  			if addr == "" {
    58  				return fmt.Errorf("OpError.Source or Addr is empty: %#v, %v", addr, e)
    59  			}
    60  		default:
    61  			return fmt.Errorf("OpError.Source or Addr is unknown type: %T, %v", addr, e)
    62  		}
    63  	}
    64  	if e.Err == nil {
    65  		return fmt.Errorf("OpError.Err is empty: %v", e)
    66  	}
    67  	return nil
    68  }
    69  
    70  // parseDialError parses nestedErr and reports whether it is a valid
    71  // error value from Dial, Listen functions.
    72  // It returns nil when nestedErr is valid.
    73  func parseDialError(nestedErr error) error {
    74  	if nestedErr == nil {
    75  		return nil
    76  	}
    77  
    78  	switch err := nestedErr.(type) {
    79  	case *OpError:
    80  		if err := err.isValid(); err != nil {
    81  			return err
    82  		}
    83  		nestedErr = err.Err
    84  		goto second
    85  	}
    86  	return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
    87  
    88  second:
    89  	if isPlatformError(nestedErr) {
    90  		return nil
    91  	}
    92  	switch err := nestedErr.(type) {
    93  	case *AddrError, *timeoutError, *DNSError, InvalidAddrError, *ParseError, *poll.DeadlineExceededError, UnknownNetworkError:
    94  		return nil
    95  	case interface{ isAddrinfoErrno() }:
    96  		return nil
    97  	case *os.SyscallError:
    98  		nestedErr = err.Err
    99  		goto third
   100  	case *fs.PathError: // for Plan 9
   101  		nestedErr = err.Err
   102  		goto third
   103  	}
   104  	switch nestedErr {
   105  	case errCanceled, ErrClosed, errMissingAddress, errNoSuitableAddress,
   106  		context.DeadlineExceeded, context.Canceled:
   107  		return nil
   108  	}
   109  	return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
   110  
   111  third:
   112  	if isPlatformError(nestedErr) {
   113  		return nil
   114  	}
   115  	return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
   116  }
   117  
   118  var dialErrorTests = []struct {
   119  	network, address string
   120  }{
   121  	{"foo", ""},
   122  	{"bar", "baz"},
   123  	{"datakit", "mh/astro/r70"},
   124  	{"tcp", ""},
   125  	{"tcp", "127.0.0.1:☺"},
   126  	{"tcp", "no-such-name:80"},
   127  	{"tcp", "mh/astro/r70:http"},
   128  
   129  	{"tcp", JoinHostPort("127.0.0.1", "-1")},
   130  	{"tcp", JoinHostPort("127.0.0.1", "123456789")},
   131  	{"udp", JoinHostPort("127.0.0.1", "-1")},
   132  	{"udp", JoinHostPort("127.0.0.1", "123456789")},
   133  	{"ip:icmp", "127.0.0.1"},
   134  
   135  	{"unix", "/path/to/somewhere"},
   136  	{"unixgram", "/path/to/somewhere"},
   137  	{"unixpacket", "/path/to/somewhere"},
   138  }
   139  
   140  func TestDialError(t *testing.T) {
   141  	switch runtime.GOOS {
   142  	case "plan9":
   143  		t.Skipf("%s does not have full support of socktest", runtime.GOOS)
   144  	}
   145  
   146  	origTestHookLookupIP := testHookLookupIP
   147  	defer func() { testHookLookupIP = origTestHookLookupIP }()
   148  	testHookLookupIP = func(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
   149  		return nil, &DNSError{Err: "dial error test", Name: "name", Server: "server", IsTimeout: true}
   150  	}
   151  	sw.Set(socktest.FilterConnect, func(so *socktest.Status) (socktest.AfterFilter, error) {
   152  		return nil, errOpNotSupported
   153  	})
   154  	defer sw.Set(socktest.FilterConnect, nil)
   155  
   156  	d := Dialer{Timeout: someTimeout}
   157  	for i, tt := range dialErrorTests {
   158  		i, tt := i, tt
   159  		t.Run(fmt.Sprint(i), func(t *testing.T) {
   160  			c, err := d.Dial(tt.network, tt.address)
   161  			if err == nil {
   162  				t.Errorf("should fail; %s:%s->%s", c.LocalAddr().Network(), c.LocalAddr(), c.RemoteAddr())
   163  				c.Close()
   164  				return
   165  			}
   166  			if tt.network == "tcp" || tt.network == "udp" {
   167  				nerr := err
   168  				if op, ok := nerr.(*OpError); ok {
   169  					nerr = op.Err
   170  				}
   171  				if sys, ok := nerr.(*os.SyscallError); ok {
   172  					nerr = sys.Err
   173  				}
   174  				if nerr == errOpNotSupported {
   175  					t.Fatalf("should fail without %v; %s:%s->", nerr, tt.network, tt.address)
   176  				}
   177  			}
   178  			if c != nil {
   179  				t.Errorf("Dial returned non-nil interface %T(%v) with err != nil", c, c)
   180  			}
   181  			if err = parseDialError(err); err != nil {
   182  				t.Error(err)
   183  			}
   184  		})
   185  	}
   186  }
   187  
   188  func TestProtocolDialError(t *testing.T) {
   189  	switch runtime.GOOS {
   190  	case "solaris", "illumos":
   191  		t.Skipf("not supported on %s", runtime.GOOS)
   192  	}
   193  
   194  	for _, network := range []string{"tcp", "udp", "ip:4294967296", "unix", "unixpacket", "unixgram"} {
   195  		var err error
   196  		switch network {
   197  		case "tcp":
   198  			_, err = DialTCP(network, nil, &TCPAddr{Port: 1 << 16})
   199  		case "udp":
   200  			_, err = DialUDP(network, nil, &UDPAddr{Port: 1 << 16})
   201  		case "ip:4294967296":
   202  			_, err = DialIP(network, nil, nil)
   203  		case "unix", "unixpacket", "unixgram":
   204  			_, err = DialUnix(network, nil, &UnixAddr{Name: "//"})
   205  		}
   206  		if err == nil {
   207  			t.Errorf("%s: should fail", network)
   208  			continue
   209  		}
   210  		if err := parseDialError(err); err != nil {
   211  			t.Errorf("%s: %v", network, err)
   212  			continue
   213  		}
   214  		t.Logf("%s: error as expected: %v", network, err)
   215  	}
   216  }
   217  
   218  func TestDialAddrError(t *testing.T) {
   219  	switch runtime.GOOS {
   220  	case "plan9":
   221  		t.Skipf("not supported on %s", runtime.GOOS)
   222  	}
   223  
   224  	if !supportsIPv4() || !supportsIPv6() {
   225  		t.Skip("both IPv4 and IPv6 are required")
   226  	}
   227  
   228  	for _, tt := range []struct {
   229  		network string
   230  		lit     string
   231  		addr    *TCPAddr
   232  	}{
   233  		{"tcp4", "::1", nil},
   234  		{"tcp4", "", &TCPAddr{IP: IPv6loopback}},
   235  		// We don't test the {"tcp6", "byte sequence", nil}
   236  		// case for now because there is no easy way to
   237  		// control name resolution.
   238  		{"tcp6", "", &TCPAddr{IP: IP{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}}},
   239  	} {
   240  		desc := tt.lit
   241  		if desc == "" {
   242  			desc = tt.addr.String()
   243  		}
   244  		t.Run(fmt.Sprintf("%s/%s", tt.network, desc), func(t *testing.T) {
   245  			var err error
   246  			var c Conn
   247  			var op string
   248  			if tt.lit != "" {
   249  				c, err = Dial(tt.network, JoinHostPort(tt.lit, "0"))
   250  				op = fmt.Sprintf("Dial(%q, %q)", tt.network, JoinHostPort(tt.lit, "0"))
   251  			} else {
   252  				c, err = DialTCP(tt.network, nil, tt.addr)
   253  				op = fmt.Sprintf("DialTCP(%q, %q)", tt.network, tt.addr)
   254  			}
   255  			t.Logf("%s: %v", op, err)
   256  			if err == nil {
   257  				c.Close()
   258  				t.Fatalf("%s succeeded, want error", op)
   259  			}
   260  			if perr := parseDialError(err); perr != nil {
   261  				t.Fatal(perr)
   262  			}
   263  			operr := err.(*OpError).Err
   264  			aerr, ok := operr.(*AddrError)
   265  			if !ok {
   266  				t.Fatalf("OpError.Err is %T, want *AddrError", operr)
   267  			}
   268  			want := tt.lit
   269  			if tt.lit == "" {
   270  				want = tt.addr.IP.String()
   271  			}
   272  			if aerr.Addr != want {
   273  				t.Errorf("error Addr=%q, want %q", aerr.Addr, want)
   274  			}
   275  		})
   276  	}
   277  }
   278  
   279  var listenErrorTests = []struct {
   280  	network, address string
   281  }{
   282  	{"foo", ""},
   283  	{"bar", "baz"},
   284  	{"datakit", "mh/astro/r70"},
   285  	{"tcp", "127.0.0.1:☺"},
   286  	{"tcp", "no-such-name:80"},
   287  	{"tcp", "mh/astro/r70:http"},
   288  
   289  	{"tcp", JoinHostPort("127.0.0.1", "-1")},
   290  	{"tcp", JoinHostPort("127.0.0.1", "123456789")},
   291  
   292  	{"unix", "/path/to/somewhere"},
   293  	{"unixpacket", "/path/to/somewhere"},
   294  }
   295  
   296  func TestListenError(t *testing.T) {
   297  	switch runtime.GOOS {
   298  	case "plan9":
   299  		t.Skipf("%s does not have full support of socktest", runtime.GOOS)
   300  	}
   301  
   302  	origTestHookLookupIP := testHookLookupIP
   303  	defer func() { testHookLookupIP = origTestHookLookupIP }()
   304  	testHookLookupIP = func(_ context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
   305  		return nil, &DNSError{Err: "listen error test", Name: "name", Server: "server", IsTimeout: true}
   306  	}
   307  	sw.Set(socktest.FilterListen, func(so *socktest.Status) (socktest.AfterFilter, error) {
   308  		return nil, errOpNotSupported
   309  	})
   310  	defer sw.Set(socktest.FilterListen, nil)
   311  
   312  	for i, tt := range listenErrorTests {
   313  		t.Run(fmt.Sprintf("%s_%s", tt.network, tt.address), func(t *testing.T) {
   314  			ln, err := Listen(tt.network, tt.address)
   315  			if err == nil {
   316  				t.Errorf("#%d: should fail; %s:%s->", i, ln.Addr().Network(), ln.Addr())
   317  				ln.Close()
   318  				return
   319  			}
   320  			if tt.network == "tcp" {
   321  				nerr := err
   322  				if op, ok := nerr.(*OpError); ok {
   323  					nerr = op.Err
   324  				}
   325  				if sys, ok := nerr.(*os.SyscallError); ok {
   326  					nerr = sys.Err
   327  				}
   328  				if nerr == errOpNotSupported {
   329  					t.Fatalf("#%d: should fail without %v; %s:%s->", i, nerr, tt.network, tt.address)
   330  				}
   331  			}
   332  			if ln != nil {
   333  				t.Errorf("Listen returned non-nil interface %T(%v) with err != nil", ln, ln)
   334  			}
   335  			if err = parseDialError(err); err != nil {
   336  				t.Errorf("#%d: %v", i, err)
   337  			}
   338  		})
   339  	}
   340  }
   341  
   342  var listenPacketErrorTests = []struct {
   343  	network, address string
   344  }{
   345  	{"foo", ""},
   346  	{"bar", "baz"},
   347  	{"datakit", "mh/astro/r70"},
   348  	{"udp", "127.0.0.1:☺"},
   349  	{"udp", "no-such-name:80"},
   350  	{"udp", "mh/astro/r70:http"},
   351  
   352  	{"udp", JoinHostPort("127.0.0.1", "-1")},
   353  	{"udp", JoinHostPort("127.0.0.1", "123456789")},
   354  }
   355  
   356  func TestListenPacketError(t *testing.T) {
   357  	switch runtime.GOOS {
   358  	case "plan9":
   359  		t.Skipf("%s does not have full support of socktest", runtime.GOOS)
   360  	}
   361  
   362  	origTestHookLookupIP := testHookLookupIP
   363  	defer func() { testHookLookupIP = origTestHookLookupIP }()
   364  	testHookLookupIP = func(_ context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
   365  		return nil, &DNSError{Err: "listen error test", Name: "name", Server: "server", IsTimeout: true}
   366  	}
   367  
   368  	for i, tt := range listenPacketErrorTests {
   369  		t.Run(fmt.Sprintf("%s_%s", tt.network, tt.address), func(t *testing.T) {
   370  			c, err := ListenPacket(tt.network, tt.address)
   371  			if err == nil {
   372  				t.Errorf("#%d: should fail; %s:%s->", i, c.LocalAddr().Network(), c.LocalAddr())
   373  				c.Close()
   374  				return
   375  			}
   376  			if c != nil {
   377  				t.Errorf("ListenPacket returned non-nil interface %T(%v) with err != nil", c, c)
   378  			}
   379  			if err = parseDialError(err); err != nil {
   380  				t.Errorf("#%d: %v", i, err)
   381  			}
   382  		})
   383  	}
   384  }
   385  
   386  func TestProtocolListenError(t *testing.T) {
   387  	switch runtime.GOOS {
   388  	case "plan9":
   389  		t.Skipf("not supported on %s", runtime.GOOS)
   390  	}
   391  
   392  	for _, network := range []string{"tcp", "udp", "ip:4294967296", "unix", "unixpacket", "unixgram"} {
   393  		var err error
   394  		switch network {
   395  		case "tcp":
   396  			_, err = ListenTCP(network, &TCPAddr{Port: 1 << 16})
   397  		case "udp":
   398  			_, err = ListenUDP(network, &UDPAddr{Port: 1 << 16})
   399  		case "ip:4294967296":
   400  			_, err = ListenIP(network, nil)
   401  		case "unix", "unixpacket":
   402  			_, err = ListenUnix(network, &UnixAddr{Name: "//"})
   403  		case "unixgram":
   404  			_, err = ListenUnixgram(network, &UnixAddr{Name: "//"})
   405  		}
   406  		if err == nil {
   407  			t.Errorf("%s: should fail", network)
   408  			continue
   409  		}
   410  		if err = parseDialError(err); err != nil {
   411  			t.Errorf("%s: %v", network, err)
   412  			continue
   413  		}
   414  	}
   415  }
   416  
   417  // parseReadError parses nestedErr and reports whether it is a valid
   418  // error value from Read functions.
   419  // It returns nil when nestedErr is valid.
   420  func parseReadError(nestedErr error) error {
   421  	if nestedErr == nil {
   422  		return nil
   423  	}
   424  
   425  	switch err := nestedErr.(type) {
   426  	case *OpError:
   427  		if err := err.isValid(); err != nil {
   428  			return err
   429  		}
   430  		nestedErr = err.Err
   431  		goto second
   432  	}
   433  	if nestedErr == io.EOF {
   434  		return nil
   435  	}
   436  	return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
   437  
   438  second:
   439  	if isPlatformError(nestedErr) {
   440  		return nil
   441  	}
   442  	switch err := nestedErr.(type) {
   443  	case *os.SyscallError:
   444  		nestedErr = err.Err
   445  		goto third
   446  	}
   447  	switch nestedErr {
   448  	case ErrClosed, errTimeout, poll.ErrNotPollable, os.ErrDeadlineExceeded:
   449  		return nil
   450  	}
   451  	return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
   452  
   453  third:
   454  	if isPlatformError(nestedErr) {
   455  		return nil
   456  	}
   457  	return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
   458  }
   459  
   460  // parseWriteError parses nestedErr and reports whether it is a valid
   461  // error value from Write functions.
   462  // It returns nil when nestedErr is valid.
   463  func parseWriteError(nestedErr error) error {
   464  	if nestedErr == nil {
   465  		return nil
   466  	}
   467  
   468  	switch err := nestedErr.(type) {
   469  	case *OpError:
   470  		if err := err.isValid(); err != nil {
   471  			return err
   472  		}
   473  		nestedErr = err.Err
   474  		goto second
   475  	}
   476  	return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
   477  
   478  second:
   479  	if isPlatformError(nestedErr) {
   480  		return nil
   481  	}
   482  	switch err := nestedErr.(type) {
   483  	case *AddrError, *timeoutError, *DNSError, InvalidAddrError, *ParseError, *poll.DeadlineExceededError, UnknownNetworkError:
   484  		return nil
   485  	case interface{ isAddrinfoErrno() }:
   486  		return nil
   487  	case *os.SyscallError:
   488  		nestedErr = err.Err
   489  		goto third
   490  	}
   491  	switch nestedErr {
   492  	case errCanceled, ErrClosed, errMissingAddress, errTimeout, os.ErrDeadlineExceeded, ErrWriteToConnected, io.ErrUnexpectedEOF:
   493  		return nil
   494  	}
   495  	return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
   496  
   497  third:
   498  	if isPlatformError(nestedErr) {
   499  		return nil
   500  	}
   501  	return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
   502  }
   503  
   504  // parseCloseError parses nestedErr and reports whether it is a valid
   505  // error value from Close functions.
   506  // It returns nil when nestedErr is valid.
   507  func parseCloseError(nestedErr error, isShutdown bool) error {
   508  	if nestedErr == nil {
   509  		return nil
   510  	}
   511  
   512  	// Because historically we have not exported the error that we
   513  	// return for an operation on a closed network connection,
   514  	// there are programs that test for the exact error string.
   515  	// Verify that string here so that we don't break those
   516  	// programs unexpectedly. See issues #4373 and #19252.
   517  	want := "use of closed network connection"
   518  	if !isShutdown && !strings.Contains(nestedErr.Error(), want) {
   519  		return fmt.Errorf("error string %q does not contain expected string %q", nestedErr, want)
   520  	}
   521  
   522  	if !isShutdown && !errors.Is(nestedErr, ErrClosed) {
   523  		return fmt.Errorf("errors.Is(%v, errClosed) returns false, want true", nestedErr)
   524  	}
   525  
   526  	switch err := nestedErr.(type) {
   527  	case *OpError:
   528  		if err := err.isValid(); err != nil {
   529  			return err
   530  		}
   531  		nestedErr = err.Err
   532  		goto second
   533  	}
   534  	return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
   535  
   536  second:
   537  	if isPlatformError(nestedErr) {
   538  		return nil
   539  	}
   540  	switch err := nestedErr.(type) {
   541  	case *os.SyscallError:
   542  		nestedErr = err.Err
   543  		goto third
   544  	case *fs.PathError: // for Plan 9
   545  		nestedErr = err.Err
   546  		goto third
   547  	}
   548  	switch nestedErr {
   549  	case ErrClosed:
   550  		return nil
   551  	}
   552  	return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
   553  
   554  third:
   555  	if isPlatformError(nestedErr) {
   556  		return nil
   557  	}
   558  	switch nestedErr {
   559  	case fs.ErrClosed: // for Plan 9
   560  		return nil
   561  	}
   562  	return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
   563  }
   564  
   565  func TestCloseError(t *testing.T) {
   566  	t.Run("tcp", func(t *testing.T) {
   567  		ln := newLocalListener(t, "tcp")
   568  		defer ln.Close()
   569  		c, err := Dial(ln.Addr().Network(), ln.Addr().String())
   570  		if err != nil {
   571  			t.Fatal(err)
   572  		}
   573  		defer c.Close()
   574  
   575  		for i := 0; i < 3; i++ {
   576  			err = c.(*TCPConn).CloseRead()
   577  			if perr := parseCloseError(err, true); perr != nil {
   578  				t.Errorf("#%d: %v", i, perr)
   579  			}
   580  		}
   581  		for i := 0; i < 3; i++ {
   582  			err = c.(*TCPConn).CloseWrite()
   583  			if perr := parseCloseError(err, true); perr != nil {
   584  				t.Errorf("#%d: %v", i, perr)
   585  			}
   586  		}
   587  		for i := 0; i < 3; i++ {
   588  			err = c.Close()
   589  			if perr := parseCloseError(err, false); perr != nil {
   590  				t.Errorf("#%d: %v", i, perr)
   591  			}
   592  			err = ln.Close()
   593  			if perr := parseCloseError(err, false); perr != nil {
   594  				t.Errorf("#%d: %v", i, perr)
   595  			}
   596  		}
   597  	})
   598  
   599  	t.Run("udp", func(t *testing.T) {
   600  		if !testableNetwork("udp") {
   601  			t.Skipf("skipping: udp not available")
   602  		}
   603  
   604  		pc, err := ListenPacket("udp", "127.0.0.1:0")
   605  		if err != nil {
   606  			t.Fatal(err)
   607  		}
   608  		defer pc.Close()
   609  
   610  		for i := 0; i < 3; i++ {
   611  			err = pc.Close()
   612  			if perr := parseCloseError(err, false); perr != nil {
   613  				t.Errorf("#%d: %v", i, perr)
   614  			}
   615  		}
   616  	})
   617  }
   618  
   619  // parseAcceptError parses nestedErr and reports whether it is a valid
   620  // error value from Accept functions.
   621  // It returns nil when nestedErr is valid.
   622  func parseAcceptError(nestedErr error) error {
   623  	if nestedErr == nil {
   624  		return nil
   625  	}
   626  
   627  	switch err := nestedErr.(type) {
   628  	case *OpError:
   629  		if err := err.isValid(); err != nil {
   630  			return err
   631  		}
   632  		nestedErr = err.Err
   633  		goto second
   634  	}
   635  	return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
   636  
   637  second:
   638  	if isPlatformError(nestedErr) {
   639  		return nil
   640  	}
   641  	switch err := nestedErr.(type) {
   642  	case *os.SyscallError:
   643  		nestedErr = err.Err
   644  		goto third
   645  	case *fs.PathError: // for Plan 9
   646  		nestedErr = err.Err
   647  		goto third
   648  	}
   649  	switch nestedErr {
   650  	case ErrClosed, errTimeout, poll.ErrNotPollable, os.ErrDeadlineExceeded:
   651  		return nil
   652  	}
   653  	return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
   654  
   655  third:
   656  	if isPlatformError(nestedErr) {
   657  		return nil
   658  	}
   659  	return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
   660  }
   661  
   662  func TestAcceptError(t *testing.T) {
   663  	handler := func(ls *localServer, ln Listener) {
   664  		for {
   665  			ln.(*TCPListener).SetDeadline(time.Now().Add(5 * time.Millisecond))
   666  			c, err := ln.Accept()
   667  			if perr := parseAcceptError(err); perr != nil {
   668  				t.Error(perr)
   669  			}
   670  			if err != nil {
   671  				if c != nil {
   672  					t.Errorf("Accept returned non-nil interface %T(%v) with err != nil", c, c)
   673  				}
   674  				if nerr, ok := err.(Error); !ok || (!nerr.Timeout() && !nerr.Temporary()) {
   675  					return
   676  				}
   677  				continue
   678  			}
   679  			c.Close()
   680  		}
   681  	}
   682  	ls := newLocalServer(t, "tcp")
   683  	if err := ls.buildup(handler); err != nil {
   684  		ls.teardown()
   685  		t.Fatal(err)
   686  	}
   687  
   688  	time.Sleep(100 * time.Millisecond)
   689  	ls.teardown()
   690  }
   691  
   692  // parseCommonError parses nestedErr and reports whether it is a valid
   693  // error value from miscellaneous functions.
   694  // It returns nil when nestedErr is valid.
   695  func parseCommonError(nestedErr error) error {
   696  	if nestedErr == nil {
   697  		return nil
   698  	}
   699  
   700  	switch err := nestedErr.(type) {
   701  	case *OpError:
   702  		if err := err.isValid(); err != nil {
   703  			return err
   704  		}
   705  		nestedErr = err.Err
   706  		goto second
   707  	}
   708  	return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
   709  
   710  second:
   711  	if isPlatformError(nestedErr) {
   712  		return nil
   713  	}
   714  	switch err := nestedErr.(type) {
   715  	case *os.SyscallError:
   716  		nestedErr = err.Err
   717  		goto third
   718  	case *os.LinkError:
   719  		nestedErr = err.Err
   720  		goto third
   721  	case *fs.PathError:
   722  		nestedErr = err.Err
   723  		goto third
   724  	}
   725  	switch nestedErr {
   726  	case ErrClosed:
   727  		return nil
   728  	}
   729  	return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
   730  
   731  third:
   732  	if isPlatformError(nestedErr) {
   733  		return nil
   734  	}
   735  	return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
   736  }
   737  
   738  func TestFileError(t *testing.T) {
   739  	switch runtime.GOOS {
   740  	case "windows":
   741  		t.Skipf("not supported on %s", runtime.GOOS)
   742  	}
   743  
   744  	f, err := os.CreateTemp("", "go-nettest")
   745  	if err != nil {
   746  		t.Fatal(err)
   747  	}
   748  	defer os.Remove(f.Name())
   749  	defer f.Close()
   750  
   751  	c, err := FileConn(f)
   752  	if err != nil {
   753  		if c != nil {
   754  			t.Errorf("FileConn returned non-nil interface %T(%v) with err != nil", c, c)
   755  		}
   756  		if perr := parseCommonError(err); perr != nil {
   757  			t.Error(perr)
   758  		}
   759  	} else {
   760  		c.Close()
   761  		t.Error("should fail")
   762  	}
   763  	ln, err := FileListener(f)
   764  	if err != nil {
   765  		if ln != nil {
   766  			t.Errorf("FileListener returned non-nil interface %T(%v) with err != nil", ln, ln)
   767  		}
   768  		if perr := parseCommonError(err); perr != nil {
   769  			t.Error(perr)
   770  		}
   771  	} else {
   772  		ln.Close()
   773  		t.Error("should fail")
   774  	}
   775  	pc, err := FilePacketConn(f)
   776  	if err != nil {
   777  		if pc != nil {
   778  			t.Errorf("FilePacketConn returned non-nil interface %T(%v) with err != nil", pc, pc)
   779  		}
   780  		if perr := parseCommonError(err); perr != nil {
   781  			t.Error(perr)
   782  		}
   783  	} else {
   784  		pc.Close()
   785  		t.Error("should fail")
   786  	}
   787  
   788  	ln = newLocalListener(t, "tcp")
   789  
   790  	for i := 0; i < 3; i++ {
   791  		f, err := ln.(*TCPListener).File()
   792  		if err != nil {
   793  			if perr := parseCommonError(err); perr != nil {
   794  				t.Error(perr)
   795  			}
   796  		} else {
   797  			f.Close()
   798  		}
   799  		ln.Close()
   800  	}
   801  }
   802  
   803  func parseLookupPortError(nestedErr error) error {
   804  	if nestedErr == nil {
   805  		return nil
   806  	}
   807  
   808  	switch nestedErr.(type) {
   809  	case *AddrError, *DNSError:
   810  		return nil
   811  	case *fs.PathError: // for Plan 9
   812  		return nil
   813  	}
   814  	return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
   815  }
   816  
   817  func TestContextError(t *testing.T) {
   818  	if !errors.Is(errCanceled, context.Canceled) {
   819  		t.Error("errCanceled is not context.Canceled")
   820  	}
   821  	if !errors.Is(errTimeout, context.DeadlineExceeded) {
   822  		t.Error("errTimeout is not context.DeadlineExceeded")
   823  	}
   824  }
   825  

View as plain text