Black Lives Matter. Support the Equal Justice Initiative.

Source file src/net/error_test.go

Documentation: net

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

View as plain text