|  | // 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. | 
|  |  | 
|  | package net | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "os" | 
|  | "testing" | 
|  | ) | 
|  |  | 
|  | var tcpServerTests = []struct { | 
|  | snet, saddr string // server endpoint | 
|  | tnet, taddr string // target endpoint for client | 
|  | }{ | 
|  | {snet: "tcp", saddr: ":0", tnet: "tcp", taddr: "127.0.0.1"}, | 
|  | {snet: "tcp", saddr: "0.0.0.0:0", tnet: "tcp", taddr: "127.0.0.1"}, | 
|  | {snet: "tcp", saddr: "[::ffff:0.0.0.0]:0", tnet: "tcp", taddr: "127.0.0.1"}, | 
|  | {snet: "tcp", saddr: "[::]:0", tnet: "tcp", taddr: "::1"}, | 
|  |  | 
|  | {snet: "tcp", saddr: ":0", tnet: "tcp", taddr: "::1"}, | 
|  | {snet: "tcp", saddr: "0.0.0.0:0", tnet: "tcp", taddr: "::1"}, | 
|  | {snet: "tcp", saddr: "[::ffff:0.0.0.0]:0", tnet: "tcp", taddr: "::1"}, | 
|  | {snet: "tcp", saddr: "[::]:0", tnet: "tcp", taddr: "127.0.0.1"}, | 
|  |  | 
|  | {snet: "tcp", saddr: ":0", tnet: "tcp4", taddr: "127.0.0.1"}, | 
|  | {snet: "tcp", saddr: "0.0.0.0:0", tnet: "tcp4", taddr: "127.0.0.1"}, | 
|  | {snet: "tcp", saddr: "[::ffff:0.0.0.0]:0", tnet: "tcp4", taddr: "127.0.0.1"}, | 
|  | {snet: "tcp", saddr: "[::]:0", tnet: "tcp6", taddr: "::1"}, | 
|  |  | 
|  | {snet: "tcp", saddr: ":0", tnet: "tcp6", taddr: "::1"}, | 
|  | {snet: "tcp", saddr: "0.0.0.0:0", tnet: "tcp6", taddr: "::1"}, | 
|  | {snet: "tcp", saddr: "[::ffff:0.0.0.0]:0", tnet: "tcp6", taddr: "::1"}, | 
|  | {snet: "tcp", saddr: "[::]:0", tnet: "tcp4", taddr: "127.0.0.1"}, | 
|  |  | 
|  | {snet: "tcp", saddr: "127.0.0.1:0", tnet: "tcp", taddr: "127.0.0.1"}, | 
|  | {snet: "tcp", saddr: "[::ffff:127.0.0.1]:0", tnet: "tcp", taddr: "127.0.0.1"}, | 
|  | {snet: "tcp", saddr: "[::1]:0", tnet: "tcp", taddr: "::1"}, | 
|  |  | 
|  | {snet: "tcp4", saddr: ":0", tnet: "tcp4", taddr: "127.0.0.1"}, | 
|  | {snet: "tcp4", saddr: "0.0.0.0:0", tnet: "tcp4", taddr: "127.0.0.1"}, | 
|  | {snet: "tcp4", saddr: "[::ffff:0.0.0.0]:0", tnet: "tcp4", taddr: "127.0.0.1"}, | 
|  |  | 
|  | {snet: "tcp4", saddr: "127.0.0.1:0", tnet: "tcp4", taddr: "127.0.0.1"}, | 
|  |  | 
|  | {snet: "tcp6", saddr: ":0", tnet: "tcp6", taddr: "::1"}, | 
|  | {snet: "tcp6", saddr: "[::]:0", tnet: "tcp6", taddr: "::1"}, | 
|  |  | 
|  | {snet: "tcp6", saddr: "[::1]:0", tnet: "tcp6", taddr: "::1"}, | 
|  | } | 
|  |  | 
|  | // TestTCPServer tests concurrent accept-read-write servers. | 
|  | func TestTCPServer(t *testing.T) { | 
|  | const N = 3 | 
|  |  | 
|  | for i, tt := range tcpServerTests { | 
|  | t.Run(tt.snet+" "+tt.saddr+"<-"+tt.taddr, func(t *testing.T) { | 
|  | if !testableListenArgs(tt.snet, tt.saddr, tt.taddr) { | 
|  | t.Skip("not testable") | 
|  | } | 
|  |  | 
|  | ln, err := Listen(tt.snet, tt.saddr) | 
|  | if err != nil { | 
|  | if perr := parseDialError(err); perr != nil { | 
|  | t.Error(perr) | 
|  | } | 
|  | t.Fatal(err) | 
|  | } | 
|  |  | 
|  | var lss []*localServer | 
|  | var tpchs []chan error | 
|  | defer func() { | 
|  | for _, ls := range lss { | 
|  | ls.teardown() | 
|  | } | 
|  | }() | 
|  | for i := 0; i < N; i++ { | 
|  | ls := (&streamListener{Listener: ln}).newLocalServer() | 
|  | lss = append(lss, ls) | 
|  | tpchs = append(tpchs, make(chan error, 1)) | 
|  | } | 
|  | for i := 0; i < N; i++ { | 
|  | ch := tpchs[i] | 
|  | handler := func(ls *localServer, ln Listener) { ls.transponder(ln, ch) } | 
|  | if err := lss[i].buildup(handler); err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | } | 
|  |  | 
|  | var trchs []chan error | 
|  | for i := 0; i < N; i++ { | 
|  | _, port, err := SplitHostPort(lss[i].Listener.Addr().String()) | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | d := Dialer{Timeout: someTimeout} | 
|  | c, err := d.Dial(tt.tnet, JoinHostPort(tt.taddr, port)) | 
|  | if err != nil { | 
|  | if perr := parseDialError(err); perr != nil { | 
|  | t.Error(perr) | 
|  | } | 
|  | t.Fatal(err) | 
|  | } | 
|  | defer c.Close() | 
|  | trchs = append(trchs, make(chan error, 1)) | 
|  | go transceiver(c, []byte("TCP SERVER TEST"), trchs[i]) | 
|  | } | 
|  |  | 
|  | for _, ch := range trchs { | 
|  | for err := range ch { | 
|  | t.Errorf("#%d: %v", i, err) | 
|  | } | 
|  | } | 
|  | for _, ch := range tpchs { | 
|  | for err := range ch { | 
|  | t.Errorf("#%d: %v", i, err) | 
|  | } | 
|  | } | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | // TestUnixAndUnixpacketServer tests concurrent accept-read-write | 
|  | // servers | 
|  | func TestUnixAndUnixpacketServer(t *testing.T) { | 
|  | var unixAndUnixpacketServerTests = []struct { | 
|  | network, address string | 
|  | }{ | 
|  | {"unix", testUnixAddr(t)}, | 
|  | {"unix", "@nettest/go/unix"}, | 
|  |  | 
|  | {"unixpacket", testUnixAddr(t)}, | 
|  | {"unixpacket", "@nettest/go/unixpacket"}, | 
|  | } | 
|  |  | 
|  | const N = 3 | 
|  |  | 
|  | for i, tt := range unixAndUnixpacketServerTests { | 
|  | if !testableListenArgs(tt.network, tt.address, "") { | 
|  | t.Logf("skipping %s test", tt.network+" "+tt.address) | 
|  | continue | 
|  | } | 
|  |  | 
|  | ln, err := Listen(tt.network, tt.address) | 
|  | if err != nil { | 
|  | if perr := parseDialError(err); perr != nil { | 
|  | t.Error(perr) | 
|  | } | 
|  | t.Fatal(err) | 
|  | } | 
|  |  | 
|  | var lss []*localServer | 
|  | var tpchs []chan error | 
|  | defer func() { | 
|  | for _, ls := range lss { | 
|  | ls.teardown() | 
|  | } | 
|  | }() | 
|  | for i := 0; i < N; i++ { | 
|  | ls := (&streamListener{Listener: ln}).newLocalServer() | 
|  | lss = append(lss, ls) | 
|  | tpchs = append(tpchs, make(chan error, 1)) | 
|  | } | 
|  | for i := 0; i < N; i++ { | 
|  | ch := tpchs[i] | 
|  | handler := func(ls *localServer, ln Listener) { ls.transponder(ln, ch) } | 
|  | if err := lss[i].buildup(handler); err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | } | 
|  |  | 
|  | var trchs []chan error | 
|  | for i := 0; i < N; i++ { | 
|  | d := Dialer{Timeout: someTimeout} | 
|  | c, err := d.Dial(lss[i].Listener.Addr().Network(), lss[i].Listener.Addr().String()) | 
|  | if err != nil { | 
|  | if perr := parseDialError(err); perr != nil { | 
|  | t.Error(perr) | 
|  | } | 
|  | t.Fatal(err) | 
|  | } | 
|  |  | 
|  | if addr := c.LocalAddr(); addr != nil { | 
|  | t.Logf("connected %s->%s", addr, lss[i].Listener.Addr()) | 
|  | } | 
|  |  | 
|  | defer c.Close() | 
|  | trchs = append(trchs, make(chan error, 1)) | 
|  | go transceiver(c, []byte("UNIX AND UNIXPACKET SERVER TEST"), trchs[i]) | 
|  | } | 
|  |  | 
|  | for _, ch := range trchs { | 
|  | for err := range ch { | 
|  | t.Errorf("#%d: %v", i, err) | 
|  | } | 
|  | } | 
|  | for _, ch := range tpchs { | 
|  | for err := range ch { | 
|  | t.Errorf("#%d: %v", i, err) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | var udpServerTests = []struct { | 
|  | snet, saddr string // server endpoint | 
|  | tnet, taddr string // target endpoint for client | 
|  | dial        bool   // test with Dial | 
|  | }{ | 
|  | {snet: "udp", saddr: ":0", tnet: "udp", taddr: "127.0.0.1"}, | 
|  | {snet: "udp", saddr: "0.0.0.0:0", tnet: "udp", taddr: "127.0.0.1"}, | 
|  | {snet: "udp", saddr: "[::ffff:0.0.0.0]:0", tnet: "udp", taddr: "127.0.0.1"}, | 
|  | {snet: "udp", saddr: "[::]:0", tnet: "udp", taddr: "::1"}, | 
|  |  | 
|  | {snet: "udp", saddr: ":0", tnet: "udp", taddr: "::1"}, | 
|  | {snet: "udp", saddr: "0.0.0.0:0", tnet: "udp", taddr: "::1"}, | 
|  | {snet: "udp", saddr: "[::ffff:0.0.0.0]:0", tnet: "udp", taddr: "::1"}, | 
|  | {snet: "udp", saddr: "[::]:0", tnet: "udp", taddr: "127.0.0.1"}, | 
|  |  | 
|  | {snet: "udp", saddr: ":0", tnet: "udp4", taddr: "127.0.0.1"}, | 
|  | {snet: "udp", saddr: "0.0.0.0:0", tnet: "udp4", taddr: "127.0.0.1"}, | 
|  | {snet: "udp", saddr: "[::ffff:0.0.0.0]:0", tnet: "udp4", taddr: "127.0.0.1"}, | 
|  | {snet: "udp", saddr: "[::]:0", tnet: "udp6", taddr: "::1"}, | 
|  |  | 
|  | {snet: "udp", saddr: ":0", tnet: "udp6", taddr: "::1"}, | 
|  | {snet: "udp", saddr: "0.0.0.0:0", tnet: "udp6", taddr: "::1"}, | 
|  | {snet: "udp", saddr: "[::ffff:0.0.0.0]:0", tnet: "udp6", taddr: "::1"}, | 
|  | {snet: "udp", saddr: "[::]:0", tnet: "udp4", taddr: "127.0.0.1"}, | 
|  |  | 
|  | {snet: "udp", saddr: "127.0.0.1:0", tnet: "udp", taddr: "127.0.0.1"}, | 
|  | {snet: "udp", saddr: "[::ffff:127.0.0.1]:0", tnet: "udp", taddr: "127.0.0.1"}, | 
|  | {snet: "udp", saddr: "[::1]:0", tnet: "udp", taddr: "::1"}, | 
|  |  | 
|  | {snet: "udp4", saddr: ":0", tnet: "udp4", taddr: "127.0.0.1"}, | 
|  | {snet: "udp4", saddr: "0.0.0.0:0", tnet: "udp4", taddr: "127.0.0.1"}, | 
|  | {snet: "udp4", saddr: "[::ffff:0.0.0.0]:0", tnet: "udp4", taddr: "127.0.0.1"}, | 
|  |  | 
|  | {snet: "udp4", saddr: "127.0.0.1:0", tnet: "udp4", taddr: "127.0.0.1"}, | 
|  |  | 
|  | {snet: "udp6", saddr: ":0", tnet: "udp6", taddr: "::1"}, | 
|  | {snet: "udp6", saddr: "[::]:0", tnet: "udp6", taddr: "::1"}, | 
|  |  | 
|  | {snet: "udp6", saddr: "[::1]:0", tnet: "udp6", taddr: "::1"}, | 
|  |  | 
|  | {snet: "udp", saddr: "127.0.0.1:0", tnet: "udp", taddr: "127.0.0.1", dial: true}, | 
|  |  | 
|  | {snet: "udp", saddr: "[::1]:0", tnet: "udp", taddr: "::1", dial: true}, | 
|  | } | 
|  |  | 
|  | func TestUDPServer(t *testing.T) { | 
|  | for i, tt := range udpServerTests { | 
|  | i, tt := i, tt | 
|  | t.Run(fmt.Sprint(i), func(t *testing.T) { | 
|  | if !testableListenArgs(tt.snet, tt.saddr, tt.taddr) { | 
|  | t.Skipf("skipping %s %s<-%s test", tt.snet, tt.saddr, tt.taddr) | 
|  | } | 
|  | t.Logf("%s %s<-%s", tt.snet, tt.saddr, tt.taddr) | 
|  |  | 
|  | c1, err := ListenPacket(tt.snet, tt.saddr) | 
|  | if err != nil { | 
|  | if perr := parseDialError(err); perr != nil { | 
|  | t.Error(perr) | 
|  | } | 
|  | t.Fatal(err) | 
|  | } | 
|  |  | 
|  | ls := (&packetListener{PacketConn: c1}).newLocalServer() | 
|  | defer ls.teardown() | 
|  | tpch := make(chan error, 1) | 
|  | handler := func(ls *localPacketServer, c PacketConn) { packetTransponder(c, tpch) } | 
|  | if err := ls.buildup(handler); err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  |  | 
|  | trch := make(chan error, 1) | 
|  | _, port, err := SplitHostPort(ls.PacketConn.LocalAddr().String()) | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | if tt.dial { | 
|  | d := Dialer{Timeout: someTimeout} | 
|  | c2, err := d.Dial(tt.tnet, JoinHostPort(tt.taddr, port)) | 
|  | if err != nil { | 
|  | if perr := parseDialError(err); perr != nil { | 
|  | t.Error(perr) | 
|  | } | 
|  | t.Fatal(err) | 
|  | } | 
|  | defer c2.Close() | 
|  | go transceiver(c2, []byte("UDP SERVER TEST"), trch) | 
|  | } else { | 
|  | c2, err := ListenPacket(tt.tnet, JoinHostPort(tt.taddr, "0")) | 
|  | if err != nil { | 
|  | if perr := parseDialError(err); perr != nil { | 
|  | t.Error(perr) | 
|  | } | 
|  | t.Fatal(err) | 
|  | } | 
|  | defer c2.Close() | 
|  | dst, err := ResolveUDPAddr(tt.tnet, JoinHostPort(tt.taddr, port)) | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | go packetTransceiver(c2, []byte("UDP SERVER TEST"), dst, trch) | 
|  | } | 
|  |  | 
|  | for trch != nil || tpch != nil { | 
|  | select { | 
|  | case err, ok := <-trch: | 
|  | if !ok { | 
|  | trch = nil | 
|  | } | 
|  | if err != nil { | 
|  | t.Errorf("client: %v", err) | 
|  | } | 
|  | case err, ok := <-tpch: | 
|  | if !ok { | 
|  | tpch = nil | 
|  | } | 
|  | if err != nil { | 
|  | t.Errorf("server: %v", err) | 
|  | } | 
|  | } | 
|  | } | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestUnixgramServer(t *testing.T) { | 
|  | var unixgramServerTests = []struct { | 
|  | saddr string // server endpoint | 
|  | caddr string // client endpoint | 
|  | dial  bool   // test with Dial | 
|  | }{ | 
|  | {saddr: testUnixAddr(t), caddr: testUnixAddr(t)}, | 
|  | {saddr: testUnixAddr(t), caddr: testUnixAddr(t), dial: true}, | 
|  |  | 
|  | {saddr: "@nettest/go/unixgram/server", caddr: "@nettest/go/unixgram/client"}, | 
|  | } | 
|  |  | 
|  | for i, tt := range unixgramServerTests { | 
|  | i, tt := i, tt | 
|  | t.Run(fmt.Sprint(i), func(t *testing.T) { | 
|  | if !testableListenArgs("unixgram", tt.saddr, "") { | 
|  | t.Skipf("skipping unixgram %s<-%s test", tt.saddr, tt.caddr) | 
|  | } | 
|  | t.Logf("unixgram %s<-%s", tt.saddr, tt.caddr) | 
|  |  | 
|  | c1, err := ListenPacket("unixgram", tt.saddr) | 
|  | if err != nil { | 
|  | if perr := parseDialError(err); perr != nil { | 
|  | t.Error(perr) | 
|  | } | 
|  | t.Fatal(err) | 
|  | } | 
|  |  | 
|  | ls := (&packetListener{PacketConn: c1}).newLocalServer() | 
|  | defer ls.teardown() | 
|  | tpch := make(chan error, 1) | 
|  | handler := func(ls *localPacketServer, c PacketConn) { packetTransponder(c, tpch) } | 
|  | if err := ls.buildup(handler); err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  |  | 
|  | trch := make(chan error, 1) | 
|  | if tt.dial { | 
|  | d := Dialer{Timeout: someTimeout, LocalAddr: &UnixAddr{Net: "unixgram", Name: tt.caddr}} | 
|  | c2, err := d.Dial("unixgram", ls.PacketConn.LocalAddr().String()) | 
|  | if err != nil { | 
|  | if perr := parseDialError(err); perr != nil { | 
|  | t.Error(perr) | 
|  | } | 
|  | t.Fatal(err) | 
|  | } | 
|  | defer os.Remove(c2.LocalAddr().String()) | 
|  | defer c2.Close() | 
|  | go transceiver(c2, []byte(c2.LocalAddr().String()), trch) | 
|  | } else { | 
|  | c2, err := ListenPacket("unixgram", tt.caddr) | 
|  | if err != nil { | 
|  | if perr := parseDialError(err); perr != nil { | 
|  | t.Error(perr) | 
|  | } | 
|  | t.Fatal(err) | 
|  | } | 
|  | defer os.Remove(c2.LocalAddr().String()) | 
|  | defer c2.Close() | 
|  | go packetTransceiver(c2, []byte("UNIXGRAM SERVER TEST"), ls.PacketConn.LocalAddr(), trch) | 
|  | } | 
|  |  | 
|  | for trch != nil || tpch != nil { | 
|  | select { | 
|  | case err, ok := <-trch: | 
|  | if !ok { | 
|  | trch = nil | 
|  | } | 
|  | if err != nil { | 
|  | t.Errorf("client: %v", err) | 
|  | } | 
|  | case err, ok := <-tpch: | 
|  | if !ok { | 
|  | tpch = nil | 
|  | } | 
|  | if err != nil { | 
|  | t.Errorf("server: %v", err) | 
|  | } | 
|  | } | 
|  | } | 
|  | }) | 
|  | } | 
|  | } |