diff --git a/libc-bottom-half/sources/tcp.c b/libc-bottom-half/sources/tcp.c index 16e8bd1ed..8b61e75f1 100644 --- a/libc-bottom-half/sources/tcp.c +++ b/libc-bottom-half/sources/tcp.c @@ -169,7 +169,6 @@ static int tcp_read_eof(void *data) { tcp_socket_state_connected_t *state = &tcp->state.connected; if (state->receive_result) { - assert(tcp->blocking); sockets_result_void_error_code_t result; __wasilibc_future_block_on(sockets_future_result_void_error_code_read( state->receive_result, &result), diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 91c21c7af..617939299 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -415,6 +415,7 @@ if (NOT (WASI STREQUAL "p1")) add_wasilibc_test(getaddrinfo.c NETWORK) add_wasilibc_test(sockets-nonblocking.c NETWORK) add_wasilibc_test(sockets-nonblocking-udp-no-connection.c NETWORK) + add_wasilibc_test(sockets-eof-delayed.c NETWORK) # Define executables for server/client tests, and they're paired together in # various combinations below for various tests. diff --git a/test/src/sockets-eof-delayed.c b/test/src/sockets-eof-delayed.c new file mode 100644 index 000000000..221da3ec9 --- /dev/null +++ b/test/src/sockets-eof-delayed.c @@ -0,0 +1,70 @@ +#include "test.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TEST(c) \ + do { \ + errno = 0; \ + if (!(c)) \ + t_error("%s failed (errno = %d)\n", #c, errno); \ + } while (0) + +int main() { + char buf[10]; + int listener_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + + // Setup a listener bound to port 0 to have the OS assign us one. + struct sockaddr_in server_address; + socklen_t server_address_len = sizeof(server_address); + server_address.sin_family = AF_INET; + server_address.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + server_address.sin_port = 0; + TEST(bind(listener_fd, (struct sockaddr *)&server_address, + sizeof(server_address)) != -1); + TEST(getsockname(listener_fd, (struct sockaddr *)&server_address, + &server_address_len) != -1); + TEST(listen(listener_fd, 1) != -1); + + // Connect a client to our server + int client_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + TEST(client_fd != -1); + TEST(connect(client_fd, (struct sockaddr *)&server_address, + server_address_len) != -1 || + errno == EINPROGRESS); + + // Wait for the client to be writable, signaling the connection is there. + struct pollfd client, listener; + client.fd = client_fd; + client.events = POLLWRNORM; + TEST(poll(&client, 1, -1) == 1); + + // Various ways of seeing that this isn't at EOF yet. + TEST(read(client_fd, buf, sizeof(buf)) == -1 && errno == EWOULDBLOCK); + client.events = POLLRDNORM; + TEST(poll(&client, 1, 0) == 0); + TEST(read(client_fd, buf, sizeof(buf)) == -1 && errno == EWOULDBLOCK); + + // Accept the socket on the listener, then immediately close it. + int server_fd; + listener.fd = listener_fd; + listener.events = POLLRDNORM; + TEST(poll(&listener, 1, -1) == 1); + TEST((server_fd = accept(listener_fd, NULL, NULL)) != -1); + TEST(close(server_fd) != -1); + + // Test to make sure that the client is now at EOF. + TEST(poll(&client, 1, -1) == 1); + TEST(read(client_fd, buf, sizeof(buf)) == 0); + TEST(read(client_fd, buf, sizeof(buf)) == 0); + + return t_status; +}