diff options
Diffstat (limited to 'src/net/mockserver_test.go')
-rw-r--r-- | src/net/mockserver_test.go | 528 |
1 files changed, 528 insertions, 0 deletions
diff --git a/src/net/mockserver_test.go b/src/net/mockserver_test.go new file mode 100644 index 0000000..867e31e --- /dev/null +++ b/src/net/mockserver_test.go @@ -0,0 +1,528 @@ +// Copyright 2013 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. + +// +build !js + +package net + +import ( + "errors" + "fmt" + "os" + "sync" + "testing" + "time" +) + +// testUnixAddr uses os.CreateTemp to get a name that is unique. +func testUnixAddr() string { + f, err := os.CreateTemp("", "go-nettest") + if err != nil { + panic(err) + } + addr := f.Name() + f.Close() + os.Remove(addr) + return addr +} + +func newLocalListener(network string) (Listener, error) { + switch network { + case "tcp": + if supportsIPv4() { + if ln, err := Listen("tcp4", "127.0.0.1:0"); err == nil { + return ln, nil + } + } + if supportsIPv6() { + return Listen("tcp6", "[::1]:0") + } + case "tcp4": + if supportsIPv4() { + return Listen("tcp4", "127.0.0.1:0") + } + case "tcp6": + if supportsIPv6() { + return Listen("tcp6", "[::1]:0") + } + case "unix", "unixpacket": + return Listen(network, testUnixAddr()) + } + return nil, fmt.Errorf("%s is not supported", network) +} + +func newDualStackListener() (lns []*TCPListener, err error) { + var args = []struct { + network string + TCPAddr + }{ + {"tcp4", TCPAddr{IP: IPv4(127, 0, 0, 1)}}, + {"tcp6", TCPAddr{IP: IPv6loopback}}, + } + for i := 0; i < 64; i++ { + var port int + var lns []*TCPListener + for _, arg := range args { + arg.TCPAddr.Port = port + ln, err := ListenTCP(arg.network, &arg.TCPAddr) + if err != nil { + continue + } + port = ln.Addr().(*TCPAddr).Port + lns = append(lns, ln) + } + if len(lns) != len(args) { + for _, ln := range lns { + ln.Close() + } + continue + } + return lns, nil + } + return nil, errors.New("no dualstack port available") +} + +type localServer struct { + lnmu sync.RWMutex + Listener + done chan bool // signal that indicates server stopped + cl []Conn // accepted connection list +} + +func (ls *localServer) buildup(handler func(*localServer, Listener)) error { + go func() { + handler(ls, ls.Listener) + close(ls.done) + }() + return nil +} + +func (ls *localServer) teardown() error { + ls.lnmu.Lock() + defer ls.lnmu.Unlock() + if ls.Listener != nil { + network := ls.Listener.Addr().Network() + address := ls.Listener.Addr().String() + ls.Listener.Close() + for _, c := range ls.cl { + if err := c.Close(); err != nil { + return err + } + } + <-ls.done + ls.Listener = nil + switch network { + case "unix", "unixpacket": + os.Remove(address) + } + } + return nil +} + +func newLocalServer(network string) (*localServer, error) { + ln, err := newLocalListener(network) + if err != nil { + return nil, err + } + return &localServer{Listener: ln, done: make(chan bool)}, nil +} + +type streamListener struct { + network, address string + Listener + done chan bool // signal that indicates server stopped +} + +func (sl *streamListener) newLocalServer() (*localServer, error) { + return &localServer{Listener: sl.Listener, done: make(chan bool)}, nil +} + +type dualStackServer struct { + lnmu sync.RWMutex + lns []streamListener + port string + + cmu sync.RWMutex + cs []Conn // established connections at the passive open side +} + +func (dss *dualStackServer) buildup(handler func(*dualStackServer, Listener)) error { + for i := range dss.lns { + go func(i int) { + handler(dss, dss.lns[i].Listener) + close(dss.lns[i].done) + }(i) + } + return nil +} + +func (dss *dualStackServer) teardownNetwork(network string) error { + dss.lnmu.Lock() + for i := range dss.lns { + if network == dss.lns[i].network && dss.lns[i].Listener != nil { + dss.lns[i].Listener.Close() + <-dss.lns[i].done + dss.lns[i].Listener = nil + } + } + dss.lnmu.Unlock() + return nil +} + +func (dss *dualStackServer) teardown() error { + dss.lnmu.Lock() + for i := range dss.lns { + if dss.lns[i].Listener != nil { + dss.lns[i].Listener.Close() + <-dss.lns[i].done + } + } + dss.lns = dss.lns[:0] + dss.lnmu.Unlock() + dss.cmu.Lock() + for _, c := range dss.cs { + c.Close() + } + dss.cs = dss.cs[:0] + dss.cmu.Unlock() + return nil +} + +func newDualStackServer() (*dualStackServer, error) { + lns, err := newDualStackListener() + if err != nil { + return nil, err + } + _, port, err := SplitHostPort(lns[0].Addr().String()) + if err != nil { + lns[0].Close() + lns[1].Close() + return nil, err + } + return &dualStackServer{ + lns: []streamListener{ + {network: "tcp4", address: lns[0].Addr().String(), Listener: lns[0], done: make(chan bool)}, + {network: "tcp6", address: lns[1].Addr().String(), Listener: lns[1], done: make(chan bool)}, + }, + port: port, + }, nil +} + +func (ls *localServer) transponder(ln Listener, ch chan<- error) { + defer close(ch) + + switch ln := ln.(type) { + case *TCPListener: + ln.SetDeadline(time.Now().Add(someTimeout)) + case *UnixListener: + ln.SetDeadline(time.Now().Add(someTimeout)) + } + c, err := ln.Accept() + if err != nil { + if perr := parseAcceptError(err); perr != nil { + ch <- perr + } + ch <- err + return + } + ls.cl = append(ls.cl, c) + + network := ln.Addr().Network() + if c.LocalAddr().Network() != network || c.RemoteAddr().Network() != network { + ch <- fmt.Errorf("got %v->%v; expected %v->%v", c.LocalAddr().Network(), c.RemoteAddr().Network(), network, network) + return + } + c.SetDeadline(time.Now().Add(someTimeout)) + c.SetReadDeadline(time.Now().Add(someTimeout)) + c.SetWriteDeadline(time.Now().Add(someTimeout)) + + b := make([]byte, 256) + n, err := c.Read(b) + if err != nil { + if perr := parseReadError(err); perr != nil { + ch <- perr + } + ch <- err + return + } + if _, err := c.Write(b[:n]); err != nil { + if perr := parseWriteError(err); perr != nil { + ch <- perr + } + ch <- err + return + } +} + +func transceiver(c Conn, wb []byte, ch chan<- error) { + defer close(ch) + + c.SetDeadline(time.Now().Add(someTimeout)) + c.SetReadDeadline(time.Now().Add(someTimeout)) + c.SetWriteDeadline(time.Now().Add(someTimeout)) + + n, err := c.Write(wb) + if err != nil { + if perr := parseWriteError(err); perr != nil { + ch <- perr + } + ch <- err + return + } + if n != len(wb) { + ch <- fmt.Errorf("wrote %d; want %d", n, len(wb)) + } + rb := make([]byte, len(wb)) + n, err = c.Read(rb) + if err != nil { + if perr := parseReadError(err); perr != nil { + ch <- perr + } + ch <- err + return + } + if n != len(wb) { + ch <- fmt.Errorf("read %d; want %d", n, len(wb)) + } +} + +func timeoutReceiver(c Conn, d, min, max time.Duration, ch chan<- error) { + var err error + defer func() { ch <- err }() + + t0 := time.Now() + if err = c.SetReadDeadline(time.Now().Add(d)); err != nil { + return + } + b := make([]byte, 256) + var n int + n, err = c.Read(b) + t1 := time.Now() + if n != 0 || err == nil || !err.(Error).Timeout() { + err = fmt.Errorf("Read did not return (0, timeout): (%d, %v)", n, err) + return + } + if dt := t1.Sub(t0); min > dt || dt > max && !testing.Short() { + err = fmt.Errorf("Read took %s; expected %s", dt, d) + return + } +} + +func timeoutTransmitter(c Conn, d, min, max time.Duration, ch chan<- error) { + var err error + defer func() { ch <- err }() + + t0 := time.Now() + if err = c.SetWriteDeadline(time.Now().Add(d)); err != nil { + return + } + var n int + for { + n, err = c.Write([]byte("TIMEOUT TRANSMITTER")) + if err != nil { + break + } + } + t1 := time.Now() + if err == nil || !err.(Error).Timeout() { + err = fmt.Errorf("Write did not return (any, timeout): (%d, %v)", n, err) + return + } + if dt := t1.Sub(t0); min > dt || dt > max && !testing.Short() { + err = fmt.Errorf("Write took %s; expected %s", dt, d) + return + } +} + +func newLocalPacketListener(network string) (PacketConn, error) { + switch network { + case "udp": + if supportsIPv4() { + return ListenPacket("udp4", "127.0.0.1:0") + } + if supportsIPv6() { + return ListenPacket("udp6", "[::1]:0") + } + case "udp4": + if supportsIPv4() { + return ListenPacket("udp4", "127.0.0.1:0") + } + case "udp6": + if supportsIPv6() { + return ListenPacket("udp6", "[::1]:0") + } + case "unixgram": + return ListenPacket(network, testUnixAddr()) + } + return nil, fmt.Errorf("%s is not supported", network) +} + +func newDualStackPacketListener() (cs []*UDPConn, err error) { + var args = []struct { + network string + UDPAddr + }{ + {"udp4", UDPAddr{IP: IPv4(127, 0, 0, 1)}}, + {"udp6", UDPAddr{IP: IPv6loopback}}, + } + for i := 0; i < 64; i++ { + var port int + var cs []*UDPConn + for _, arg := range args { + arg.UDPAddr.Port = port + c, err := ListenUDP(arg.network, &arg.UDPAddr) + if err != nil { + continue + } + port = c.LocalAddr().(*UDPAddr).Port + cs = append(cs, c) + } + if len(cs) != len(args) { + for _, c := range cs { + c.Close() + } + continue + } + return cs, nil + } + return nil, errors.New("no dualstack port available") +} + +type localPacketServer struct { + pcmu sync.RWMutex + PacketConn + done chan bool // signal that indicates server stopped +} + +func (ls *localPacketServer) buildup(handler func(*localPacketServer, PacketConn)) error { + go func() { + handler(ls, ls.PacketConn) + close(ls.done) + }() + return nil +} + +func (ls *localPacketServer) teardown() error { + ls.pcmu.Lock() + if ls.PacketConn != nil { + network := ls.PacketConn.LocalAddr().Network() + address := ls.PacketConn.LocalAddr().String() + ls.PacketConn.Close() + <-ls.done + ls.PacketConn = nil + switch network { + case "unixgram": + os.Remove(address) + } + } + ls.pcmu.Unlock() + return nil +} + +func newLocalPacketServer(network string) (*localPacketServer, error) { + c, err := newLocalPacketListener(network) + if err != nil { + return nil, err + } + return &localPacketServer{PacketConn: c, done: make(chan bool)}, nil +} + +type packetListener struct { + PacketConn +} + +func (pl *packetListener) newLocalServer() (*localPacketServer, error) { + return &localPacketServer{PacketConn: pl.PacketConn, done: make(chan bool)}, nil +} + +func packetTransponder(c PacketConn, ch chan<- error) { + defer close(ch) + + c.SetDeadline(time.Now().Add(someTimeout)) + c.SetReadDeadline(time.Now().Add(someTimeout)) + c.SetWriteDeadline(time.Now().Add(someTimeout)) + + b := make([]byte, 256) + n, peer, err := c.ReadFrom(b) + if err != nil { + if perr := parseReadError(err); perr != nil { + ch <- perr + } + ch <- err + return + } + if peer == nil { // for connected-mode sockets + switch c.LocalAddr().Network() { + case "udp": + peer, err = ResolveUDPAddr("udp", string(b[:n])) + case "unixgram": + peer, err = ResolveUnixAddr("unixgram", string(b[:n])) + } + if err != nil { + ch <- err + return + } + } + if _, err := c.WriteTo(b[:n], peer); err != nil { + if perr := parseWriteError(err); perr != nil { + ch <- perr + } + ch <- err + return + } +} + +func packetTransceiver(c PacketConn, wb []byte, dst Addr, ch chan<- error) { + defer close(ch) + + c.SetDeadline(time.Now().Add(someTimeout)) + c.SetReadDeadline(time.Now().Add(someTimeout)) + c.SetWriteDeadline(time.Now().Add(someTimeout)) + + n, err := c.WriteTo(wb, dst) + if err != nil { + if perr := parseWriteError(err); perr != nil { + ch <- perr + } + ch <- err + return + } + if n != len(wb) { + ch <- fmt.Errorf("wrote %d; want %d", n, len(wb)) + } + rb := make([]byte, len(wb)) + n, _, err = c.ReadFrom(rb) + if err != nil { + if perr := parseReadError(err); perr != nil { + ch <- perr + } + ch <- err + return + } + if n != len(wb) { + ch <- fmt.Errorf("read %d; want %d", n, len(wb)) + } +} + +func timeoutPacketReceiver(c PacketConn, d, min, max time.Duration, ch chan<- error) { + var err error + defer func() { ch <- err }() + + t0 := time.Now() + if err = c.SetReadDeadline(time.Now().Add(d)); err != nil { + return + } + b := make([]byte, 256) + var n int + n, _, err = c.ReadFrom(b) + t1 := time.Now() + if n != 0 || err == nil || !err.(Error).Timeout() { + err = fmt.Errorf("ReadFrom did not return (0, timeout): (%d, %v)", n, err) + return + } + if dt := t1.Sub(t0); min > dt || dt > max && !testing.Short() { + err = fmt.Errorf("ReadFrom took %s; expected %s", dt, d) + return + } +} |