win: allow bound/connected socket in uv_tcp_open()

On Unix you can pass bound and even connected socket to uv_tcp_open
and it will work as expected: you can run uv_listen or other io
functions (uv_read, uv_write) on it without problems.
On windows on the other hand the function always assumes to have clean
socket and for example uv_listen will try to bind it again, and
uv_read/write will return with errors about unreadable streams
(basically invalid internal flags).

To check if socket is already bound uv_tcp_getsockname is called which
on windows returns error when socket is unbound. To further
differentiate connected one from just bound, uv_tcp_getpeername also
returns error but when target socket is not connected.

PR-URL: https://github.com/libuv/libuv/pull/1447
Reviewed-By: Bartosz Sosnowski <bartosz@janeasystems.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
This commit is contained in:
Maciej Szeptuch (Neverous) 2017-07-31 01:29:12 +02:00 committed by Ben Noordhuis
parent 371ca6d4b2
commit 6827fa3451
3 changed files with 77 additions and 0 deletions

View File

@ -1446,6 +1446,8 @@ int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) {
WSAPROTOCOL_INFOW protocol_info;
int opt_len;
int err;
struct sockaddr_storage saddr;
int saddr_len;
/* Detect the address family of the socket. */
opt_len = (int) sizeof protocol_info;
@ -1466,6 +1468,19 @@ int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) {
return uv_translate_sys_error(err);
}
/* Support already active socket. */
saddr_len = sizeof(saddr);
if (!uv_tcp_getsockname(handle, (struct sockaddr*) &saddr, &saddr_len)) {
/* Socket is already bound. */
handle->flags |= UV_HANDLE_BOUND;
saddr_len = sizeof(saddr);
if (!uv_tcp_getpeername(handle, (struct sockaddr*) &saddr, &saddr_len)) {
/* Socket is already connected. */
uv_connection_init((uv_stream_t*) handle);
handle->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
}
}
return 0;
}

View File

@ -81,6 +81,8 @@ TEST_DECLARE (tcp_try_write)
TEST_DECLARE (tcp_write_queue_order)
TEST_DECLARE (tcp_open)
TEST_DECLARE (tcp_open_twice)
TEST_DECLARE (tcp_open_bound)
TEST_DECLARE (tcp_open_connected)
TEST_DECLARE (tcp_connect_error_after_write)
TEST_DECLARE (tcp_shutdown_after_write)
TEST_DECLARE (tcp_bind_error_addrinuse)
@ -491,6 +493,9 @@ TASK_LIST_START
TEST_ENTRY (tcp_open)
TEST_HELPER (tcp_open, tcp4_echo_server)
TEST_ENTRY (tcp_open_twice)
TEST_ENTRY (tcp_open_bound)
TEST_ENTRY (tcp_open_connected)
TEST_HELPER (tcp_open_connected, tcp4_echo_server)
TEST_ENTRY (tcp_shutdown_after_write)
TEST_HELPER (tcp_shutdown_after_write, tcp4_echo_server)

View File

@ -218,3 +218,60 @@ TEST_IMPL(tcp_open_twice) {
MAKE_VALGRIND_HAPPY();
return 0;
}
TEST_IMPL(tcp_open_bound) {
struct sockaddr_in addr;
uv_tcp_t server;
uv_os_sock_t sock;
startup();
sock = create_tcp_socket();
ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
ASSERT(0 == uv_tcp_init(uv_default_loop(), &server));
ASSERT(0 == bind(sock, (struct sockaddr*) &addr, sizeof(addr)));
ASSERT(0 == uv_tcp_open(&server, sock));
ASSERT(0 == uv_listen((uv_stream_t*) &server, 128, NULL));
MAKE_VALGRIND_HAPPY();
return 0;
}
TEST_IMPL(tcp_open_connected) {
struct sockaddr_in addr;
uv_tcp_t client;
uv_os_sock_t sock;
uv_buf_t buf = uv_buf_init("PING", 4);
ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
startup();
sock = create_tcp_socket();
ASSERT(0 == connect(sock, (struct sockaddr*) &addr, sizeof(addr)));
ASSERT(0 == uv_tcp_init(uv_default_loop(), &client));
ASSERT(0 == uv_tcp_open(&client, sock));
ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &client, &buf, 1, write_cb));
ASSERT(0 == uv_shutdown(&shutdown_req, (uv_stream_t*) &client, shutdown_cb));
ASSERT(0 == uv_read_start((uv_stream_t*) &client, alloc_cb, read_cb));
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
ASSERT(shutdown_cb_called == 1);
ASSERT(write_cb_called == 1);
ASSERT(close_cb_called == 1);
MAKE_VALGRIND_HAPPY();
return 0;
}