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 }