From 4a7da91853f6e4f9b519d9ac193daef69940a73f Mon Sep 17 00:00:00 2001 From: Max Maeder Date: Fri, 29 Nov 2024 13:12:48 -0600 Subject: [PATCH 1/2] Rework MockServer Accept, update tests --- codec/websocket/stream_test.go | 123 +++++++++++++++++++++------------ codec/websocket/test_main.go | 22 ++++-- 2 files changed, 95 insertions(+), 50 deletions(-) diff --git a/codec/websocket/stream_test.go b/codec/websocket/stream_test.go index 09d22c7..0a0a4fb 100644 --- a/codec/websocket/stream_test.go +++ b/codec/websocket/stream_test.go @@ -23,11 +23,15 @@ func assertState(t *testing.T, ws *Stream, expected StreamState) { func TestClientServerSendsInvalidCloseCode(t *testing.T) { assert := assert.New(t) + portChan := make(chan int, 1) + go func() { - srv := &MockServer{} + srv := &MockServer{ + portChan: portChan, + } defer srv.Close() - err := srv.Accept("localhost:8080") + err := srv.Accept("localhost:0") if err != nil { panic(err) } @@ -59,7 +63,8 @@ func TestClientServerSendsInvalidCloseCode(t *testing.T) { assert.Equal(reason, "") } }() - time.Sleep(10 * time.Millisecond) + + wsURI := fmt.Sprintf("ws://localhost:%d", <-portChan) ioc := sonic.MustIO() defer ioc.Close() @@ -70,7 +75,7 @@ func TestClientServerSendsInvalidCloseCode(t *testing.T) { } done := false - ws.AsyncHandshake("ws://localhost:8080", func(err error) { + ws.AsyncHandshake(wsURI, func(err error) { if err != nil { t.Fatal(err) } @@ -90,11 +95,15 @@ func TestClientServerSendsInvalidCloseCode(t *testing.T) { func TestClientEchoCloseCode(t *testing.T) { assert := assert.New(t) + portChan := make(chan int, 1) + go func() { - srv := &MockServer{} + srv := &MockServer{ + portChan: portChan, + } defer srv.Close() - err := srv.Accept("localhost:8080") + err := srv.Accept("localhost:0") if err != nil { panic(err) } @@ -122,7 +131,8 @@ func TestClientEchoCloseCode(t *testing.T) { assert.Equal(reason, "something") } }() - time.Sleep(10 * time.Millisecond) + + wsURI := fmt.Sprintf("ws://localhost:%d", <-portChan) ioc := sonic.MustIO() defer ioc.Close() @@ -133,7 +143,7 @@ func TestClientEchoCloseCode(t *testing.T) { } done := false - ws.AsyncHandshake("ws://localhost:8080", func(err error) { + ws.AsyncHandshake(wsURI, func(err error) { if err != nil { t.Fatal(err) } @@ -155,11 +165,15 @@ func TestClientSendPingWithInvalidPayload(t *testing.T) { // the connection immediately with 1002/Protocol Error. assert := assert.New(t) + portChan := make(chan int, 1) + go func() { - srv := &MockServer{} + srv := &MockServer{ + portChan: portChan, + } defer srv.Close() - err := srv.Accept("localhost:8080") + err := srv.Accept("localhost:0") if err != nil { panic(err) } @@ -191,7 +205,8 @@ func TestClientSendPingWithInvalidPayload(t *testing.T) { assert.Empty(reason) } }() - time.Sleep(10 * time.Millisecond) + + wsURI := fmt.Sprintf("ws://localhost:%d", <-portChan) ioc := sonic.MustIO() defer ioc.Close() @@ -202,7 +217,7 @@ func TestClientSendPingWithInvalidPayload(t *testing.T) { } done := false - ws.AsyncHandshake("ws://localhost:8080", func(err error) { + ws.AsyncHandshake(wsURI, func(err error) { if err != nil { t.Fatal(err) } @@ -223,11 +238,15 @@ func TestClientSendPingWithInvalidPayload(t *testing.T) { func TestClientSendMessageWithPayload126(t *testing.T) { assert := assert.New(t) + portChan := make(chan int, 1) + go func() { - srv := &MockServer{} + srv := &MockServer{ + portChan: portChan, + } defer srv.Close() - err := srv.Accept("localhost:8080") + err := srv.Accept("localhost:0") if err != nil { panic(err) } @@ -242,7 +261,8 @@ func TestClientSendMessageWithPayload126(t *testing.T) { frame.WriteTo(srv.conn) }() - time.Sleep(10 * time.Millisecond) + + wsURI := fmt.Sprintf("ws://localhost:%d", <-portChan) ioc := sonic.MustIO() defer ioc.Close() @@ -253,7 +273,7 @@ func TestClientSendMessageWithPayload126(t *testing.T) { } done := false - ws.AsyncHandshake("ws://localhost:8080", func(err error) { + ws.AsyncHandshake(wsURI, func(err error) { if err != nil { t.Fatal(err) } @@ -277,11 +297,15 @@ func TestClientSendMessageWithPayload126(t *testing.T) { func TestClientSendMessageWithPayload127(t *testing.T) { assert := assert.New(t) + portChan := make(chan int, 1) + go func() { - srv := &MockServer{} + srv := &MockServer{ + portChan: portChan, + } defer srv.Close() - err := srv.Accept("localhost:8080") + err := srv.Accept("localhost:0") if err != nil { panic(err) } @@ -296,7 +320,8 @@ func TestClientSendMessageWithPayload127(t *testing.T) { frame.WriteTo(srv.conn) }() - time.Sleep(10 * time.Millisecond) + + wsURI := fmt.Sprintf("ws://localhost:%d", <-portChan) ioc := sonic.MustIO() defer ioc.Close() @@ -307,7 +332,7 @@ func TestClientSendMessageWithPayload127(t *testing.T) { } done := false - ws.AsyncHandshake("ws://localhost:8080", func(err error) { + ws.AsyncHandshake(wsURI, func(err error) { if err != nil { t.Fatal(err) } @@ -329,11 +354,19 @@ func TestClientSendMessageWithPayload127(t *testing.T) { } func TestClientReconnectOnFailedRead(t *testing.T) { + port, err := GetFreePort() + if err != nil { + panic(err) + } + + serverURI := fmt.Sprintf("localhost:%d", port) + wsURI := fmt.Sprintf("ws://localhost:%d", port) + go func() { for i := 0; i < 10; i++ { srv := &MockServer{} - err := srv.Accept("localhost:8080") + err := srv.Accept(serverURI) if err != nil { panic(err) } @@ -389,7 +422,7 @@ func TestClientReconnectOnFailedRead(t *testing.T) { } connect = func() { - ws.AsyncHandshake("ws://localhost:8080", onHandshake) + ws.AsyncHandshake(wsURI, onHandshake) } connect() @@ -465,17 +498,20 @@ func TestClientFailedHandshakeNoServer(t *testing.T) { } func TestClientSuccessfulHandshake(t *testing.T) { - srv := &MockServer{} + srv := &MockServer{ + portChan: make(chan int, 1), + } go func() { defer srv.Close() - err := srv.Accept("localhost:8080") + err := srv.Accept("localhost:0") if err != nil { panic(err) } }() - time.Sleep(10 * time.Millisecond) + + wsURI := fmt.Sprintf("ws://localhost:%d", <-srv.portChan) ioc := sonic.MustIO() defer ioc.Close() @@ -501,7 +537,7 @@ func TestClientSuccessfulHandshake(t *testing.T) { assertState(t, ws, StateHandshake) - ws.AsyncHandshake("ws://localhost:8080", func(err error) { + ws.AsyncHandshake(wsURI, func(err error) { if err != nil { assertState(t, ws, StateTerminated) } else { @@ -524,17 +560,20 @@ func TestClientSuccessfulHandshake(t *testing.T) { } func TestClientSuccessfulHandshakeWithExtraHeaders(t *testing.T) { - srv := &MockServer{} + srv := &MockServer{ + portChan: make(chan int, 1), + } go func() { defer srv.Close() - err := srv.Accept("localhost:8080") + err := srv.Accept("localhost:0") if err != nil { panic(err) } }() - time.Sleep(10 * time.Millisecond) + + wsURI := fmt.Sprintf("ws://localhost:%d", <-srv.portChan) ioc := sonic.MustIO() defer ioc.Close() @@ -558,7 +597,7 @@ func TestClientSuccessfulHandshakeWithExtraHeaders(t *testing.T) { } ws.AsyncHandshake( - "ws://localhost:8080", + wsURI, func(err error) { if err != nil { assertState(t, ws, StateTerminated) @@ -1647,15 +1686,12 @@ func TestClientAbnormalClose(t *testing.T) { portChan := make(chan int, 1) go func() { - srv := &MockServer{} + srv := &MockServer{ + portChan: portChan, + } defer srv.Close() - err := srv.Accept("localhost:0", func(port int) { - if port <= 0 { - panic(fmt.Sprintf("Got invalid port from MockServer: %d", port)) - } - portChan <- port - }) + err := srv.Accept("localhost:0") if err != nil { panic(err) } @@ -1663,7 +1699,6 @@ func TestClientAbnormalClose(t *testing.T) { // Simulate an abnormal closure (close the TCP connection without sending a WebSocket close frame) srv.Close() }() - time.Sleep(10 * time.Millisecond) wsURI := fmt.Sprintf("ws://localhost:%d", <-portChan) @@ -1696,15 +1731,12 @@ func TestClientAsyncAbnormalClose(t *testing.T) { portChan := make(chan int, 1) go func() { - srv := &MockServer{} + srv := &MockServer{ + portChan: portChan, + } defer srv.Close() - err := srv.Accept("localhost:0", func(port int) { - if port <= 0 { - panic(fmt.Sprintf("Got invalid port from MockServer: %d", port)) - } - portChan <- port - }) + err := srv.Accept("localhost:0") if err != nil { panic(err) } @@ -1712,7 +1744,6 @@ func TestClientAsyncAbnormalClose(t *testing.T) { // Simulate an abnormal closure (close the TCP connection without sending a WebSocket close frame) srv.Close() }() - time.Sleep(10 * time.Millisecond) wsURI := fmt.Sprintf("ws://localhost:%d", <-portChan) diff --git a/codec/websocket/test_main.go b/codec/websocket/test_main.go index 368d841..716f7c6 100644 --- a/codec/websocket/test_main.go +++ b/codec/websocket/test_main.go @@ -18,13 +18,14 @@ type MockServer struct { conn net.Conn closed int32 port int32 + portChan chan int Upgrade *http.Request } // Accept starts the mock server, listening on the specified address. // If a callback is provided, it is invoked with the assigned port. -func (s *MockServer) Accept(addr string, opts ...func(int)) (err error) { +func (s *MockServer) Accept(addr string) (err error) { s.ln, err = net.Listen("tcp", addr) if err != nil { return err @@ -33,9 +34,8 @@ func (s *MockServer) Accept(addr string, opts ...func(int)) (err error) { port := int(s.ln.Addr().(*net.TCPAddr).Port) atomic.StoreInt32(&s.port, int32(port)) - // Call port callback if provided - if len(opts) > 0 && opts[0] != nil { - opts[0](port) + if s.portChan != nil { + s.portChan <- port } conn, err := s.ln.Accept() @@ -187,3 +187,17 @@ func (s *MockStream) Close() error { func (s *MockStream) RawFd() int { return -1 } + +// GetFreePort asks the kernel for a free open port that is ready to use. +// From: https://gist.github.com/sevkin/96bdae9274465b2d09191384f86ef39d +func GetFreePort() (port int, err error) { + var a *net.TCPAddr + if a, err = net.ResolveTCPAddr("tcp", "localhost:0"); err == nil { + var l *net.TCPListener + if l, err = net.ListenTCP("tcp", a); err == nil { + defer l.Close() + return l.Addr().(*net.TCPAddr).Port, nil + } + } + return +} From c500afe46354477948537a26744ef4425ab97c23 Mon Sep 17 00:00:00 2001 From: Max Maeder Date: Thu, 5 Dec 2024 19:31:04 -0600 Subject: [PATCH 2/2] Refactor port channel --- codec/websocket/client_performance_test.go | 2 +- codec/websocket/stream_test.go | 123 +++++++-------------- codec/websocket/test_main.go | 36 +++--- 3 files changed, 56 insertions(+), 105 deletions(-) diff --git a/codec/websocket/client_performance_test.go b/codec/websocket/client_performance_test.go index b1493c6..8341b8e 100644 --- a/codec/websocket/client_performance_test.go +++ b/codec/websocket/client_performance_test.go @@ -25,7 +25,7 @@ func TestClientReadWrite(t *testing.T) { } dur := time.Duration(idur) * time.Second - s := &MockServer{} + s := NewMockServer() go func() { defer s.Close() diff --git a/codec/websocket/stream_test.go b/codec/websocket/stream_test.go index 0a0a4fb..25a206b 100644 --- a/codec/websocket/stream_test.go +++ b/codec/websocket/stream_test.go @@ -8,7 +8,6 @@ import ( "io" "net/http" "testing" - "time" "github.com/stretchr/testify/assert" "github.com/talostrading/sonic" @@ -23,15 +22,12 @@ func assertState(t *testing.T, ws *Stream, expected StreamState) { func TestClientServerSendsInvalidCloseCode(t *testing.T) { assert := assert.New(t) - portChan := make(chan int, 1) + srv := NewMockServer() go func() { - srv := &MockServer{ - portChan: portChan, - } defer srv.Close() - err := srv.Accept("localhost:0") + err := srv.Accept(MockServerDynamicAddr) if err != nil { panic(err) } @@ -64,8 +60,6 @@ func TestClientServerSendsInvalidCloseCode(t *testing.T) { } }() - wsURI := fmt.Sprintf("ws://localhost:%d", <-portChan) - ioc := sonic.MustIO() defer ioc.Close() @@ -75,7 +69,7 @@ func TestClientServerSendsInvalidCloseCode(t *testing.T) { } done := false - ws.AsyncHandshake(wsURI, func(err error) { + ws.AsyncHandshake(fmt.Sprintf("ws://localhost:%d", <-srv.portChan), func(err error) { if err != nil { t.Fatal(err) } @@ -95,15 +89,12 @@ func TestClientServerSendsInvalidCloseCode(t *testing.T) { func TestClientEchoCloseCode(t *testing.T) { assert := assert.New(t) - portChan := make(chan int, 1) + srv := NewMockServer() go func() { - srv := &MockServer{ - portChan: portChan, - } defer srv.Close() - err := srv.Accept("localhost:0") + err := srv.Accept(MockServerDynamicAddr) if err != nil { panic(err) } @@ -131,8 +122,6 @@ func TestClientEchoCloseCode(t *testing.T) { assert.Equal(reason, "something") } }() - - wsURI := fmt.Sprintf("ws://localhost:%d", <-portChan) ioc := sonic.MustIO() defer ioc.Close() @@ -143,7 +132,7 @@ func TestClientEchoCloseCode(t *testing.T) { } done := false - ws.AsyncHandshake(wsURI, func(err error) { + ws.AsyncHandshake(fmt.Sprintf("ws://localhost:%d", <-srv.portChan), func(err error) { if err != nil { t.Fatal(err) } @@ -165,15 +154,12 @@ func TestClientSendPingWithInvalidPayload(t *testing.T) { // the connection immediately with 1002/Protocol Error. assert := assert.New(t) - portChan := make(chan int, 1) + srv := NewMockServer() go func() { - srv := &MockServer{ - portChan: portChan, - } defer srv.Close() - err := srv.Accept("localhost:0") + err := srv.Accept(MockServerDynamicAddr) if err != nil { panic(err) } @@ -206,8 +192,6 @@ func TestClientSendPingWithInvalidPayload(t *testing.T) { } }() - wsURI := fmt.Sprintf("ws://localhost:%d", <-portChan) - ioc := sonic.MustIO() defer ioc.Close() @@ -217,7 +201,7 @@ func TestClientSendPingWithInvalidPayload(t *testing.T) { } done := false - ws.AsyncHandshake(wsURI, func(err error) { + ws.AsyncHandshake(fmt.Sprintf("ws://localhost:%d", <-srv.portChan), func(err error) { if err != nil { t.Fatal(err) } @@ -238,15 +222,12 @@ func TestClientSendPingWithInvalidPayload(t *testing.T) { func TestClientSendMessageWithPayload126(t *testing.T) { assert := assert.New(t) - portChan := make(chan int, 1) + srv := NewMockServer() go func() { - srv := &MockServer{ - portChan: portChan, - } defer srv.Close() - err := srv.Accept("localhost:0") + err := srv.Accept(MockServerDynamicAddr) if err != nil { panic(err) } @@ -262,8 +243,6 @@ func TestClientSendMessageWithPayload126(t *testing.T) { frame.WriteTo(srv.conn) }() - wsURI := fmt.Sprintf("ws://localhost:%d", <-portChan) - ioc := sonic.MustIO() defer ioc.Close() @@ -273,7 +252,7 @@ func TestClientSendMessageWithPayload126(t *testing.T) { } done := false - ws.AsyncHandshake(wsURI, func(err error) { + ws.AsyncHandshake(fmt.Sprintf("ws://localhost:%d", <-srv.portChan), func(err error) { if err != nil { t.Fatal(err) } @@ -297,15 +276,12 @@ func TestClientSendMessageWithPayload126(t *testing.T) { func TestClientSendMessageWithPayload127(t *testing.T) { assert := assert.New(t) - portChan := make(chan int, 1) + srv := NewMockServer() go func() { - srv := &MockServer{ - portChan: portChan, - } defer srv.Close() - err := srv.Accept("localhost:0") + err := srv.Accept(MockServerDynamicAddr) if err != nil { panic(err) } @@ -321,8 +297,6 @@ func TestClientSendMessageWithPayload127(t *testing.T) { frame.WriteTo(srv.conn) }() - wsURI := fmt.Sprintf("ws://localhost:%d", <-portChan) - ioc := sonic.MustIO() defer ioc.Close() @@ -332,7 +306,7 @@ func TestClientSendMessageWithPayload127(t *testing.T) { } done := false - ws.AsyncHandshake(wsURI, func(err error) { + ws.AsyncHandshake(fmt.Sprintf("ws://localhost:%d", <-srv.portChan), func(err error) { if err != nil { t.Fatal(err) } @@ -354,28 +328,29 @@ func TestClientSendMessageWithPayload127(t *testing.T) { } func TestClientReconnectOnFailedRead(t *testing.T) { - port, err := GetFreePort() - if err != nil { - panic(err) - } - - serverURI := fmt.Sprintf("localhost:%d", port) - wsURI := fmt.Sprintf("ws://localhost:%d", port) + srv := NewMockServer() + port := 0 go func() { for i := 0; i < 10; i++ { - srv := &MockServer{} - - err := srv.Accept(serverURI) + var err error + if port != 0 { + err = srv.Accept(fmt.Sprintf("localhost:%d", port)) + } else { + err = srv.Accept(MockServerDynamicAddr) + } if err != nil { panic(err) } srv.Write([]byte("hello")) srv.Close() + + srv = NewMockServer() } }() - time.Sleep(10 * time.Millisecond) + + port = <- srv.portChan ioc := sonic.MustIO() defer ioc.Close() @@ -422,7 +397,7 @@ func TestClientReconnectOnFailedRead(t *testing.T) { } connect = func() { - ws.AsyncHandshake(wsURI, onHandshake) + ws.AsyncHandshake(fmt.Sprintf("ws://localhost:%d", port), onHandshake) } connect() @@ -498,21 +473,17 @@ func TestClientFailedHandshakeNoServer(t *testing.T) { } func TestClientSuccessfulHandshake(t *testing.T) { - srv := &MockServer{ - portChan: make(chan int, 1), - } + srv := NewMockServer() go func() { defer srv.Close() - err := srv.Accept("localhost:0") + err := srv.Accept(MockServerDynamicAddr) if err != nil { panic(err) } }() - wsURI := fmt.Sprintf("ws://localhost:%d", <-srv.portChan) - ioc := sonic.MustIO() defer ioc.Close() @@ -537,7 +508,7 @@ func TestClientSuccessfulHandshake(t *testing.T) { assertState(t, ws, StateHandshake) - ws.AsyncHandshake(wsURI, func(err error) { + ws.AsyncHandshake(fmt.Sprintf("ws://localhost:%d", <-srv.portChan), func(err error) { if err != nil { assertState(t, ws, StateTerminated) } else { @@ -560,21 +531,17 @@ func TestClientSuccessfulHandshake(t *testing.T) { } func TestClientSuccessfulHandshakeWithExtraHeaders(t *testing.T) { - srv := &MockServer{ - portChan: make(chan int, 1), - } + srv := NewMockServer() go func() { defer srv.Close() - err := srv.Accept("localhost:0") + err := srv.Accept(MockServerDynamicAddr) if err != nil { panic(err) } }() - wsURI := fmt.Sprintf("ws://localhost:%d", <-srv.portChan) - ioc := sonic.MustIO() defer ioc.Close() @@ -597,7 +564,7 @@ func TestClientSuccessfulHandshakeWithExtraHeaders(t *testing.T) { } ws.AsyncHandshake( - wsURI, + fmt.Sprintf("ws://localhost:%d", <-srv.portChan), func(err error) { if err != nil { assertState(t, ws, StateTerminated) @@ -1683,15 +1650,12 @@ func TestClientAsyncCloseHandshakePeerStarts(t *testing.T) { func TestClientAbnormalClose(t *testing.T) { assert := assert.New(t) - portChan := make(chan int, 1) + srv := NewMockServer() go func() { - srv := &MockServer{ - portChan: portChan, - } defer srv.Close() - err := srv.Accept("localhost:0") + err := srv.Accept(MockServerDynamicAddr) if err != nil { panic(err) } @@ -1700,15 +1664,13 @@ func TestClientAbnormalClose(t *testing.T) { srv.Close() }() - wsURI := fmt.Sprintf("ws://localhost:%d", <-portChan) - ioc := sonic.MustIO() defer ioc.Close() ws, err := NewWebsocketStream(ioc, nil, RoleClient) assert.Nil(err) - err = ws.Handshake(wsURI) + err = ws.Handshake(fmt.Sprintf("ws://localhost:%d", <-srv.portChan)) assert.Nil(err) assert.Equal(ws.State(), StateActive) // Verify WebSocket active @@ -1728,15 +1690,12 @@ func TestClientAbnormalClose(t *testing.T) { func TestClientAsyncAbnormalClose(t *testing.T) { assert := assert.New(t) - portChan := make(chan int, 1) + srv := NewMockServer() go func() { - srv := &MockServer{ - portChan: portChan, - } defer srv.Close() - err := srv.Accept("localhost:0") + err := srv.Accept(MockServerDynamicAddr) if err != nil { panic(err) } @@ -1745,8 +1704,6 @@ func TestClientAsyncAbnormalClose(t *testing.T) { srv.Close() }() - wsURI := fmt.Sprintf("ws://localhost:%d", <-portChan) - ioc := sonic.MustIO() defer ioc.Close() @@ -1754,7 +1711,7 @@ func TestClientAsyncAbnormalClose(t *testing.T) { assert.Nil(err) done := false - ws.AsyncHandshake(wsURI, func(err error) { + ws.AsyncHandshake(fmt.Sprintf("ws://localhost:%d", <-srv.portChan), func(err error) { assert.Nil(err) assert.Equal(ws.State(), StateActive) // Verify WebSocket active diff --git a/codec/websocket/test_main.go b/codec/websocket/test_main.go index 716f7c6..8d32b72 100644 --- a/codec/websocket/test_main.go +++ b/codec/websocket/test_main.go @@ -23,20 +23,28 @@ type MockServer struct { Upgrade *http.Request } -// Accept starts the mock server, listening on the specified address. -// If a callback is provided, it is invoked with the assigned port. +func NewMockServer() *MockServer { + return &MockServer{ + portChan: make(chan int, 1), // Buffer port channel to prevent blocking server + } +} + +// Accept starts the mock server on the specified address. +// If MockServerDynamicAddr is provided as the address, the server binds to any available port. +const MockServerDynamicAddr = "" func (s *MockServer) Accept(addr string) (err error) { - s.ln, err = net.Listen("tcp", addr) + if addr == MockServerDynamicAddr { + s.ln, err = net.Listen("tcp", "localhost:0") + } else { + s.ln, err = net.Listen("tcp", addr) + } if err != nil { return err } port := int(s.ln.Addr().(*net.TCPAddr).Port) atomic.StoreInt32(&s.port, int32(port)) - - if s.portChan != nil { - s.portChan <- port - } + s.portChan <- port conn, err := s.ln.Accept() if err != nil { @@ -187,17 +195,3 @@ func (s *MockStream) Close() error { func (s *MockStream) RawFd() int { return -1 } - -// GetFreePort asks the kernel for a free open port that is ready to use. -// From: https://gist.github.com/sevkin/96bdae9274465b2d09191384f86ef39d -func GetFreePort() (port int, err error) { - var a *net.TCPAddr - if a, err = net.ResolveTCPAddr("tcp", "localhost:0"); err == nil { - var l *net.TCPListener - if l, err = net.ListenTCP("tcp", a); err == nil { - defer l.Close() - return l.Addr().(*net.TCPAddr).Port, nil - } - } - return -}