From e2d3594774c2f8ec0066cda540946db491e0b2da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20Arboleda?= Date: Sat, 25 May 2024 01:35:35 -0500 Subject: [PATCH 1/3] unix,win: introduce `uv_accept_reject` call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This call will immediately close the accepted fd and, continue with the queued fds. Fixes: https://github.com/libuv/libuv/issues/1754 Co-authored-by: Hüseyin Açacak Signed-off-by: Juan José Arboleda --- CMakeLists.txt | 3 + Makefile.am | 3 + docs/src/stream.rst | 13 ++++ include/uv.h | 1 + src/unix/stream.c | 72 ++++++++++++------ src/win/stream.c | 42 +++++++++++ test/test-list.h | 6 ++ test/test-pipe-reject.c | 71 ++++++++++++++++++ test/test-tcp-reject-accept.c | 137 ++++++++++++++++++++++++++++++++++ test/test-tcp-reject.c | 84 +++++++++++++++++++++ 10 files changed, 411 insertions(+), 21 deletions(-) create mode 100644 test/test-pipe-reject.c create mode 100644 test/test-tcp-reject-accept.c create mode 100644 test/test-tcp-reject.c diff --git a/CMakeLists.txt b/CMakeLists.txt index af89db2d..6b2b8df8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -600,6 +600,7 @@ if(LIBUV_BUILD_TESTS) test/test-pipe-connect-prepare.c test/test-pipe-getsockname.c test/test-pipe-pending-instances.c + test/test-pipe-reject.c test/test-pipe-sendmsg.c test/test-pipe-server-close.c test/test-pipe-set-fchmod.c @@ -637,6 +638,8 @@ if(LIBUV_BUILD_TESTS) test/test-tcp-bind-error.c test/test-tcp-bind6-error.c test/test-tcp-close-accept.c + test/test-tcp-reject.c + test/test-tcp-reject-accept.c test/test-tcp-close-after-read-timeout.c test/test-tcp-close-while-connecting.c test/test-tcp-close.c diff --git a/Makefile.am b/Makefile.am index 9b9e6be7..63a252e9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -226,6 +226,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-pipe-connect-prepare.c \ test/test-pipe-getsockname.c \ test/test-pipe-pending-instances.c \ + test/test-pipe-reject.c \ test/test-pipe-sendmsg.c \ test/test-pipe-server-close.c \ test/test-pipe-close-stdout-read-stdin.c \ @@ -264,6 +265,8 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-tcp-bind-error.c \ test/test-tcp-bind6-error.c \ test/test-tcp-close-accept.c \ + test/test-tcp-reject.c \ + test/test-tcp-reject-accept.c \ test/test-tcp-close-while-connecting.c \ test/test-tcp-close-after-read-timeout.c \ test/test-tcp-close.c \ diff --git a/docs/src/stream.rst b/docs/src/stream.rst index 0b42c4b3..db0e663d 100644 --- a/docs/src/stream.rst +++ b/docs/src/stream.rst @@ -133,6 +133,19 @@ API .. note:: `server` and `client` must be handles running on the same loop. +.. c:function:: int uv_reject(uv_stream_t* server) + + This call is used in conjunction with :c:func:`uv_listen` to accept and + immediately discard incoming connections. Call this function after receiving + a :c:type:`uv_connection_cb`. + + When the :c:type:`uv_connection_cb` callback is called it is guaranteed that + this function will complete successfully the first time. If you attempt to + use it more than once, it may fail. It is suggested to only call this + function once per :c:type:`uv_connection_cb` call. As :c:type:`uv_accept`. + + .. versionadded:: REPLACEME + .. c:function:: int uv_read_start(uv_stream_t* stream, uv_alloc_cb alloc_cb, uv_read_cb read_cb) Read data from an incoming stream. The :c:type:`uv_read_cb` callback will diff --git a/include/uv.h b/include/uv.h index f0ec376b..a9a02920 100644 --- a/include/uv.h +++ b/include/uv.h @@ -542,6 +542,7 @@ UV_EXTERN size_t uv_stream_get_write_queue_size(const uv_stream_t* stream); UV_EXTERN int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb); UV_EXTERN int uv_accept(uv_stream_t* server, uv_stream_t* client); +UV_EXTERN int uv_reject(uv_stream_t* server); UV_EXTERN int uv_read_start(uv_stream_t*, uv_alloc_cb alloc_cb, diff --git a/src/unix/stream.c b/src/unix/stream.c index 18763b47..548523d0 100644 --- a/src/unix/stream.c +++ b/src/unix/stream.c @@ -533,6 +533,54 @@ void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { } +static void uv__process_queued_fds(uv_stream_t* server) { + /* Process queued fds */ + uv__stream_queued_fds_t* queued_fds; + + queued_fds = server->queued_fds; + + /* Read first */ + server->accepted_fd = queued_fds->fds[0]; + + /* All read, free */ + assert(queued_fds->offset > 0); + if (--queued_fds->offset == 0) { + uv__free(queued_fds); + server->queued_fds = NULL; + } else { + /* Shift rest */ + memmove(queued_fds->fds, + queued_fds->fds + 1, + queued_fds->offset * sizeof(*queued_fds->fds)); + } +} + + +int uv_reject(uv_stream_t* server) { + if (server->accepted_fd == -1) + return UV_EAGAIN; + + switch (server->type) { + case UV_NAMED_PIPE: + case UV_UDP: + case UV_TCP: + uv__close(server->accepted_fd); /* Simply close the fd */ + break; + default: + return UV_EINVAL; + } + + /* Process queued fds if needed */ + if (server->queued_fds != NULL) { + uv__process_queued_fds(server); + } else { + server->accepted_fd = -1; + uv__io_start(server->loop, &server->io_watcher, POLLIN); + } + + return 0; +} + int uv_accept(uv_stream_t* server, uv_stream_t* client) { int err; @@ -569,30 +617,12 @@ int uv_accept(uv_stream_t* server, uv_stream_t* client) { client->flags |= UV_HANDLE_BOUND; done: - /* Process queued fds */ + /* Process queued fds if needed */ if (server->queued_fds != NULL) { - uv__stream_queued_fds_t* queued_fds; - - queued_fds = server->queued_fds; - - /* Read first */ - server->accepted_fd = queued_fds->fds[0]; - - /* All read, free */ - assert(queued_fds->offset > 0); - if (--queued_fds->offset == 0) { - uv__free(queued_fds); - server->queued_fds = NULL; - } else { - /* Shift rest */ - memmove(queued_fds->fds, - queued_fds->fds + 1, - queued_fds->offset * sizeof(*queued_fds->fds)); - } + uv__process_queued_fds(server); } else { server->accepted_fd = -1; - if (err == 0) - uv__io_start(server->loop, &server->io_watcher, POLLIN); + uv__io_start(server->loop, &server->io_watcher, POLLIN); } return err; } diff --git a/src/win/stream.c b/src/win/stream.c index a53a10b0..2a98dcb2 100644 --- a/src/win/stream.c +++ b/src/win/stream.c @@ -48,6 +48,48 @@ int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) { } +int uv_reject(uv_stream_t* client) { + int err = 0; + uv_pipe_t* pipe_client; + uv_tcp_accept_t* req; + + if (client == NULL) { + return uv_translate_sys_error(ERROR_INVALID_PARAMETER); + } + + switch (client->type) { + case UV_NAMED_PIPE: + pipe_client = (uv_pipe_t*)client; + if (pipe_client->u.fd == -1) { + CloseHandle(pipe_client->handle); + } else { + _close(pipe_client->u.fd); + } + + pipe_client->u.fd = -1; + pipe_client->handle = INVALID_HANDLE_VALUE; + break; + case UV_TCP: + req = ((uv_tcp_t*)client)->tcp.serv.pending_accepts; + + if (!req) { + err = WSAEWOULDBLOCK; + } + + if (req->accept_socket == INVALID_SOCKET) { + err = WSAENOTCONN; + } + + closesocket(req->accept_socket); + req->accept_socket = INVALID_SOCKET; + break; + default: + err = ERROR_INVALID_PARAMETER; + } + return uv_translate_sys_error(err); +} + + int uv_accept(uv_stream_t* server, uv_stream_t* client) { int err; diff --git a/test/test-list.h b/test/test-list.h index 5aa71f59..bb50fe8d 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -153,6 +153,8 @@ TEST_DECLARE (tcp_write_to_half_open_connection) TEST_DECLARE (tcp_unexpected_read) TEST_DECLARE (tcp_read_stop) TEST_DECLARE (tcp_read_stop_start) +TEST_DECLARE (tcp_reject) +TEST_DECLARE (tcp_reject_accept) TEST_DECLARE (tcp_reuseport) TEST_DECLARE (tcp_rst) TEST_DECLARE (tcp_bind6_error_addrinuse) @@ -213,6 +215,7 @@ TEST_DECLARE (pipe_getsockname_autobind) TEST_DECLARE (pipe_getsockname_blocking) TEST_DECLARE (pipe_pending_instances) TEST_DECLARE (pipe_sendmsg) +TEST_DECLARE (pipe_reject) TEST_DECLARE (pipe_server_close) TEST_DECLARE (connection_fail) TEST_DECLARE (connection_fail_doesnt_auto_close) @@ -769,6 +772,8 @@ TASK_LIST_START TEST_ENTRY (tcp_close_accept) TEST_ENTRY (tcp_oob) #endif + TEST_ENTRY (tcp_reject) + TEST_ENTRY (tcp_reject_accept) TEST_ENTRY (tcp_flags) TEST_ENTRY (tcp_write_to_half_open_connection) TEST_ENTRY (tcp_unexpected_read) @@ -840,6 +845,7 @@ TASK_LIST_START TEST_ENTRY (pipe_getsockname_blocking) TEST_ENTRY (pipe_pending_instances) TEST_ENTRY (pipe_sendmsg) + TEST_ENTRY (pipe_reject) TEST_ENTRY (connection_fail) TEST_ENTRY (connection_fail_doesnt_auto_close) diff --git a/test/test-pipe-reject.c b/test/test-pipe-reject.c new file mode 100644 index 00000000..243bfbeb --- /dev/null +++ b/test/test-pipe-reject.c @@ -0,0 +1,71 @@ +/* Copyright libuv project and contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "task.h" +#include "uv.h" + +typedef struct { + uv_pipe_t pipe_handle; + uv_connect_t conn_req; +} client_t; +static uv_pipe_t server_handle; +static client_t client_handle; +static int connect_cb_called; + +static void connect_cb(uv_connect_t* _, int status) { + ASSERT_OK(status); + connect_cb_called++; +} + +static void connection_pipe_cb(uv_stream_t* server, int status) { + ASSERT_OK(uv_reject(server)); + ASSERT_OK(status); + uv_close((uv_handle_t*) &server_handle, NULL); +} + +TEST_IMPL(pipe_reject) { + + int r; + uv_loop_t* loop; + + loop = uv_default_loop(); + + r = uv_pipe_init(loop, &server_handle, 0); + ASSERT_OK(r); + + r = uv_pipe_bind(&server_handle, TEST_PIPENAME); + ASSERT_OK(r); + + r = uv_listen((uv_stream_t*)&server_handle, 128, connection_pipe_cb); + ASSERT_OK(r); + + r = uv_pipe_init(loop, (uv_pipe_t*)&client_handle, 0); + ASSERT_OK(r); + uv_pipe_connect(&client_handle.conn_req, + &client_handle.pipe_handle, + TEST_PIPENAME, + connect_cb); + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(connect_cb_called == 1); + return 0; +} diff --git a/test/test-tcp-reject-accept.c b/test/test-tcp-reject-accept.c new file mode 100644 index 00000000..b3e14d8c --- /dev/null +++ b/test/test-tcp-reject-accept.c @@ -0,0 +1,137 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" +#include +#include + +static int connection_cb_called = 0; +static int do_accept_called = 0; +static int close_cb_called = 0; +static int connect_cb_called = 0; +uv_tcp_t* server; + +static void close_cb(uv_handle_t* handle) { + ASSERT_NOT_NULL(handle); + free(handle); + close_cb_called++; +} + +static void connection_cb(uv_stream_t* tcp, int status) { + ASSERT_OK(status); + + if (connection_cb_called == 0) { + ASSERT_OK(uv_reject(tcp)); + +#ifdef _WIN32 + ASSERT(server->tcp.serv.pending_accepts->accept_socket == INVALID_SOCKET); +#else + ASSERT(server->accepted_fd == -1); +#endif + } else { + uv_tcp_t* accepted_handle = (uv_tcp_t*)malloc(sizeof *accepted_handle); + + ASSERT_NOT_NULL(accepted_handle); + + int r = uv_tcp_init(uv_default_loop(), accepted_handle); + ASSERT_OK(r); + + r = uv_accept((uv_stream_t*)tcp, (uv_stream_t*)accepted_handle); + ASSERT_OK(r); + + do_accept_called++; + + uv_close((uv_handle_t*)accepted_handle, close_cb); + } + + /* After accepting the two clients close the server handle */ + if (do_accept_called == 1) { + uv_close((uv_handle_t*)tcp, close_cb); + } + + connection_cb_called++; +} + +static void start_server(void) { + struct sockaddr_in addr; + server = (uv_tcp_t*)malloc(sizeof *server); + int r; + + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_NOT_NULL(server); + + r = uv_tcp_init(uv_default_loop(), server); + ASSERT_OK(r); + r = uv_tcp_bind(server, (const struct sockaddr*) &addr, 0); + ASSERT_OK(r); + + r = uv_listen((uv_stream_t*)server, 128, connection_cb); + ASSERT_OK(r); +} + +static void connect_cb(uv_connect_t* req, int status) { + ASSERT_NOT_NULL(req); + ASSERT_OK(status); + connect_cb_called++; + free(req); +} + +static void client_connect(uv_tcp_t* client) { + struct sockaddr_in addr; + int r; + uv_connect_t* connect_req = malloc(sizeof *connect_req); + + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_NOT_NULL(client); + ASSERT_NOT_NULL(connect_req); + + ASSERT_OK(uv_tcp_init(uv_default_loop(), client)); + + r = uv_tcp_connect(connect_req, + client, + (const struct sockaddr*) &addr, + connect_cb); + ASSERT_OK(r); +} + +TEST_IMPL(tcp_reject_accept) { + uv_tcp_t client1; + uv_tcp_t client2; + + start_server(); + + ASSERT_OK(uv_tcp_init(uv_default_loop(), &client1)); + ASSERT_OK(uv_tcp_init(uv_default_loop(), &client2)); + + client_connect(&client1); + client_connect(&client2); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT_EQ(2, connection_cb_called); + ASSERT_EQ(1, do_accept_called); + ASSERT_EQ(2, connect_cb_called); + ASSERT_EQ(2, close_cb_called); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} diff --git a/test/test-tcp-reject.c b/test/test-tcp-reject.c new file mode 100644 index 00000000..59cefb4b --- /dev/null +++ b/test/test-tcp-reject.c @@ -0,0 +1,84 @@ +/* Copyright libuv project and contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "task.h" +#include "uv.h" + +static uv_tcp_t server; +static uv_tcp_t client; +static int connection_cb_called; +static int connect_cb_called; + +static void connect_cb(uv_connect_t* _, int status) { + ASSERT_OK(status); + connect_cb_called++; +} + +static void connection_cb(uv_stream_t* tcp, int status) { + ASSERT_OK(status); + connection_cb_called++; + + /* Reject the connection */ + ASSERT_OK(uv_reject(tcp)); + + /* The server should not have accepted the connection */ +#ifdef _WIN32 + ASSERT(server.tcp.serv.pending_accepts->accept_socket == INVALID_SOCKET); +#else + ASSERT(server.accepted_fd == -1); +#endif + + /* Close the server */ + uv_close((uv_handle_t*) &server, NULL); +} + +static void start_server(void) { + struct sockaddr_in addr; + + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + ASSERT_OK(uv_tcp_init(uv_default_loop(), &server)); + ASSERT_OK(uv_tcp_bind(&server, (struct sockaddr*) &addr, 0)); + ASSERT_OK(uv_listen((uv_stream_t*) &server, 128, connection_cb)); +} + +TEST_IMPL(tcp_reject) { + uv_connect_t connect_req; + struct sockaddr_in addr; + + start_server(); + + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + ASSERT_OK(uv_tcp_init(uv_default_loop(), &client)); + ASSERT_OK(uv_tcp_connect(&connect_req, + &client, + (struct sockaddr*) &addr, + connect_cb)); + + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + ASSERT_EQ(1, connection_cb_called); + ASSERT_EQ(1, connect_cb_called); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} From e7880bf95457854b3093b7173fb410e886772eac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=BCseyin=20A=C3=A7acak?= Date: Mon, 13 Jan 2025 09:55:59 +0300 Subject: [PATCH 2/3] fixup! unix,win: introduce `uv_accept_reject` call --- src/win/stream.c | 44 +++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/src/win/stream.c b/src/win/stream.c index 2a98dcb2..8388e397 100644 --- a/src/win/stream.c +++ b/src/win/stream.c @@ -48,45 +48,39 @@ int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) { } -int uv_reject(uv_stream_t* client) { - int err = 0; - uv_pipe_t* pipe_client; - uv_tcp_accept_t* req; +int uv_reject(uv_stream_t* server) { + uv_tcp_accept_t* tcp_req; + uv_pipe_accept_t* pipe_req; - if (client == NULL) { - return uv_translate_sys_error(ERROR_INVALID_PARAMETER); + if (server == NULL) { + return UV_EINVAL; } - switch (client->type) { + switch (server->type) { case UV_NAMED_PIPE: - pipe_client = (uv_pipe_t*)client; - if (pipe_client->u.fd == -1) { - CloseHandle(pipe_client->handle); - } else { - _close(pipe_client->u.fd); + pipe_req = ((uv_pipe_t*)server)->pipe.serv.pending_accepts; + + if (!pipe_req || pipe_req->pipeHandle == INVALID_HANDLE_VALUE) { + return UV_EAGAIN; } - pipe_client->u.fd = -1; - pipe_client->handle = INVALID_HANDLE_VALUE; + CloseHandle(pipe_req->pipeHandle); + pipe_req->pipeHandle = INVALID_HANDLE_VALUE; break; case UV_TCP: - req = ((uv_tcp_t*)client)->tcp.serv.pending_accepts; + tcp_req = ((uv_tcp_t*)server)->tcp.serv.pending_accepts; - if (!req) { - err = WSAEWOULDBLOCK; + if (!tcp_req || tcp_req->accept_socket == INVALID_SOCKET) { + return UV_EAGAIN; } - if (req->accept_socket == INVALID_SOCKET) { - err = WSAENOTCONN; - } - - closesocket(req->accept_socket); - req->accept_socket = INVALID_SOCKET; + closesocket(tcp_req->accept_socket); + tcp_req->accept_socket = INVALID_SOCKET; break; default: - err = ERROR_INVALID_PARAMETER; + return UV_EINVAL; } - return uv_translate_sys_error(err); + return 0; } From 4b50b38676c67d46bad1baafbb375da1d0abcdab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=BCseyin=20A=C3=A7acak?= Date: Thu, 20 Feb 2025 14:46:59 +0300 Subject: [PATCH 3/3] fixup! unix,win: introduce `uv_accept_reject` call --- src/win/stream.c | 4 +++ test/test-pipe-reject.c | 53 ++++++++++++++++++++++++++++++----- test/test-tcp-reject-accept.c | 32 ++++++++++----------- test/test-tcp-reject.c | 9 +++--- 4 files changed, 70 insertions(+), 28 deletions(-) diff --git a/src/win/stream.c b/src/win/stream.c index 8388e397..89f0ccbb 100644 --- a/src/win/stream.c +++ b/src/win/stream.c @@ -66,6 +66,8 @@ int uv_reject(uv_stream_t* server) { CloseHandle(pipe_req->pipeHandle); pipe_req->pipeHandle = INVALID_HANDLE_VALUE; + ((uv_pipe_t*)server)->pipe.serv.pending_accepts = pipe_req->next_pending; + pipe_req->next_pending = NULL; break; case UV_TCP: tcp_req = ((uv_tcp_t*)server)->tcp.serv.pending_accepts; @@ -76,6 +78,8 @@ int uv_reject(uv_stream_t* server) { closesocket(tcp_req->accept_socket); tcp_req->accept_socket = INVALID_SOCKET; + ((uv_tcp_t*)server)->tcp.serv.pending_accepts = tcp_req->next_pending; + tcp_req->next_pending = NULL; break; default: return UV_EINVAL; diff --git a/test/test-pipe-reject.c b/test/test-pipe-reject.c index 243bfbeb..4f0ad581 100644 --- a/test/test-pipe-reject.c +++ b/test/test-pipe-reject.c @@ -27,8 +27,11 @@ typedef struct { uv_connect_t conn_req; } client_t; static uv_pipe_t server_handle; -static client_t client_handle; static int connect_cb_called; +static int do_accept_called = 0; +static int connection_cb_called = 0; +static uv_pipe_t connections[2]; +static client_t clients[2]; static void connect_cb(uv_connect_t* _, int status) { ASSERT_OK(status); @@ -36,9 +39,36 @@ static void connect_cb(uv_connect_t* _, int status) { } static void connection_pipe_cb(uv_stream_t* server, int status) { - ASSERT_OK(uv_reject(server)); + int r; + uv_pipe_t* conn; + + conn = &connections[connection_cb_called]; ASSERT_OK(status); - uv_close((uv_handle_t*) &server_handle, NULL); + + if (connection_cb_called == 0) { + ASSERT_OK(uv_reject(server)); + + r = uv_pipe_init(server->loop, conn, 0); + ASSERT_OK(r); + + r = uv_accept(server, (uv_stream_t*)conn); + ASSERT(r == UV_EAGAIN); + } else { + r = uv_pipe_init(server->loop, conn, 0); + ASSERT_OK(r); + + r = uv_accept(server, (uv_stream_t*)conn); + ASSERT_OK(r); + + do_accept_called++; + } + + /* After accepting the second client, close the server handle */ + if (do_accept_called == 1) { + uv_close((uv_handle_t*) &server_handle, NULL); + } + + connection_cb_called++; } TEST_IMPL(pipe_reject) { @@ -57,15 +87,24 @@ TEST_IMPL(pipe_reject) { r = uv_listen((uv_stream_t*)&server_handle, 128, connection_pipe_cb); ASSERT_OK(r); - r = uv_pipe_init(loop, (uv_pipe_t*)&client_handle, 0); + r = uv_pipe_init(loop, &clients[0].pipe_handle, 0); ASSERT_OK(r); - uv_pipe_connect(&client_handle.conn_req, - &client_handle.pipe_handle, + uv_pipe_connect(&clients[0].conn_req, + &clients[0].pipe_handle, + TEST_PIPENAME, + connect_cb); + + r = uv_pipe_init(loop, &clients[1].pipe_handle, 0); + ASSERT_OK(r); + uv_pipe_connect(&clients[1].conn_req, + &clients[1].pipe_handle, TEST_PIPENAME, connect_cb); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(connect_cb_called == 1); + ASSERT_EQ(2, connection_cb_called); + ASSERT_EQ(2, connect_cb_called); + ASSERT_EQ(1, do_accept_called); return 0; } diff --git a/test/test-tcp-reject-accept.c b/test/test-tcp-reject-accept.c index b3e14d8c..a58b7ba2 100644 --- a/test/test-tcp-reject-accept.c +++ b/test/test-tcp-reject-accept.c @@ -28,42 +28,42 @@ static int connection_cb_called = 0; static int do_accept_called = 0; static int close_cb_called = 0; static int connect_cb_called = 0; +static uv_tcp_t connections[2]; uv_tcp_t* server; static void close_cb(uv_handle_t* handle) { ASSERT_NOT_NULL(handle); - free(handle); close_cb_called++; } static void connection_cb(uv_stream_t* tcp, int status) { + int r; + uv_tcp_t* conn; + + conn = &connections[connection_cb_called]; ASSERT_OK(status); if (connection_cb_called == 0) { ASSERT_OK(uv_reject(tcp)); -#ifdef _WIN32 - ASSERT(server->tcp.serv.pending_accepts->accept_socket == INVALID_SOCKET); -#else - ASSERT(server->accepted_fd == -1); -#endif - } else { - uv_tcp_t* accepted_handle = (uv_tcp_t*)malloc(sizeof *accepted_handle); - - ASSERT_NOT_NULL(accepted_handle); - - int r = uv_tcp_init(uv_default_loop(), accepted_handle); + r = uv_tcp_init(tcp->loop, conn); ASSERT_OK(r); - r = uv_accept((uv_stream_t*)tcp, (uv_stream_t*)accepted_handle); + r = uv_accept(tcp, (uv_stream_t*)conn); + ASSERT(r == UV_EAGAIN); + } else { + r = uv_tcp_init(uv_default_loop(), conn); + ASSERT_OK(r); + + r = uv_accept((uv_stream_t*)tcp, (uv_stream_t*)conn); ASSERT_OK(r); do_accept_called++; - uv_close((uv_handle_t*)accepted_handle, close_cb); + uv_close((uv_handle_t*)conn, close_cb); } - /* After accepting the two clients close the server handle */ + /* After accepting the second client, close the server handle */ if (do_accept_called == 1) { uv_close((uv_handle_t*)tcp, close_cb); } @@ -73,7 +73,7 @@ static void connection_cb(uv_stream_t* tcp, int status) { static void start_server(void) { struct sockaddr_in addr; - server = (uv_tcp_t*)malloc(sizeof *server); + server = malloc(sizeof *server); int r; ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); diff --git a/test/test-tcp-reject.c b/test/test-tcp-reject.c index 59cefb4b..c9f41be2 100644 --- a/test/test-tcp-reject.c +++ b/test/test-tcp-reject.c @@ -24,6 +24,7 @@ static uv_tcp_t server; static uv_tcp_t client; +static uv_tcp_t conn; static int connection_cb_called; static int connect_cb_called; @@ -40,11 +41,9 @@ static void connection_cb(uv_stream_t* tcp, int status) { ASSERT_OK(uv_reject(tcp)); /* The server should not have accepted the connection */ -#ifdef _WIN32 - ASSERT(server.tcp.serv.pending_accepts->accept_socket == INVALID_SOCKET); -#else - ASSERT(server.accepted_fd == -1); -#endif + int r = uv_tcp_init(uv_default_loop(), &conn); + r = uv_accept((uv_stream_t*)tcp, (uv_stream_t*)&conn); + ASSERT(r == UV_EAGAIN); /* Close the server */ uv_close((uv_handle_t*) &server, NULL);