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