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

View as plain text