net: add helpers for server testing

Also moves a few server test helpers into mockserver_test.go.

Change-Id: I5a95c9bc6f0c4683751bcca77e26a8586a377466
Reviewed-on: https://go-review.googlesource.com/9106
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/src/net/mockserver_test.go b/src/net/mockserver_test.go
index 3a21452..07ffd63 100644
--- a/src/net/mockserver_test.go
+++ b/src/net/mockserver_test.go
@@ -6,10 +6,26 @@
 
 import (
 	"fmt"
+	"io/ioutil"
 	"os"
 	"sync"
+	"time"
 )
 
+// testUnixAddr uses ioutil.TempFile to get a name that is unique.
+// It also uses /tmp directory in case it is prohibited to create UNIX
+// sockets in TMPDIR.
+func testUnixAddr() string {
+	f, err := ioutil.TempFile("", "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", "tcp4", "tcp6":
@@ -155,3 +171,212 @@
 	}
 	return dss, nil
 }
+
+func 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
+	}
+	defer c.Close()
+
+	network := ln.Addr().Network()
+	if c.LocalAddr().Network() != network || c.LocalAddr().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 newLocalPacketListener(network string) (PacketConn, error) {
+	switch network {
+	case "udp", "udp4", "udp6":
+		if supportsIPv4 {
+			return ListenPacket("udp4", "127.0.0.1:0")
+		}
+		if supportsIPv6 {
+			return ListenPacket("udp6", "[::1]:0")
+		}
+	case "unixgram":
+		return ListenPacket(network, testUnixAddr())
+	}
+	return nil, fmt.Errorf("%s is not supported", network)
+}
+
+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))
+	}
+}