diff --git a/test/test-connect-success-events.c b/test/test-connect-success-events.c new file mode 100644 index 0000000..3f55cc3 --- /dev/null +++ b/test/test-connect-success-events.c @@ -0,0 +1,126 @@ +/* In this test we check that stream sockets connected with `connect()` do not + * continuously report EPOLLOUT, but only when they're actually writable. + + * It is a regression test for a bug that was caused by incorrectly mapping the + `AFD_POLL_CONNECT` + * event to `EPOLLOUT`. + */ + +#include +#include + +#include "test-util.h" +#include "util.h" +#include "wepoll.h" +#include "win.h" +#include "ws.h" + +#define PORT 12345 +#define ROUNDS 5 +#define SEND_LENGTH (1 << 20) + +int main(void) { + unsigned long one = 1; + SOCKET listen_sock, server_sock, client_sock; + struct sockaddr_in addr; + HANDLE ephnd; + struct epoll_event ev; + int round; + int r; + + /* Initialize winsock. */ + r = ws_global_init(); + check(r == 0); + + /* Set up address that the server listens on and the client connects to. */ + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = htons(PORT); + + /* Create the server socket and start listening for incoming connections. */ + listen_sock = socket(AF_INET, SOCK_STREAM, 0); + check(listen_sock != INVALID_SOCKET); + r = bind(listen_sock, (struct sockaddr*) &addr, sizeof addr); + check(r == 0); + r = listen(listen_sock, SOMAXCONN); + check(r == 0); + + /* Create the client-end socket, switch it to non-blocking mode. */ + client_sock = socket(AF_INET, SOCK_STREAM, 0); + check(client_sock != INVALID_SOCKET); + r = ioctlsocket(client_sock, (long) FIONBIO, &one); + check(r == 0); + + /* Create an epoll port for the client end of the connection. */ + ephnd = epoll_create1(0); + check(ephnd != NULL); + + /* Associate the client socket with the epoll port. */ + ev.events = EPOLLOUT; + ev.data.sock = client_sock; + r = epoll_ctl(ephnd, EPOLL_CTL_ADD, client_sock, &ev); + check(r == 0); + + /* Begin connecting the client socket to the server. */ + r = connect(client_sock, (struct sockaddr*) &addr, sizeof addr); + check(r == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK); + + /* Accept the server end of the connection. */ + server_sock = accept(listen_sock, NULL, NULL); + check(server_sock != INVALID_SOCKET); + + for (round = 0; round < ROUNDS; round++) { + static char buf[SEND_LENGTH]; + struct epoll_event evs[8]; + int bytes_received, bytes_sent; + + /* Wait for client end to become writable. */ + r = epoll_wait(ephnd, evs, array_count(evs), -1); + check(r == 1); + check(evs[0].data.sock == client_sock); + check(evs[0].events == EPOLLOUT); + + /* Try to send data until the kernel buffer is full. */ + memset(buf, round, sizeof buf); + bytes_sent = 0; + do { + r = send(client_sock, buf, sizeof buf, 0); + if (r >= 0) + bytes_sent += r; + else + check(WSAGetLastError() == WSAEWOULDBLOCK); + } while (r > 0); + + /* The client end should not be writable now. */ + r = epoll_wait(ephnd, evs, array_count(evs), 0); + check(r == 0); + + /* Read all data incoming on the server end of the connection. */ + bytes_received = 0; + do { + r = recv(server_sock, buf, sizeof buf, 0); + check(r > 0); + check(buf[0] == round); + bytes_received += r; + } while (bytes_received < bytes_sent); + check(bytes_received == bytes_sent); + } + + /* Disassociate the client socket from epoll port. */ + r = epoll_ctl(ephnd, EPOLL_CTL_DEL, client_sock, NULL); + check(r == 0); + + /* Close the epoll port. */ + r = epoll_close(ephnd); + check(r == 0); + + /* Close all sockets. */ + r = closesocket(listen_sock); + check(r == 0); + r = closesocket(server_sock); + check(r == 0); + r = closesocket(client_sock); + check(r == 0); + + return 0; +}