Skip to content

Commit 4bd808c

Browse files
vtjnashJeffroMF
authored andcommitted
Re-merge "unix,stream: clear read/write states on close/eof"
This reverts commit 46f36e3. PR-URL: libuv#3006 Refs: libuv#2967 Refs: libuv#2409 Refs: libuv#2943 Refs: libuv#2968 Refs: nodejs/node#36111 Reviewed-By: Santiago Gimeno <[email protected]>
1 parent 5c83677 commit 4bd808c

9 files changed

+306
-2
lines changed

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,9 @@ if(LIBUV_BUILD_TESTS)
485485
test/test-metrics.c
486486
test/test-multiple-listen.c
487487
test/test-mutexes.c
488+
test/test-not-readable-nor-writable-on-read-error.c
489+
test/test-not-readable-on-eof.c
490+
test/test-not-writable-after-shutdown.c
488491
test/test-osx-select.c
489492
test/test-pass-always.c
490493
test/test-ping-pong.c

Makefile.am

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,9 @@ test_run_tests_SOURCES = test/blackhole-server.c \
206206
test/test-metrics.c \
207207
test/test-multiple-listen.c \
208208
test/test-mutexes.c \
209+
test/test-not-readable-nor-writable-on-read-error.c \
210+
test/test-not-readable-on-eof.c \
211+
test/test-not-writable-after-shutdown.c \
209212
test/test-osx-select.c \
210213
test/test-pass-always.c \
211214
test/test-ping-pong.c \

