github.com/cristalhq/netx@v0.0.0-20221116164110-442313ef3309/listener_test.go (about) 1 package netx 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "net" 8 "testing" 9 "time" 10 ) 11 12 func TestListener(t *testing.T) { 13 ctx := context.Background() 14 ln, err := NewTCPListener(ctx, "tcp", "127.0.0.1:8081", TCPListenerConfig{}) 15 failIfErr(t, err, "cannot create listener: %s", err) 16 17 go func() { 18 conn, err := ln.Accept() 19 failIfErr(t, err, "cannot accept") 20 21 _, err = conn.Write([]byte("hello world")) 22 failIfErr(t, err, "cannot write") 23 conn.Close() 24 }() 25 26 client, err := net.Dial("tcp", "127.0.0.1:8081") 27 failIfErr(t, err, "cannot dial") 28 29 got, err := io.ReadAll(client) 30 failIfErr(t, err, "cannot read") 31 32 if string(got) != "hello world" { 33 t.Fatal(string(got)) 34 } 35 } 36 37 func TestTCPListener_DeferAccept(t *testing.T) { 38 testConfig(t, TCPListenerConfig{DeferAccept: true}) 39 } 40 41 func TestTCPListener_ReusePort(t *testing.T) { 42 testConfig(t, TCPListenerConfig{ReusePort: true}) 43 } 44 45 func TestTCPListener_FastOpen(t *testing.T) { 46 testConfig(t, TCPListenerConfig{FastOpen: true}) 47 } 48 49 func TestTCPListener_All(t *testing.T) { 50 cfg := TCPListenerConfig{ 51 ReusePort: true, 52 DeferAccept: true, 53 FastOpen: true, 54 } 55 testConfig(t, cfg) 56 } 57 58 func TestTCPListener_Backlog(t *testing.T) { 59 cfg := TCPListenerConfig{ 60 Backlog: 32, 61 } 62 testConfig(t, cfg) 63 } 64 65 func testConfig(t *testing.T, cfg TCPListenerConfig) { 66 testTCPListener(t, cfg, "tcp4", "localhost:10081") 67 // TODO(oleg): fix IPv6 68 // testTCPListener(t, cfg, "tcp6", "ip6-localhost:10081") 69 } 70 71 func testTCPListener(t *testing.T, cfg TCPListenerConfig, network, addr string) { 72 const requestsCount = 1000 73 74 var serversCount = 1 75 if cfg.ReusePort { 76 serversCount = 10 77 } 78 79 doneCh := make(chan struct{}, serversCount) 80 ctx := context.Background() 81 82 var lns []net.Listener 83 for i := 0; i < serversCount; i++ { 84 ln, err := NewTCPListener(ctx, network, addr, cfg) 85 failIfErr(t, err, "cannot create listener %d using Config %#v: %s", i, &cfg, err) 86 87 go func() { 88 serveEcho(t, ln) 89 doneCh <- struct{}{} 90 }() 91 lns = append(lns, ln) 92 } 93 94 for i := 0; i < requestsCount; i++ { 95 c, err := net.Dial(network, addr) 96 failIfErr(t, err, "%d. unexpected error when dialing: %s", i, err) 97 98 req := fmt.Sprintf("request number %d", i) 99 _, err = c.Write([]byte(req)) 100 failIfErr(t, err, "%d. unexpected error when writing request: %s", i, err) 101 102 err = c.(*net.TCPConn).CloseWrite() 103 failIfErr(t, err, "%d. unexpected error when closing write end of the connection: %s", i, err) 104 105 var resp []byte 106 ch := make(chan struct{}) 107 go func() { 108 resp, err = io.ReadAll(c) 109 failIfErr(t, err, "%d. unexpected error when reading response: %s", i, err) 110 close(ch) 111 }() 112 113 select { 114 case <-ch: 115 case <-time.After(200 * time.Millisecond): 116 t.Fatalf("%d. timeout when waiting for response: %s", i, err) 117 } 118 119 if string(resp) != req { 120 t.Fatalf("%d. unexpected response %q. Expecting %q", i, resp, req) 121 } 122 err = c.Close() 123 failIfErr(t, err, "%d. unexpected error when closing connection: %s", i, err) 124 } 125 126 for _, ln := range lns { 127 err := ln.Close() 128 failIfErr(t, err, "unexpected error when closing listener") 129 } 130 131 for i := 0; i < serversCount; i++ { 132 select { 133 case <-doneCh: 134 case <-time.After(time.Second): 135 t.Fatalf("timeout when waiting for servers to be closed") 136 } 137 } 138 } 139 140 func serveEcho(t *testing.T, ln net.Listener) { 141 for { 142 c, err := ln.Accept() 143 if err != nil { 144 break 145 } 146 147 req, err := io.ReadAll(c) 148 failIfErr(t, err, "unepxected error when reading request: %s", err) 149 150 _, err = c.Write(req) 151 failIfErr(t, err, "unexpected error when writing response: %s", err) 152 153 err = c.Close() 154 failIfErr(t, err, "unexpected error when closing connection: %s", err) 155 } 156 } 157 158 func BenchmarkListener(b *testing.B) { 159 } 160 161 func failIfErr(tb testing.TB, err error, format string, args ...interface{}) { 162 tb.Helper() 163 if err != nil { 164 tb.Fatalf(format, args...) 165 } 166 }