|  | // Copyright 2009 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | //go:build !js | 
|  |  | 
|  | package net | 
|  |  | 
|  | import ( | 
|  | "errors" | 
|  | "fmt" | 
|  | "io" | 
|  | "net/internal/socktest" | 
|  | "os" | 
|  | "runtime" | 
|  | "testing" | 
|  | "time" | 
|  | ) | 
|  |  | 
|  | func TestCloseRead(t *testing.T) { | 
|  | switch runtime.GOOS { | 
|  | case "plan9": | 
|  | t.Skipf("not supported on %s", runtime.GOOS) | 
|  | } | 
|  | t.Parallel() | 
|  |  | 
|  | for _, network := range []string{"tcp", "unix", "unixpacket"} { | 
|  | network := network | 
|  | t.Run(network, func(t *testing.T) { | 
|  | if !testableNetwork(network) { | 
|  | t.Skipf("network %s is not testable on the current platform", network) | 
|  | } | 
|  | t.Parallel() | 
|  |  | 
|  | ln := newLocalListener(t, network) | 
|  | switch network { | 
|  | case "unix", "unixpacket": | 
|  | defer os.Remove(ln.Addr().String()) | 
|  | } | 
|  | defer ln.Close() | 
|  |  | 
|  | c, err := Dial(ln.Addr().Network(), ln.Addr().String()) | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | switch network { | 
|  | case "unix", "unixpacket": | 
|  | defer os.Remove(c.LocalAddr().String()) | 
|  | } | 
|  | defer c.Close() | 
|  |  | 
|  | switch c := c.(type) { | 
|  | case *TCPConn: | 
|  | err = c.CloseRead() | 
|  | case *UnixConn: | 
|  | err = c.CloseRead() | 
|  | } | 
|  | if err != nil { | 
|  | if perr := parseCloseError(err, true); perr != nil { | 
|  | t.Error(perr) | 
|  | } | 
|  | t.Fatal(err) | 
|  | } | 
|  | var b [1]byte | 
|  | n, err := c.Read(b[:]) | 
|  | if n != 0 || err == nil { | 
|  | t.Fatalf("got (%d, %v); want (0, error)", n, err) | 
|  | } | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestCloseWrite(t *testing.T) { | 
|  | switch runtime.GOOS { | 
|  | case "plan9": | 
|  | t.Skipf("not supported on %s", runtime.GOOS) | 
|  | } | 
|  |  | 
|  | t.Parallel() | 
|  | deadline, _ := t.Deadline() | 
|  | if !deadline.IsZero() { | 
|  | // Leave 10% headroom on the deadline to report errors and clean up. | 
|  | deadline = deadline.Add(-time.Until(deadline) / 10) | 
|  | } | 
|  |  | 
|  | for _, network := range []string{"tcp", "unix", "unixpacket"} { | 
|  | network := network | 
|  | t.Run(network, func(t *testing.T) { | 
|  | if !testableNetwork(network) { | 
|  | t.Skipf("network %s is not testable on the current platform", network) | 
|  | } | 
|  | t.Parallel() | 
|  |  | 
|  | handler := func(ls *localServer, ln Listener) { | 
|  | c, err := ln.Accept() | 
|  | if err != nil { | 
|  | t.Error(err) | 
|  | return | 
|  | } | 
|  | if !deadline.IsZero() { | 
|  | c.SetDeadline(deadline) | 
|  | } | 
|  | defer c.Close() | 
|  |  | 
|  | var b [1]byte | 
|  | n, err := c.Read(b[:]) | 
|  | if n != 0 || err != io.EOF { | 
|  | t.Errorf("got (%d, %v); want (0, io.EOF)", n, err) | 
|  | return | 
|  | } | 
|  | switch c := c.(type) { | 
|  | case *TCPConn: | 
|  | err = c.CloseWrite() | 
|  | case *UnixConn: | 
|  | err = c.CloseWrite() | 
|  | } | 
|  | if err != nil { | 
|  | if perr := parseCloseError(err, true); perr != nil { | 
|  | t.Error(perr) | 
|  | } | 
|  | t.Error(err) | 
|  | return | 
|  | } | 
|  | n, err = c.Write(b[:]) | 
|  | if err == nil { | 
|  | t.Errorf("got (%d, %v); want (any, error)", n, err) | 
|  | return | 
|  | } | 
|  | } | 
|  |  | 
|  | ls := newLocalServer(t, network) | 
|  | defer ls.teardown() | 
|  | if err := ls.buildup(handler); err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  |  | 
|  | c, err := Dial(ls.Listener.Addr().Network(), ls.Listener.Addr().String()) | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | if !deadline.IsZero() { | 
|  | c.SetDeadline(deadline) | 
|  | } | 
|  | switch network { | 
|  | case "unix", "unixpacket": | 
|  | defer os.Remove(c.LocalAddr().String()) | 
|  | } | 
|  | defer c.Close() | 
|  |  | 
|  | switch c := c.(type) { | 
|  | case *TCPConn: | 
|  | err = c.CloseWrite() | 
|  | case *UnixConn: | 
|  | err = c.CloseWrite() | 
|  | } | 
|  | if err != nil { | 
|  | if perr := parseCloseError(err, true); perr != nil { | 
|  | t.Error(perr) | 
|  | } | 
|  | t.Fatal(err) | 
|  | } | 
|  | var b [1]byte | 
|  | n, err := c.Read(b[:]) | 
|  | if n != 0 || err != io.EOF { | 
|  | t.Fatalf("got (%d, %v); want (0, io.EOF)", n, err) | 
|  | } | 
|  | n, err = c.Write(b[:]) | 
|  | if err == nil { | 
|  | t.Fatalf("got (%d, %v); want (any, error)", n, err) | 
|  | } | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestConnClose(t *testing.T) { | 
|  | t.Parallel() | 
|  | for _, network := range []string{"tcp", "unix", "unixpacket"} { | 
|  | network := network | 
|  | t.Run(network, func(t *testing.T) { | 
|  | if !testableNetwork(network) { | 
|  | t.Skipf("network %s is not testable on the current platform", network) | 
|  | } | 
|  | t.Parallel() | 
|  |  | 
|  | ln := newLocalListener(t, network) | 
|  | switch network { | 
|  | case "unix", "unixpacket": | 
|  | defer os.Remove(ln.Addr().String()) | 
|  | } | 
|  | defer ln.Close() | 
|  |  | 
|  | c, err := Dial(ln.Addr().Network(), ln.Addr().String()) | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | switch network { | 
|  | case "unix", "unixpacket": | 
|  | defer os.Remove(c.LocalAddr().String()) | 
|  | } | 
|  | defer c.Close() | 
|  |  | 
|  | if err := c.Close(); err != nil { | 
|  | if perr := parseCloseError(err, false); perr != nil { | 
|  | t.Error(perr) | 
|  | } | 
|  | t.Fatal(err) | 
|  | } | 
|  | var b [1]byte | 
|  | n, err := c.Read(b[:]) | 
|  | if n != 0 || err == nil { | 
|  | t.Fatalf("got (%d, %v); want (0, error)", n, err) | 
|  | } | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestListenerClose(t *testing.T) { | 
|  | t.Parallel() | 
|  | for _, network := range []string{"tcp", "unix", "unixpacket"} { | 
|  | network := network | 
|  | t.Run(network, func(t *testing.T) { | 
|  | if !testableNetwork(network) { | 
|  | t.Skipf("network %s is not testable on the current platform", network) | 
|  | } | 
|  | t.Parallel() | 
|  |  | 
|  | ln := newLocalListener(t, network) | 
|  | switch network { | 
|  | case "unix", "unixpacket": | 
|  | defer os.Remove(ln.Addr().String()) | 
|  | } | 
|  |  | 
|  | if err := ln.Close(); err != nil { | 
|  | if perr := parseCloseError(err, false); perr != nil { | 
|  | t.Error(perr) | 
|  | } | 
|  | t.Fatal(err) | 
|  | } | 
|  | c, err := ln.Accept() | 
|  | if err == nil { | 
|  | c.Close() | 
|  | t.Fatal("should fail") | 
|  | } | 
|  |  | 
|  | // Note: we cannot ensure that a subsequent Dial does not succeed, because | 
|  | // we do not in general have any guarantee that ln.Addr is not immediately | 
|  | // reused. (TCP sockets enter a TIME_WAIT state when closed, but that only | 
|  | // applies to existing connections for the port — it does not prevent the | 
|  | // port itself from being used for entirely new connections in the | 
|  | // meantime.) | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestPacketConnClose(t *testing.T) { | 
|  | t.Parallel() | 
|  | for _, network := range []string{"udp", "unixgram"} { | 
|  | network := network | 
|  | t.Run(network, func(t *testing.T) { | 
|  | if !testableNetwork(network) { | 
|  | t.Skipf("network %s is not testable on the current platform", network) | 
|  | } | 
|  | t.Parallel() | 
|  |  | 
|  | c := newLocalPacketListener(t, network) | 
|  | switch network { | 
|  | case "unixgram": | 
|  | defer os.Remove(c.LocalAddr().String()) | 
|  | } | 
|  | defer c.Close() | 
|  |  | 
|  | if err := c.Close(); err != nil { | 
|  | if perr := parseCloseError(err, false); perr != nil { | 
|  | t.Error(perr) | 
|  | } | 
|  | t.Fatal(err) | 
|  | } | 
|  | var b [1]byte | 
|  | n, _, err := c.ReadFrom(b[:]) | 
|  | if n != 0 || err == nil { | 
|  | t.Fatalf("got (%d, %v); want (0, error)", n, err) | 
|  | } | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestListenCloseListen(t *testing.T) { | 
|  | const maxTries = 10 | 
|  | for tries := 0; tries < maxTries; tries++ { | 
|  | ln := newLocalListener(t, "tcp") | 
|  | addr := ln.Addr().String() | 
|  | // TODO: This is racy. The selected address could be reused in between this | 
|  | // Close and the subsequent Listen. | 
|  | if err := ln.Close(); err != nil { | 
|  | if perr := parseCloseError(err, false); perr != nil { | 
|  | t.Error(perr) | 
|  | } | 
|  | t.Fatal(err) | 
|  | } | 
|  | ln, err := Listen("tcp", addr) | 
|  | if err == nil { | 
|  | // Success. (This test didn't always make it here earlier.) | 
|  | ln.Close() | 
|  | return | 
|  | } | 
|  | t.Errorf("failed on try %d/%d: %v", tries+1, maxTries, err) | 
|  | } | 
|  | t.Fatalf("failed to listen/close/listen on same address after %d tries", maxTries) | 
|  | } | 
|  |  | 
|  | // See golang.org/issue/6163, golang.org/issue/6987. | 
|  | func TestAcceptIgnoreAbortedConnRequest(t *testing.T) { | 
|  | switch runtime.GOOS { | 
|  | case "plan9": | 
|  | t.Skipf("%s does not have full support of socktest", runtime.GOOS) | 
|  | } | 
|  |  | 
|  | syserr := make(chan error) | 
|  | go func() { | 
|  | defer close(syserr) | 
|  | for _, err := range abortedConnRequestErrors { | 
|  | syserr <- err | 
|  | } | 
|  | }() | 
|  | sw.Set(socktest.FilterAccept, func(so *socktest.Status) (socktest.AfterFilter, error) { | 
|  | if err, ok := <-syserr; ok { | 
|  | return nil, err | 
|  | } | 
|  | return nil, nil | 
|  | }) | 
|  | defer sw.Set(socktest.FilterAccept, nil) | 
|  |  | 
|  | operr := make(chan error, 1) | 
|  | handler := func(ls *localServer, ln Listener) { | 
|  | defer close(operr) | 
|  | c, err := ln.Accept() | 
|  | if err != nil { | 
|  | if perr := parseAcceptError(err); perr != nil { | 
|  | operr <- perr | 
|  | } | 
|  | operr <- err | 
|  | return | 
|  | } | 
|  | c.Close() | 
|  | } | 
|  | ls := newLocalServer(t, "tcp") | 
|  | defer ls.teardown() | 
|  | if err := ls.buildup(handler); err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  |  | 
|  | c, err := Dial(ls.Listener.Addr().Network(), ls.Listener.Addr().String()) | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | c.Close() | 
|  |  | 
|  | for err := range operr { | 
|  | t.Error(err) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestZeroByteRead(t *testing.T) { | 
|  | t.Parallel() | 
|  | for _, network := range []string{"tcp", "unix", "unixpacket"} { | 
|  | network := network | 
|  | t.Run(network, func(t *testing.T) { | 
|  | if !testableNetwork(network) { | 
|  | t.Skipf("network %s is not testable on the current platform", network) | 
|  | } | 
|  | t.Parallel() | 
|  |  | 
|  | ln := newLocalListener(t, network) | 
|  | connc := make(chan Conn, 1) | 
|  | go func() { | 
|  | defer ln.Close() | 
|  | c, err := ln.Accept() | 
|  | if err != nil { | 
|  | t.Error(err) | 
|  | } | 
|  | connc <- c // might be nil | 
|  | }() | 
|  | c, err := Dial(network, ln.Addr().String()) | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | defer c.Close() | 
|  | sc := <-connc | 
|  | if sc == nil { | 
|  | return | 
|  | } | 
|  | defer sc.Close() | 
|  |  | 
|  | if runtime.GOOS == "windows" { | 
|  | // A zero byte read on Windows caused a wait for readability first. | 
|  | // Rather than change that behavior, satisfy it in this test. | 
|  | // See Issue 15735. | 
|  | go io.WriteString(sc, "a") | 
|  | } | 
|  |  | 
|  | n, err := c.Read(nil) | 
|  | if n != 0 || err != nil { | 
|  | t.Errorf("%s: zero byte client read = %v, %v; want 0, nil", network, n, err) | 
|  | } | 
|  |  | 
|  | if runtime.GOOS == "windows" { | 
|  | // Same as comment above. | 
|  | go io.WriteString(c, "a") | 
|  | } | 
|  | n, err = sc.Read(nil) | 
|  | if n != 0 || err != nil { | 
|  | t.Errorf("%s: zero byte server read = %v, %v; want 0, nil", network, n, err) | 
|  | } | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | // withTCPConnPair sets up a TCP connection between two peers, then | 
|  | // runs peer1 and peer2 concurrently. withTCPConnPair returns when | 
|  | // both have completed. | 
|  | func withTCPConnPair(t *testing.T, peer1, peer2 func(c *TCPConn) error) { | 
|  | ln := newLocalListener(t, "tcp") | 
|  | defer ln.Close() | 
|  | errc := make(chan error, 2) | 
|  | go func() { | 
|  | c1, err := ln.Accept() | 
|  | if err != nil { | 
|  | errc <- err | 
|  | return | 
|  | } | 
|  | defer c1.Close() | 
|  | errc <- peer1(c1.(*TCPConn)) | 
|  | }() | 
|  | go func() { | 
|  | c2, err := Dial("tcp", ln.Addr().String()) | 
|  | if err != nil { | 
|  | errc <- err | 
|  | return | 
|  | } | 
|  | defer c2.Close() | 
|  | errc <- peer2(c2.(*TCPConn)) | 
|  | }() | 
|  | for i := 0; i < 2; i++ { | 
|  | if err := <-errc; err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Tests that a blocked Read is interrupted by a concurrent SetReadDeadline | 
|  | // modifying that Conn's read deadline to the past. | 
|  | // See golang.org/cl/30164 which documented this. The net/http package | 
|  | // depends on this. | 
|  | func TestReadTimeoutUnblocksRead(t *testing.T) { | 
|  | serverDone := make(chan struct{}) | 
|  | server := func(cs *TCPConn) error { | 
|  | defer close(serverDone) | 
|  | errc := make(chan error, 1) | 
|  | go func() { | 
|  | defer close(errc) | 
|  | go func() { | 
|  | // TODO: find a better way to wait | 
|  | // until we're blocked in the cs.Read | 
|  | // call below. Sleep is lame. | 
|  | time.Sleep(100 * time.Millisecond) | 
|  |  | 
|  | // Interrupt the upcoming Read, unblocking it: | 
|  | cs.SetReadDeadline(time.Unix(123, 0)) // time in the past | 
|  | }() | 
|  | var buf [1]byte | 
|  | n, err := cs.Read(buf[:1]) | 
|  | if n != 0 || err == nil { | 
|  | errc <- fmt.Errorf("Read = %v, %v; want 0, non-nil", n, err) | 
|  | } | 
|  | }() | 
|  | select { | 
|  | case err := <-errc: | 
|  | return err | 
|  | case <-time.After(5 * time.Second): | 
|  | buf := make([]byte, 2<<20) | 
|  | buf = buf[:runtime.Stack(buf, true)] | 
|  | println("Stacks at timeout:\n", string(buf)) | 
|  | return errors.New("timeout waiting for Read to finish") | 
|  | } | 
|  |  | 
|  | } | 
|  | // Do nothing in the client. Never write. Just wait for the | 
|  | // server's half to be done. | 
|  | client := func(*TCPConn) error { | 
|  | <-serverDone | 
|  | return nil | 
|  | } | 
|  | withTCPConnPair(t, client, server) | 
|  | } | 
|  |  | 
|  | // Issue 17695: verify that a blocked Read is woken up by a Close. | 
|  | func TestCloseUnblocksRead(t *testing.T) { | 
|  | t.Parallel() | 
|  | server := func(cs *TCPConn) error { | 
|  | // Give the client time to get stuck in a Read: | 
|  | time.Sleep(20 * time.Millisecond) | 
|  | cs.Close() | 
|  | return nil | 
|  | } | 
|  | client := func(ss *TCPConn) error { | 
|  | n, err := ss.Read([]byte{0}) | 
|  | if n != 0 || err != io.EOF { | 
|  | return fmt.Errorf("Read = %v, %v; want 0, EOF", n, err) | 
|  | } | 
|  | return nil | 
|  | } | 
|  | withTCPConnPair(t, client, server) | 
|  | } | 
|  |  | 
|  | // Issue 24808: verify that ECONNRESET is not temporary for read. | 
|  | func TestNotTemporaryRead(t *testing.T) { | 
|  | t.Parallel() | 
|  |  | 
|  | ln := newLocalListener(t, "tcp") | 
|  | serverDone := make(chan struct{}) | 
|  | dialed := make(chan struct{}) | 
|  | go func() { | 
|  | defer close(serverDone) | 
|  |  | 
|  | cs, err := ln.Accept() | 
|  | if err != nil { | 
|  | return | 
|  | } | 
|  | <-dialed | 
|  | cs.(*TCPConn).SetLinger(0) | 
|  | cs.Close() | 
|  | }() | 
|  | defer func() { | 
|  | ln.Close() | 
|  | <-serverDone | 
|  | }() | 
|  |  | 
|  | ss, err := Dial("tcp", ln.Addr().String()) | 
|  | close(dialed) | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | defer ss.Close() | 
|  |  | 
|  | _, err = ss.Read([]byte{0}) | 
|  | if err == nil { | 
|  | t.Fatal("Read succeeded unexpectedly") | 
|  | } else if err == io.EOF { | 
|  | // This happens on Plan 9, but for some reason (prior to CL 385314) it was | 
|  | // accepted everywhere else too. | 
|  | if runtime.GOOS == "plan9" { | 
|  | return | 
|  | } | 
|  | t.Fatal("Read unexpectedly returned io.EOF after socket was abruptly closed") | 
|  | } | 
|  | if ne, ok := err.(Error); !ok { | 
|  | t.Errorf("Read error does not implement net.Error: %v", err) | 
|  | } else if ne.Temporary() { | 
|  | t.Errorf("Read error is unexpectedly temporary: %v", err) | 
|  | } | 
|  | } | 
|  |  | 
|  | // The various errors should implement the Error interface. | 
|  | func TestErrors(t *testing.T) { | 
|  | var ( | 
|  | _ Error = &OpError{} | 
|  | _ Error = &ParseError{} | 
|  | _ Error = &AddrError{} | 
|  | _ Error = UnknownNetworkError("") | 
|  | _ Error = InvalidAddrError("") | 
|  | _ Error = &timeoutError{} | 
|  | _ Error = &DNSConfigError{} | 
|  | _ Error = &DNSError{} | 
|  | ) | 
|  |  | 
|  | // ErrClosed was introduced as type error, so we can't check | 
|  | // it using a declaration. | 
|  | if _, ok := ErrClosed.(Error); !ok { | 
|  | t.Fatal("ErrClosed does not implement Error") | 
|  | } | 
|  | } |