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

View as plain text