src/unix/stream.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,7 @@ uv_handle_type uv__handle_type(int fd) {
10111011
static void uv__stream_eof(uv_stream_t* stream, const uv_buf_t* buf) {
10121012
stream->flags |= UV_HANDLE_READ_EOF;
10131013
stream->flags &= ~UV_HANDLE_READING;
1014+
stream->flags &= ~UV_HANDLE_READABLE;
10141015
uv__io_stop(stream->loop, &stream->io_watcher, POLLIN);
10151016
if (!uv__io_active(&stream->io_watcher, POLLOUT))
10161017
uv__handle_stop(stream);
@@ -1198,6 +1199,7 @@ static void uv__read(uv_stream_t* stream) {
11981199
#endif
11991200
} else {
12001201
/* Error. User should call uv_close(). */
1202+
stream->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
12011203
stream->read_cb(stream, UV__ERR(errno), &buf);
12021204
if (stream->flags & UV_HANDLE_READING) {
12031205
stream->flags &= ~UV_HANDLE_READING;
@@ -1286,6 +1288,7 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) {
12861288
req->cb = cb;
12871289
stream->shutdown_req = req;
12881290
stream->flags |= UV_HANDLE_SHUTTING;
1291+
stream->flags &= ~UV_HANDLE_WRITABLE;
12891292

12901293
uv__io_start(stream->loop, &stream->io_watcher, POLLOUT);
12911294
uv__stream_osx_interrupt_select(stream);

src/win/tcp.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,6 +1024,7 @@ void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle,
10241024
*/
10251025
err = WSAECONNRESET;
10261026
}
1027+
handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
10271028

10281029
handle->read_cb((uv_stream_t*)handle,
10291030
uv_translate_sys_error(err),
@@ -1105,6 +1106,7 @@ void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle,
11051106
* Unix. */
11061107
err = WSAECONNRESET;
11071108
}
1109+
handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
11081110

11091111
handle->read_cb((uv_stream_t*)handle,
11101112
uv_translate_sys_error(err),

test/echo-server.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ static void after_shutdown(uv_shutdown_t* req, int status) {
6969
free(req);
7070
}
7171

72-
7372
static void after_read(uv_stream_t* handle,
7473
ssize_t nread,
7574
const uv_buf_t* buf) {
@@ -96,13 +95,20 @@ static void after_read(uv_stream_t* handle,
9695
/*
9796
* Scan for the letter Q which signals that we should quit the server.
9897
* If we get QS it means close the stream.
98+
* If we get QSH it means disable linger before close the socket.
9999
*/
100100
if (!server_closed) {
101101
for (i = 0; i < nread; i++) {
102102
if (buf->base[i] == 'Q') {
103103
if (i + 1 < nread && buf->base[i + 1] == 'S') {
104+
int reset = 0;
105+
if (i + 2 < nread && buf->base[i + 2] == 'H')
106+
reset = 1;
104107
free(buf->base);
105-
uv_close((uv_handle_t*)handle, on_close);
108+
if (reset && handle->type == UV_TCP)
109+
ASSERT(0 == uv_tcp_close_reset((uv_tcp_t*) handle, on_close));
110+
else
111+
uv_close((uv_handle_t*) handle, on_close);
106112
return;
107113
} else {
108114
uv_close(server, on_server_close);

test/test-list.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,10 @@ TEST_DECLARE (handle_type_name)
504504
TEST_DECLARE (req_type_name)
505505
TEST_DECLARE (getters_setters)
506506

507+
TEST_DECLARE (not_writable_after_shutdown)
508+
TEST_DECLARE (not_readable_nor_writable_on_read_error)
509+
TEST_DECLARE (not_readable_on_eof)
510+
507511
#ifndef _WIN32
508512
TEST_DECLARE (fork_timer)
509513
TEST_DECLARE (fork_socketpair)
@@ -1127,6 +1131,13 @@ TASK_LIST_START
11271131
TEST_ENTRY (idna_toascii)
11281132
#endif
11291133

1134+
TEST_ENTRY (not_writable_after_shutdown)
1135+
TEST_HELPER (not_writable_after_shutdown, tcp4_echo_server)
1136+
TEST_ENTRY (not_readable_nor_writable_on_read_error)
1137+
TEST_HELPER (not_readable_nor_writable_on_read_error, tcp4_echo_server)
1138+
TEST_ENTRY (not_readable_on_eof)
1139+
TEST_HELPER (not_readable_on_eof, tcp4_echo_server)
1140+
11301141
TEST_ENTRY (metrics_idle_time)
11311142
TEST_ENTRY (metrics_idle_time_thread)
11321143
TEST_ENTRY (metrics_idle_time_zero)
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/* Copyright the libuv project contributors. All rights reserved.
2+
*
3+
* Permission is hereby granted, free of charge, to any person obtaining a copy
4+
* of this software and associated documentation files (the "Software"), to
5+
* deal in the Software without restriction, including without limitation the
6+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7+
* sell copies of the Software, and to permit persons to whom the Software is
8+
* furnished to do so, subject to the following conditions:
9+
*
10+
* The above copyright notice and this permission notice shall be included in
11+
* all copies or substantial portions of the Software.
12+
*
13+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19+
* IN THE SOFTWARE.
20+
*/
21+
22+
#include "uv.h"
23+
#include "task.h"
24+
25+
static uv_loop_t loop;
26+
static uv_tcp_t tcp_client;
27+
static uv_connect_t connect_req;
28+
static uv_write_t write_req;
29+
static char reset_me_cmd[] = {'Q', 'S', 'H'};
30+
31+
static int connect_cb_called;
32+
static int read_cb_called;
33+
static int write_cb_called;
34+
static int close_cb_called;
35+
36+
static void write_cb(uv_write_t* req, int status) {
37+
write_cb_called++;
38+
ASSERT(status == 0);
39+
}
40+
41+
static void alloc_cb(uv_handle_t* handle,
42+
size_t suggested_size,
43+
uv_buf_t* buf) {
44+
static char slab[64];
45+
buf->base = slab;
46+
buf->len = sizeof(slab);
47+
}
48+
49+
static void close_cb(uv_handle_t* handle) {
50+
close_cb_called++;
51+
}
52+
53+
static void read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) {
54+
read_cb_called++;
55+
56+
ASSERT((nread < 0) && (nread != UV_EOF));
57+
ASSERT(0 == uv_is_writable(handle));
58+
ASSERT(0 == uv_is_readable(handle));
59+
60+
uv_close((uv_handle_t*) handle, close_cb);
61+
}
62+
63+
static void connect_cb(uv_connect_t* req, int status) {
64+
int r;
65+
uv_buf_t reset_me;
66+
67+
connect_cb_called++;
68+
ASSERT(status == 0);
69+
70+
r = uv_read_start((uv_stream_t*) &tcp_client, alloc_cb, read_cb);
71+
ASSERT(r == 0);
72+
73+
reset_me = uv_buf_init(reset_me_cmd, sizeof(reset_me_cmd));
74+
75+
r = uv_write(&write_req,
76+
(uv_stream_t*) &tcp_client,
77+
&reset_me,
78+
1,
79+
write_cb);
80+
81+
ASSERT(r == 0);
82+
}
83+
84+
TEST_IMPL(not_readable_nor_writable_on_read_error) {
85+
struct sockaddr_in sa;
86+
ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &sa));
87+
ASSERT(0 == uv_loop_init(&loop));
88+
ASSERT(0 == uv_tcp_init(&loop, &tcp_client));
89+
90+
ASSERT(0 == uv_tcp_connect(&connect_req,
91+
&tcp_client,
92+
(const struct sockaddr*) &sa,
93+
connect_cb));
94+
95+
ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT));
96+
97+
ASSERT(connect_cb_called == 1);
98+
ASSERT(read_cb_called == 1);
99+
ASSERT(write_cb_called == 1);
100+
ASSERT(close_cb_called == 1);
101+
102+
MAKE_VALGRIND_HAPPY();
103+
return 0;
104+
}

test/test-not-readable-on-eof.c

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/* Copyright the libuv project contributors. All rights reserved.
2+
*
3+
* Permission is hereby granted, free of charge, to any person obtaining a copy
4+
* of this software and associated documentation files (the "Software"), to
5+
* deal in the Software without restriction, including without limitation the
6+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7+
* sell copies of the Software, and to permit persons to whom the Software is
8+
* furnished to do so, subject to the following conditions:
9+
*
10+
* The above copyright notice and this permission notice shall be included in
11+
* all copies or substantial portions of the Software.
12+
*
13+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19+
* IN THE SOFTWARE.
20+
*/
21+
22+
#include "uv.h"
23+
#include "task.h"
24+
25+
static uv_loop_t loop;
26+
static uv_tcp_t tcp_client;
27+
static uv_connect_t connect_req;
28+
static uv_write_t write_req;
29+
static char close_me_cmd[] = {'Q', 'S'};
30+
31+
static int connect_cb_called;
32+
static int read_cb_called;
33+
static int write_cb_called;
34+
static int close_cb_called;
35+
36+
static void write_cb(uv_write_t* req, int status) {
37+
write_cb_called++;
38+
ASSERT(status == 0);
39+
}
40+
41+
static void alloc_cb(uv_handle_t* handle,
42+
size_t suggested_size,
43+
uv_buf_t* buf) {
44+
static char slab[64];
45+
buf->base = slab;
46+
buf->len = sizeof(slab);
47+
}
48+
49+
static void close_cb(uv_handle_t* handle) {
50+
close_cb_called++;
51+
}
52+
53+
static void read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) {
54+
read_cb_called++;
55+
56+
ASSERT(nread == UV_EOF);
57+
ASSERT(0 == uv_is_readable(handle));
58+
59+
uv_close((uv_handle_t*) handle, close_cb);
60+
}
61+
62+
static void connect_cb(uv_connect_t* req, int status) {
63+
int r;
64+
uv_buf_t close_me;
65+
66+
connect_cb_called++;
67+
ASSERT(status == 0);
68+
69+
r = uv_read_start((uv_stream_t*) &tcp_client, alloc_cb, read_cb);
70+
ASSERT(r == 0);
71+
72+
close_me = uv_buf_init(close_me_cmd, sizeof(close_me_cmd));
73+
74+
r = uv_write(&write_req,
75+
(uv_stream_t*) &tcp_client,
76+
&close_me,
77+
1,
78+
write_cb);
79+
80+
ASSERT(r == 0);
81+
}
82+
83+
TEST_IMPL(not_readable_on_eof) {
84+
struct sockaddr_in sa;
85+
ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &sa));
86+
ASSERT(0 == uv_loop_init(&loop));
87+
ASSERT(0 == uv_tcp_init(&loop, &tcp_client));
88+
89+
ASSERT(0 == uv_tcp_connect(&connect_req,
90+
&tcp_client,
91+
(const struct sockaddr*) &sa,
92+
connect_cb));
93+
94+
ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT));
95+
96+
ASSERT(connect_cb_called == 1);
97+
ASSERT(read_cb_called == 1);
98+
ASSERT(write_cb_called == 1);
99+
ASSERT(close_cb_called == 1);
100+
101+
MAKE_VALGRIND_HAPPY();
102+
return 0;
103+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/* Copyright the libuv project contributors. All rights reserved.
2+
*
3+
* Permission is hereby granted, free of charge, to any person obtaining a copy
4+
* of this software and associated documentation files (the "Software"), to
5+
* deal in the Software without restriction, including without limitation the
6+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7+
* sell copies of the Software, and to permit persons to whom the Software is
8+
* furnished to do so, subject to the following conditions:
9+
*
10+
* The above copyright notice and this permission notice shall be included in
11+
* all copies or substantial portions of the Software.
12+
*
13+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19+
* IN THE SOFTWARE.
20+
*/
21+
22+
#include "uv.h"
23+
#include "task.h"
24+
25+
static uv_shutdown_t shutdown_req;
26+
27+
static void close_cb(uv_handle_t* handle) {
28+
29+
}
30+
31+
static void shutdown_cb(uv_shutdown_t* req, int status) {
32+
uv_close((uv_handle_t*) req->handle, close_cb);
33+
}
34+
35+
static void connect_cb(uv_connect_t* req, int status) {
36+
int r;
37+
ASSERT(status == 0);
38+
39+
r = uv_shutdown(&shutdown_req, req->handle, shutdown_cb);
40+
ASSERT(r == 0);
41+
42+
ASSERT(0 == uv_is_writable(req->handle));
43+
}
44+
45+
TEST_IMPL(not_writable_after_shutdown) {
46+
int r;
47+
struct sockaddr_in addr;
48+
uv_loop_t* loop;
49+
uv_tcp_t socket;
50+
uv_connect_t connect_req;
51+
52+
ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
53+
loop = uv_default_loop();
54+
55+
r = uv_tcp_init(loop, &socket);
56+
ASSERT(r == 0);
57+
58+
r = uv_tcp_connect(&connect_req,
59+
&socket,
60+
(const struct sockaddr*) &addr,
61+
connect_cb);
62+
ASSERT(r == 0);
63+
64+
r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
65+
ASSERT(r == 0);
66+
67+
MAKE_VALGRIND_HAPPY();
68+
return 0;
69+
}

0 commit comments

Comments
 (0)