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 31static struct sockaddr_in addr; 32static uv_tcp_t tcp_server; 33static uv_tcp_t tcp_outgoing[2]; 34static uv_tcp_t tcp_incoming[ARRAY_SIZE(tcp_outgoing)]; 35static uv_connect_t connect_reqs[ARRAY_SIZE(tcp_outgoing)]; 36static uv_tcp_t tcp_check; 37static uv_connect_t tcp_check_req; 38static uv_write_t write_reqs[ARRAY_SIZE(tcp_outgoing)]; 39static unsigned int got_connections; 40static unsigned int close_cb_called; 41static unsigned int write_cb_called; 42static unsigned int read_cb_called; 43static unsigned int pending_incoming; 44 45static void close_cb(uv_handle_t* handle) { 46 close_cb_called++; 47} 48 49static void write_cb(uv_write_t* req, int status) { 50 ASSERT_OK(status); 51 write_cb_called++; 52} 53 54static 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 81static 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 87static 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 117static 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 142TEST_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 196typedef int file_has_no_tests; /* ISO C forbids an empty translation unit. */ 197 198#endif /* !_WIN32 */ 199