github.com/weedge/lib@v0.0.0-20230424045628-a36dcc1d90e4/poller/netpoll/epoll_test.go (about)

     1  // +build linux
     2  
     3  package netpoll
     4  
     5  import (
     6  	"bytes"
     7  	"io"
     8  	"net"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	"golang.org/x/sys/unix"
    14  )
    15  
    16  func TestEpollCreate(t *testing.T) {
    17  	s, err := EpollCreate(epollConfig(t))
    18  	if err != nil {
    19  		t.Fatal(err)
    20  	}
    21  	if err = s.Close(); err != nil {
    22  		t.Fatal(err)
    23  	}
    24  }
    25  
    26  func TestEpollAddClosed(t *testing.T) {
    27  	s, err := EpollCreate(epollConfig(t))
    28  	if err != nil {
    29  		t.Fatal(err)
    30  	}
    31  	if err = s.Close(); err != nil {
    32  		t.Fatal(err)
    33  	}
    34  	if err = s.Add(42, 0, nil); err != ErrClosed {
    35  		t.Fatalf("Add() = %s; want %s", err, ErrClosed)
    36  	}
    37  }
    38  
    39  func TestEpollDel(t *testing.T) {
    40  	ln := RunEchoServer(t)
    41  	defer ln.Close()
    42  
    43  	conn, err := net.Dial("tcp", ln.Addr().String())
    44  	if err != nil {
    45  		t.Fatal(err)
    46  	}
    47  	defer conn.Close()
    48  
    49  	s, err := EpollCreate(epollConfig(t))
    50  	if err != nil {
    51  		t.Fatal(err)
    52  	}
    53  
    54  	f, err := conn.(filer).File()
    55  	if err != nil {
    56  		t.Fatal(err)
    57  	}
    58  
    59  	err = s.Add(int(f.Fd()), EPOLLIN, func(events EpollEvent) {})
    60  	if err != nil {
    61  		t.Fatal(err)
    62  	}
    63  	if err = s.Del(int(f.Fd())); err != nil {
    64  		t.Errorf("unexpected error: %s", err)
    65  	}
    66  
    67  	if err = s.Close(); err != nil {
    68  		t.Fatal(err)
    69  	}
    70  }
    71  
    72  func TestEpollServer(t *testing.T) {
    73  	ep, err := EpollCreate(epollConfig(t))
    74  	if err != nil {
    75  		t.Fatal(err)
    76  	}
    77  
    78  	// Create listener on port 4444.
    79  	ln, err := listen(4444)
    80  	if err != nil {
    81  		t.Fatal(err)
    82  	}
    83  	defer unix.Close(ln)
    84  
    85  	var received bytes.Buffer
    86  	done := make(chan struct{})
    87  
    88  	// Add listener fd to epoll instance to know when there are new incoming
    89  	// connections.
    90  	ep.Add(ln, EPOLLIN, func(evt EpollEvent) {
    91  		if evt&_EPOLLCLOSED != 0 {
    92  			return
    93  		}
    94  
    95  		// Accept new incoming connection.
    96  		conn, _, err := unix.Accept(ln)
    97  		if err != nil {
    98  			t.Fatalf("could not accept: %s", err)
    99  		}
   100  
   101  		// Socket must not block read() from it.
   102  		unix.SetNonblock(conn, true)
   103  
   104  		// Add connection fd to epoll instance to get notifications about
   105  		// available data.
   106  		ep.Add(conn, EPOLLIN|EPOLLET|EPOLLHUP|EPOLLRDHUP, func(evt EpollEvent) {
   107  			// If EPOLLRDHUP is supported, it will be triggered after conn
   108  			// close() or shutdown(). In older versions EPOLLHUP is triggered.
   109  			if evt&_EPOLLCLOSED != 0 {
   110  				return
   111  			}
   112  
   113  			var buf [128]byte
   114  			for {
   115  				n, _ := unix.Read(conn, buf[:])
   116  				if n == 0 {
   117  					close(done)
   118  				}
   119  				if n <= 0 {
   120  					break
   121  				}
   122  				received.Write(buf[:n])
   123  			}
   124  		})
   125  	})
   126  
   127  	conn, err := dial(4444)
   128  	if err != nil {
   129  		t.Fatal(err)
   130  	}
   131  
   132  	// Write some data bytes one by one to the conn.
   133  	data := []byte("hello, epoll!")
   134  	for i := 0; i < len(data); i++ {
   135  		if _, err := unix.Write(conn, data[i:i+1]); err != nil {
   136  			t.Fatalf("could not make %d-th write (%v): %s", i, string(data[i]), err)
   137  		}
   138  		time.Sleep(time.Millisecond)
   139  	}
   140  
   141  	unix.Close(conn)
   142  	<-done
   143  
   144  	if err = ep.Close(); err != nil {
   145  		t.Fatal(err)
   146  	}
   147  	if !bytes.Equal(received.Bytes(), data) {
   148  		t.Errorf("bytes not equal")
   149  	}
   150  }
   151  
   152  func dial(port int) (conn int, err error) {
   153  	conn, err = unix.Socket(unix.AF_INET, unix.SOCK_STREAM, 0)
   154  	if err != nil {
   155  		return
   156  	}
   157  
   158  	addr := &unix.SockaddrInet4{
   159  		Port: port,
   160  		Addr: [4]byte{0x7f, 0, 0, 1}, // 127.0.0.1
   161  	}
   162  
   163  	err = unix.Connect(conn, addr)
   164  	if err == nil {
   165  		err = unix.SetNonblock(conn, true)
   166  	}
   167  
   168  	return
   169  }
   170  
   171  func listen(port int) (ln int, err error) {
   172  	ln, err = unix.Socket(unix.AF_INET, unix.O_NONBLOCK|unix.SOCK_STREAM, 0)
   173  	if err != nil {
   174  		return
   175  	}
   176  
   177  	// Need for avoid receiving EADDRINUSE error.
   178  	// Closed listener could be in TIME_WAIT state some time.
   179  	unix.SetsockoptInt(ln, unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
   180  
   181  	addr := &unix.SockaddrInet4{
   182  		Port: port,
   183  		Addr: [4]byte{0x7f, 0, 0, 1}, // 127.0.0.1
   184  	}
   185  
   186  	if err = unix.Bind(ln, addr); err != nil {
   187  		return
   188  	}
   189  	err = unix.Listen(ln, 4)
   190  
   191  	return
   192  }
   193  
   194  // RunEchoServer starts tcp echo server.
   195  func RunEchoServer(tb testing.TB) net.Listener {
   196  	ln, err := net.Listen("tcp", "localhost:")
   197  	if err != nil {
   198  		tb.Fatal(err)
   199  		return nil
   200  	}
   201  	go func() {
   202  		for {
   203  			conn, err := ln.Accept()
   204  			if err != nil {
   205  				if strings.Contains(err.Error(), "use of closed network connection") {
   206  					// Server closed.
   207  					return
   208  				}
   209  
   210  				tb.Fatal(err)
   211  			}
   212  			go func() {
   213  				if _, err := io.Copy(conn, conn); err != nil && err != io.EOF {
   214  					tb.Fatal(err)
   215  				}
   216  			}()
   217  		}
   218  	}()
   219  	return ln
   220  }
   221  
   222  func epollConfig(tb testing.TB) *EpollConfig {
   223  	return &EpollConfig{
   224  		OnWaitError: func(err error) {
   225  			tb.Fatal(err)
   226  		},
   227  	}
   228  }