1 | /* Copyright Joyent, Inc. and other Node 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 | /* this test is Unix only */ |
23 | #ifndef _WIN32 |
24 | |
25 | #include "uv.h" |
26 | #include "task.h" |
27 | |
28 | #include <stdio.h> |
29 | #include <string.h> |
30 | |
31 | static struct sockaddr_in addr; |
32 | static uv_tcp_t tcp_server; |
33 | static uv_tcp_t tcp_outgoing[2]; |
34 | static uv_tcp_t tcp_incoming[ARRAY_SIZE(tcp_outgoing)]; |
35 | static uv_connect_t connect_reqs[ARRAY_SIZE(tcp_outgoing)]; |
36 | static uv_tcp_t tcp_check; |
37 | static uv_connect_t tcp_check_req; |
38 | static uv_write_t write_reqs[ARRAY_SIZE(tcp_outgoing)]; |
39 | static unsigned int got_connections; |
40 | static unsigned int close_cb_called; |
41 | static unsigned int write_cb_called; |
42 | static unsigned int read_cb_called; |
43 | static unsigned int pending_incoming; |
44 | |
45 | static void close_cb(uv_handle_t* handle) { |
46 | close_cb_called++; |
47 | } |
48 | |
49 | static void write_cb(uv_write_t* req, int status) { |
50 | ASSERT(status == 0); |
51 | write_cb_called++; |
52 | } |
53 | |
54 | static void connect_cb(uv_connect_t* req, int status) { |
55 | unsigned int i; |
56 | uv_buf_t buf; |
57 | uv_stream_t* outgoing; |
58 | |
59 | if (req == &tcp_check_req) { |
60 | ASSERT(status != 0); |
61 | |
62 | /* |
63 | * Time to finish the test: close both the check and pending incoming |
64 | * connections |
65 | */ |
66 | uv_close((uv_handle_t*) &tcp_incoming[pending_incoming], close_cb); |
67 | uv_close((uv_handle_t*) &tcp_check, close_cb); |
68 | return; |
69 | } |
70 | |
71 | ASSERT(status == 0); |
72 | ASSERT(connect_reqs <= req); |
73 | ASSERT(req <= connect_reqs + ARRAY_SIZE(connect_reqs)); |
74 | i = req - connect_reqs; |
75 | |
76 | buf = uv_buf_init("x" , 1); |
77 | outgoing = (uv_stream_t*) &tcp_outgoing[i]; |
78 | ASSERT(0 == uv_write(&write_reqs[i], outgoing, &buf, 1, write_cb)); |
79 | } |
80 | |
81 | static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { |
82 | static char slab[1]; |
83 | buf->base = slab; |
84 | buf->len = sizeof(slab); |
85 | } |
86 | |
87 | static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { |
88 | uv_loop_t* loop; |
89 | unsigned int i; |
90 | |
91 | pending_incoming = (uv_tcp_t*) stream - &tcp_incoming[0]; |
92 | ASSERT(pending_incoming < got_connections); |
93 | ASSERT(0 == uv_read_stop(stream)); |
94 | ASSERT(1 == nread); |
95 | |
96 | loop = stream->loop; |
97 | read_cb_called++; |
98 | |
99 | /* Close all active incomings, except current one */ |
100 | for (i = 0; i < got_connections; i++) { |
101 | if (i != pending_incoming) |
102 | uv_close((uv_handle_t*) &tcp_incoming[i], close_cb); |
103 | } |
104 | |
105 | /* Close server, so no one will connect to it */ |
106 | uv_close((uv_handle_t*) &tcp_server, close_cb); |
107 | |
108 | /* Create new fd that should be one of the closed incomings */ |
109 | ASSERT(0 == uv_tcp_init(loop, &tcp_check)); |
110 | ASSERT(0 == uv_tcp_connect(&tcp_check_req, |
111 | &tcp_check, |
112 | (const struct sockaddr*) &addr, |
113 | connect_cb)); |
114 | ASSERT(0 == uv_read_start((uv_stream_t*) &tcp_check, alloc_cb, read_cb)); |
115 | } |
116 | |
117 | static void connection_cb(uv_stream_t* server, int status) { |
118 | unsigned int i; |
119 | uv_tcp_t* incoming; |
120 | |
121 | ASSERT(server == (uv_stream_t*) &tcp_server); |
122 | |
123 | /* Ignore tcp_check connection */ |
124 | if (got_connections == ARRAY_SIZE(tcp_incoming)) |
125 | return; |
126 | |
127 | /* Accept everyone */ |
128 | incoming = &tcp_incoming[got_connections++]; |
129 | ASSERT(0 == uv_tcp_init(server->loop, incoming)); |
130 | ASSERT(0 == uv_accept(server, (uv_stream_t*) incoming)); |
131 | |
132 | if (got_connections != ARRAY_SIZE(tcp_incoming)) |
133 | return; |
134 | |
135 | /* Once all clients are accepted - start reading */ |
136 | for (i = 0; i < ARRAY_SIZE(tcp_incoming); i++) { |
137 | incoming = &tcp_incoming[i]; |
138 | ASSERT(0 == uv_read_start((uv_stream_t*) incoming, alloc_cb, read_cb)); |
139 | } |
140 | } |
141 | |
142 | TEST_IMPL(tcp_close_accept) { |
143 | unsigned int i; |
144 | uv_loop_t* loop; |
145 | uv_tcp_t* client; |
146 | |
147 | /* |
148 | * A little explanation of what goes on below: |
149 | * |
150 | * We'll create server and connect to it using two clients, each writing one |
151 | * byte once connected. |
152 | * |
153 | * When all clients will be accepted by server - we'll start reading from them |
154 | * and, on first client's first byte, will close second client and server. |
155 | * After that, we'll immediately initiate new connection to server using |
156 | * tcp_check handle (thus, reusing fd from second client). |
157 | * |
158 | * In this situation uv__io_poll()'s event list should still contain read |
159 | * event for second client, and, if not cleaned up properly, `tcp_check` will |
160 | * receive stale event of second incoming and invoke `connect_cb` with zero |
161 | * status. |
162 | */ |
163 | |
164 | loop = uv_default_loop(); |
165 | ASSERT(0 == uv_ip4_addr("127.0.0.1" , TEST_PORT, &addr)); |
166 | |
167 | ASSERT(0 == uv_tcp_init(loop, &tcp_server)); |
168 | ASSERT(0 == uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0)); |
169 | ASSERT(0 == uv_listen((uv_stream_t*) &tcp_server, |
170 | ARRAY_SIZE(tcp_outgoing), |
171 | connection_cb)); |
172 | |
173 | for (i = 0; i < ARRAY_SIZE(tcp_outgoing); i++) { |
174 | client = tcp_outgoing + i; |
175 | |
176 | ASSERT(0 == uv_tcp_init(loop, client)); |
177 | ASSERT(0 == uv_tcp_connect(&connect_reqs[i], |
178 | client, |
179 | (const struct sockaddr*) &addr, |
180 | connect_cb)); |
181 | } |
182 | |
183 | uv_run(loop, UV_RUN_DEFAULT); |
184 | |
185 | ASSERT(ARRAY_SIZE(tcp_outgoing) == got_connections); |
186 | ASSERT((ARRAY_SIZE(tcp_outgoing) + 2) == close_cb_called); |
187 | ASSERT(ARRAY_SIZE(tcp_outgoing) == write_cb_called); |
188 | ASSERT(1 == read_cb_called); |
189 | |
190 | MAKE_VALGRIND_HAPPY(); |
191 | return 0; |
192 | } |
193 | |
194 | #else |
195 | |
196 | typedef int file_has_no_tests; /* ISO C forbids an empty translation unit. */ |
197 | |
198 | #endif /* !_WIN32 */ |
199 | |