// Test tcp.Addr works correctly. func TestTCPAddr(t *testing.T) { tests.ResetLog() defer tests.DisplayLog() t.Log("Given the need to listen on any open port and know that bound address.") { // Create a configuration. cfg := tcp.Config{ NetType: "tcp4", Addr: ":0", // Defer port assignment to OS. ConnHandler: tcpConnHandler{}, ReqHandler: tcpReqHandler{}, RespHandler: tcpRespHandler{}, OptIntPool: tcp.OptIntPool{ RecvMinPoolSize: func() int { return 2 }, RecvMaxPoolSize: func() int { return 1000 }, SendMinPoolSize: func() int { return 2 }, SendMaxPoolSize: func() int { return 1000 }, }, } // Create a new TCP value. u, err := tcp.New(tests.Context, "TEST", cfg) if err != nil { t.Fatal("\tShould be able to create a new TCP listener.", tests.Failed, err) } t.Log("\tShould be able to create a new TCP listener.", tests.Success) // Addr should be nil before Start. if addr := u.Addr(); addr != nil { t.Fatalf("\tAddr() should be nil before Start; Addr() = %q. %s", addr, tests.Failed) } t.Log("\tAddr() should be nil before Start.", tests.Success) // Start accepting client data. if err := u.Start(tests.Context); err != nil { t.Fatal("\tShould be able to start the TCP listener.", tests.Failed, err) } defer u.Stop(tests.Context) // Addr should be non-nil after Start. addr := u.Addr() if addr == nil { t.Fatal("\tAddr() should be not be nil after Start.", tests.Failed) } t.Log("\tAddr() should be not be nil after Start.", tests.Success) // The OS should assign a random open port, which shouldn't be 0. _, port, err := net.SplitHostPort(addr.String()) if err != nil { t.Fatalf("\tSplitHostPort should not fail. tests.Failed %v. %s", err, tests.Failed) } if port == "0" { t.Fatalf("\tAddr port should not be %q. %s", port, tests.Failed) } t.Logf("\tAddr() should be not be 0 after Start (port = %q). %s", port, tests.Success) } }
func main() { const context = "startup" // Create the configuration. cfg := tcp.Config{ NetType: "tcp4", Addr: ":6000", ConnHandler: tcpConnHandler{}, ReqHandler: tcpReqHandler{}, RespHandler: tcpRespHandler{}, OptIntPool: tcp.OptIntPool{ RecvMinPoolSize: func() int { return 2 }, RecvMaxPoolSize: func() int { return 100 }, SendMinPoolSize: func() int { return 2 }, SendMaxPoolSize: func() int { return 100 }, }, } // Create a new TCP value. t, err := tcp.New(context, "Sample", cfg) if err != nil { log.Error(context, "main", err, "Creating tcp") return } // Start accepting client data. if err := t.Start(context); err != nil { log.Error(context, "main", err, "Starting tcp") return } // Defer the stop on shutdown. defer t.Stop(context) log.User(context, "main", "Waiting for data on: %s", t.Addr()) // Listen for an interrupt signal from the OS. sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, os.Interrupt) <-sigChan // Use telnet to test the server. // telnet localhost 6000 log.User(context, "main", "Shutting down") }
// TestRateLimit tests we can drop connections when they come in too fast. func TestRateLimit(t *testing.T) { tests.ResetLog() defer tests.DisplayLog() const ratelimit = 1 * time.Second t.Log("Given the need to drop TCP connections.") { recvCfg := pool.Config{ MinRoutines: func() int { return 2 }, MaxRoutines: func() int { return 1000 }, } recv, err := pool.New(tests.Context, "Test-Recv", recvCfg) if err != nil { t.Fatal("\tShould be able to create a work pool for the recv.", tests.Failed, err) } sendCfg := pool.Config{ MinRoutines: func() int { return 2 }, MaxRoutines: func() int { return 1000 }, } send, err := pool.New(tests.Context, "Test-Send", sendCfg) if err != nil { t.Fatal("\tShould be able to create a work pool for the send.", tests.Failed, err) } // Create a configuration. cfg := tcp.Config{ NetType: "tcp4", Addr: ":0", ConnHandler: tcpConnHandler{}, ReqHandler: tcpReqHandler{}, RespHandler: tcpRespHandler{}, OptUserPool: tcp.OptUserPool{ RecvPool: recv, SendPool: send, }, OptRateLimit: tcp.OptRateLimit{ RateLimit: func() time.Duration { return ratelimit }, }, } // Create a new TCP value. u, err := tcp.New(tests.Context, "TEST", cfg) if err != nil { t.Fatal("\tShould be able to create a new TCP listener.", tests.Failed, err) } t.Log("\tShould be able to create a new TCP listener.", tests.Success) // Start accepting client data. if err := u.Start(tests.Context); err != nil { t.Fatal("\tShould be able to start the TCP listener.", tests.Failed, err) } t.Log("\tShould be able to start the TCP listener.", tests.Success) defer u.Stop(tests.Context) newconn := func() (*bufio.Writer, *bufio.Reader, net.Conn, error) { // Let's connect to the host:port. conn, err := net.Dial("tcp4", u.Addr().String()) if err != nil { return nil, nil, nil, err } return bufio.NewWriter(conn), bufio.NewReader(conn), conn, nil } // Make a successful connection successfulTest := func(ctx interface{}) { w, r, c, err := newconn() if err != nil { t.Fatal("\tShould be able to dial a new TCP connection.", ctx, tests.Failed, err) } t.Log("\tShould be able to dial a new TCP connection.", ctx, tests.Success) defer c.Close() if _, err := w.WriteString("Hello\n"); err != nil { t.Fatal("\tShould be able to send data to the connection.", ctx, tests.Failed, err) } t.Log("\tShould be able to send data to the connection.", ctx, tests.Success) if err := w.Flush(); err != nil { t.Fatal("\tShould be able to flush the writer.", ctx, tests.Failed, err) } t.Log("\tShould be able to flush the writer.", ctx, tests.Success) // Let's read the response. response, err := r.ReadString('\n') if err != nil { t.Fatal("\tShould be able to read the response from the connection.", ctx, tests.Failed, err) } t.Log("\tShould be able to read the response from the connection.", ctx, tests.Success) t.Log(response) } successfulTest("PRE-LIMIT") // The next 100 connections should fail (assuming it's all under rateLimit amount of time). for i := 0; i < 100; i++ { // Apparently, even though the connection should not exist, we are still allowed // to connect to the remote socket and write to it. The error is exhibited // only when it's time to perform a read on that connection. w, r, c, err := newconn() if err != nil { t.Fatal("\tShould be able to dial a non-first TCP connection.", tests.Failed, err) } t.Log("\tShould be able to dial a non-first TCP connection", c.LocalAddr(), tests.Success) defer c.Close() if _, err := w.WriteString("Hello\n"); err != nil { t.Fatal("\tShould be able to send data to the connection.", tests.Failed, err) } t.Log("\tShould be able to send data to the connection.", tests.Success) if err := w.Flush(); err != nil { t.Fatal("\tShould be able to flush the writer.", tests.Failed, err) } t.Log("\tShould be able to flush the writer.", tests.Success) // Let's read the response. _, err = r.ReadString('\n') if err == nil { t.Fatal("\tShould have tests.Failed to read from the connection.", tests.Failed) } t.Log("\tShould have tests.Failed to read from the connection", tests.Success, err) } // Sleep for rateLimit to perform another successful test. time.Sleep(ratelimit) successfulTest("POST-LIMIT") // NOTE If you call another 'successfulTest' here, we will fail because we expect // the test to fail due to the limit. } }
// TestTCP provide a test of listening for a connection and // echoing the data back. func TestTCP(t *testing.T) { tests.ResetLog() defer tests.DisplayLog() t.Log("Given the need to listen and process TCP data.") { // Create a configuration. cfg := tcp.Config{ NetType: "tcp4", Addr: ":0", ConnHandler: tcpConnHandler{}, ReqHandler: tcpReqHandler{}, RespHandler: tcpRespHandler{}, OptIntPool: tcp.OptIntPool{ RecvMinPoolSize: func() int { return 2 }, RecvMaxPoolSize: func() int { return 1000 }, SendMinPoolSize: func() int { return 2 }, SendMaxPoolSize: func() int { return 1000 }, }, } // Create a new TCP value. u, err := tcp.New(tests.Context, "TEST", cfg) if err != nil { t.Fatal("\tShould be able to create a new TCP listener.", tests.Failed, err) } t.Log("\tShould be able to create a new TCP listener.", tests.Success) // Start accepting client data. if err := u.Start(tests.Context); err != nil { t.Fatal("\tShould be able to start the TCP listener.", tests.Failed, err) } t.Log("\tShould be able to start the TCP listener.", tests.Success) defer u.Stop(tests.Context) // Let's connect back and send a TCP package conn, err := net.Dial("tcp4", u.Addr().String()) if err != nil { t.Fatal("\tShould be able to dial a new TCP connection.", tests.Failed, err) } t.Log("\tShould be able to dial a new TCP connection.", tests.Success) // Setup a bufio reader to extract the response. bufReader := bufio.NewReader(conn) bufWriter := bufio.NewWriter(conn) // Send some know data to the tcp listener. if _, err := bufWriter.WriteString("Hello\n"); err != nil { t.Fatal("\tShould be able to send data to the connection.", tests.Failed, err) } t.Log("\tShould be able to send data to the connection.", tests.Success) bufWriter.Flush() // Let's read the response. response, err := bufReader.ReadString('\n') if err != nil { t.Fatal("\tShould be able to read the response from the connection.", tests.Failed, err) } t.Log("\tShould be able to read the response from the connection.", tests.Success) if response == "GOT IT\n" { t.Log("\tShould receive the string \"GOT IT\".", tests.Success) } else { t.Error("\tShould receive the string \"GOT IT\".", tests.Failed, response) } d := atomic.LoadInt64(&dur) duration := time.Duration(d) if duration <= 2*time.Second { t.Log("\tShould be less that 2 seconds.", tests.Success) } else { t.Error("\tShould be less that 2 seconds.", tests.Failed, duration) } } }
// TestDropConnections tests we can drop connections when configured. func TestDropConnections(t *testing.T) { tests.ResetLog() defer tests.DisplayLog() t.Log("Given the need to drop TCP connections.") { recvCfg := pool.Config{ MinRoutines: func() int { return 2 }, MaxRoutines: func() int { return 1000 }, } recv, err := pool.New(tests.Context, "Test-Recv", recvCfg) if err != nil { t.Fatal("\tShould be able to create a work pool for the recv.", tests.Failed, err) } sendCfg := pool.Config{ MinRoutines: func() int { return 2 }, MaxRoutines: func() int { return 1000 }, } send, err := pool.New(tests.Context, "Test-Send", sendCfg) if err != nil { t.Fatal("\tShould be able to create a work pool for the send.", tests.Failed, err) } // Create a configuration. cfg := tcp.Config{ NetType: "tcp4", Addr: ":0", ConnHandler: tcpConnHandler{}, ReqHandler: tcpReqHandler{}, RespHandler: tcpRespHandler{}, OptUserPool: tcp.OptUserPool{ RecvPool: recv, SendPool: send, }, } // Create a new TCP value. u, err := tcp.New(tests.Context, "TEST", cfg) if err != nil { t.Fatal("\tShould be able to create a new TCP listener.", tests.Failed, err) } t.Log("\tShould be able to create a new TCP listener.", tests.Success) // Set the drop connection flag to true. t.Log("\tSet the drop connections flag to TRUE.", tests.Success) u.DropConnections(tests.Context, true) // Start accepting client data. if err := u.Start(tests.Context); err != nil { t.Fatal("\tShould be able to start the TCP listener.", tests.Failed, err) } t.Log("\tShould be able to start the TCP listener.", tests.Success) defer u.Stop(tests.Context) // Let's connect to the host:port. conn, err := net.Dial("tcp4", u.Addr().String()) if err != nil { t.Fatal("\tShould be able to dial a new TCP connection.", tests.Failed, err) } t.Log("\tShould be able to dial a new TCP connection.", tests.Success) // An attempt to read should result in an EOF. b := make([]byte, 1) if _, err = conn.Read(b); err == nil { t.Fatal("\tShould not be able to read the response from the connection.", tests.Failed, err) } t.Log("\tShould not be able to read the response from the connection.", tests.Success) } }