unix,tcp: allow EINVAL errno from setsockopt in uv_tcp_close_reset() (#3662)
Some setsockopt() implememantations may return with errno of EINVAL when the socket has been shut down already, as documented in the Open Group Specifications Issue 7, 2018. When this happens, reset errno and continue to mark the socket closed and handle any callback.
This commit is contained in:
parent
47e95602c4
commit
f5e4d85cd2
@ -321,8 +321,16 @@ int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb) {
|
||||
return UV_EINVAL;
|
||||
|
||||
fd = uv__stream_fd(handle);
|
||||
if (0 != setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)))
|
||||
return UV__ERR(errno);
|
||||
if (0 != setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l))) {
|
||||
if (errno == EINVAL) {
|
||||
/* Open Group Specifications Issue 7, 2018 edition states that
|
||||
* EINVAL may mean the socket has been shut down already.
|
||||
* Behavior observed on Solaris, illumos and macOS. */
|
||||
errno = 0;
|
||||
} else {
|
||||
return UV__ERR(errno);
|
||||
}
|
||||
}
|
||||
|
||||
uv_close((uv_handle_t*) handle, close_cb);
|
||||
return 0;
|
||||
|
||||
@ -133,6 +133,7 @@ TEST_DECLARE (tcp_close_after_read_timeout)
|
||||
TEST_DECLARE (tcp_close)
|
||||
TEST_DECLARE (tcp_close_reset_accepted)
|
||||
TEST_DECLARE (tcp_close_reset_accepted_after_shutdown)
|
||||
TEST_DECLARE (tcp_close_reset_accepted_after_socket_shutdown)
|
||||
TEST_DECLARE (tcp_close_reset_client)
|
||||
TEST_DECLARE (tcp_close_reset_client_after_shutdown)
|
||||
TEST_DECLARE (tcp_create_early)
|
||||
@ -710,6 +711,7 @@ TASK_LIST_START
|
||||
TEST_ENTRY (tcp_close)
|
||||
TEST_ENTRY (tcp_close_reset_accepted)
|
||||
TEST_ENTRY (tcp_close_reset_accepted_after_shutdown)
|
||||
TEST_ENTRY (tcp_close_reset_accepted_after_socket_shutdown)
|
||||
TEST_ENTRY (tcp_close_reset_client)
|
||||
TEST_ENTRY (tcp_close_reset_client_after_shutdown)
|
||||
TEST_ENTRY (tcp_create_early)
|
||||
|
||||
@ -25,6 +25,12 @@
|
||||
#include <errno.h>
|
||||
#include <string.h> /* memset */
|
||||
|
||||
#ifdef _WIN32
|
||||
# define INVALID_FD (INVALID_HANDLE_VALUE)
|
||||
#else
|
||||
# define INVALID_FD (-1)
|
||||
#endif
|
||||
|
||||
static uv_loop_t* loop;
|
||||
static uv_tcp_t tcp_server;
|
||||
static uv_tcp_t tcp_client;
|
||||
@ -62,9 +68,22 @@ static void do_write(uv_tcp_t* handle) {
|
||||
|
||||
|
||||
static void do_close(uv_tcp_t* handle) {
|
||||
uv_os_fd_t fd;
|
||||
int r;
|
||||
|
||||
if (shutdown_before_close == 1) {
|
||||
ASSERT(0 == uv_shutdown(&shutdown_req, (uv_stream_t*) handle, shutdown_cb));
|
||||
ASSERT(UV_EINVAL == uv_tcp_close_reset(handle, close_cb));
|
||||
} else if (shutdown_before_close == 2) {
|
||||
r = uv_fileno((const uv_handle_t*) handle, &fd);
|
||||
ASSERT_EQ(r, 0);
|
||||
ASSERT_NE(fd, INVALID_FD);
|
||||
#ifdef _WIN32
|
||||
ASSERT_EQ(0, shutdown(fd, SD_BOTH));
|
||||
#else
|
||||
ASSERT_EQ(0, shutdown(fd, SHUT_RDWR));
|
||||
#endif
|
||||
ASSERT_EQ(0, uv_tcp_close_reset(handle, close_cb));
|
||||
} else {
|
||||
ASSERT(0 == uv_tcp_close_reset(handle, close_cb));
|
||||
ASSERT(UV_ENOTCONN == uv_shutdown(&shutdown_req, (uv_stream_t*) handle, shutdown_cb));
|
||||
@ -288,3 +307,30 @@ TEST_IMPL(tcp_close_reset_accepted_after_shutdown) {
|
||||
MAKE_VALGRIND_HAPPY();
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST_IMPL(tcp_close_reset_accepted_after_socket_shutdown) {
|
||||
int r;
|
||||
|
||||
loop = uv_default_loop();
|
||||
|
||||
start_server(loop, &tcp_server);
|
||||
|
||||
client_close = 0;
|
||||
shutdown_before_close = 2;
|
||||
|
||||
do_connect(loop, &tcp_client);
|
||||
|
||||
ASSERT_EQ(write_cb_called, 0);
|
||||
ASSERT_EQ(close_cb_called, 0);
|
||||
ASSERT_EQ(shutdown_cb_called, 0);
|
||||
|
||||
r = uv_run(loop, UV_RUN_DEFAULT);
|
||||
ASSERT_EQ(r, 0);
|
||||
|
||||
ASSERT_EQ(write_cb_called, 4);
|
||||
ASSERT_EQ(close_cb_called, 1);
|
||||
ASSERT_EQ(shutdown_cb_called, 0);
|
||||
|
||||
MAKE_VALGRIND_HAPPY();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user