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  }