xref: /libuv/test/test-tcp-close-accept.c (revision 011a1ac1)
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 
close_cb(uv_handle_t * handle)45 static void close_cb(uv_handle_t* handle) {
46   close_cb_called++;
47 }
48 
write_cb(uv_write_t * req,int status)49 static void write_cb(uv_write_t* req, int status) {
50   ASSERT_OK(status);
51   write_cb_called++;
52 }
53 
connect_cb(uv_connect_t * req,int status)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);
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_OK(status);
72   ASSERT_LE(connect_reqs, req);
73   ASSERT_LE(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_OK(uv_write(&write_reqs[i], outgoing, &buf, 1, write_cb));
79 }
80 
alloc_cb(uv_handle_t * handle,size_t size,uv_buf_t * buf)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 
read_cb(uv_stream_t * stream,ssize_t nread,const uv_buf_t * buf)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_LT(pending_incoming, got_connections);
93   ASSERT_OK(uv_read_stop(stream));
94   ASSERT_EQ(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_OK(uv_tcp_init(loop, &tcp_check));
110   ASSERT_OK(uv_tcp_connect(&tcp_check_req,
111                            &tcp_check,
112                            (const struct sockaddr*) &addr,
113                            connect_cb));
114   ASSERT_OK(uv_read_start((uv_stream_t*) &tcp_check, alloc_cb, read_cb));
115 }
116 
connection_cb(uv_stream_t * server,int status)117 static void connection_cb(uv_stream_t* server, int status) {
118   unsigned int i;
119   uv_tcp_t* incoming;
120 
121   ASSERT_PTR_EQ(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_OK(uv_tcp_init(server->loop, incoming));
130   ASSERT_OK(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_OK(uv_read_start((uv_stream_t*) incoming, alloc_cb, read_cb));
139   }
140 }
141 
TEST_IMPL(tcp_close_accept)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_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
166 
167   ASSERT_OK(uv_tcp_init(loop, &tcp_server));
168   ASSERT_OK(uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0));
169   ASSERT_OK(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_OK(uv_tcp_init(loop, client));
177     ASSERT_OK(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_EQ(ARRAY_SIZE(tcp_outgoing), got_connections);
186   ASSERT_EQ((ARRAY_SIZE(tcp_outgoing) + 2), close_cb_called);
187   ASSERT_EQ(ARRAY_SIZE(tcp_outgoing), write_cb_called);
188   ASSERT_EQ(1, read_cb_called);
189 
190   MAKE_VALGRIND_HAPPY(loop);
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