From acbd16806952f731824ac7651a963a370bc0438b Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Thu, 2 Jun 2011 11:31:42 +0200 Subject: [PATCH] Add test to assert behavior of uv_tcp_t after shutdown Handles should *never* call the close callback without the user having first issued a uv_close() command. This test is broken on windows. See #52. --- test/test-list.h | 4 + test/test-shutdown-eof.c | 175 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+) create mode 100644 test/test-shutdown-eof.c diff --git a/test/test-list.h b/test/test-list.h index f0f08e2b..c3cb2522 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -29,6 +29,7 @@ TEST_DECLARE (bind_error_fault) TEST_DECLARE (bind_error_inval) TEST_DECLARE (connection_fail) TEST_DECLARE (connection_fail_doesnt_auto_close) +TEST_DECLARE (shutdown_eof) TEST_DECLARE (callback_stack) TEST_DECLARE (timer) TEST_DECLARE (timer_again) @@ -66,6 +67,9 @@ TASK_LIST_START TEST_ENTRY (connection_fail) TEST_ENTRY (connection_fail_doesnt_auto_close) + TEST_ENTRY (shutdown_eof) + TEST_HELPER (shutdown_eof, echo_server) + TEST_ENTRY (callback_stack) TEST_HELPER (callback_stack, echo_server) diff --git a/test/test-shutdown-eof.c b/test/test-shutdown-eof.c new file mode 100644 index 00000000..0e0c772d --- /dev/null +++ b/test/test-shutdown-eof.c @@ -0,0 +1,175 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "../uv.h" +#include "task.h" +#include +#include + +static uv_timer_t timer; +static uv_tcp_t tcp; +static uv_req_t connect_req, write_req, shutdown_req; +static uv_buf_t qbuf; +static int got_q; +static int got_eof; +static int called_connect_cb; +static int called_shutdown_cb; +static int called_tcp_close_cb; +static int called_timer_close_cb; +static int called_timer_cb; + + +static uv_buf_t alloc_cb(uv_tcp_t* tcp, size_t size) { + uv_buf_t buf; + buf.base = (char*)malloc(size); + buf.len = size; + return buf; +} + + +static void read_cb(uv_tcp_t* t, int nread, uv_buf_t buf) { + ASSERT(t == &tcp); + + if (!got_q) { + ASSERT(nread == 1); + ASSERT(!got_eof); + ASSERT(buf.base[0] == 'Q'); + free(buf.base); + got_q = 1; + puts("got Q"); + } else { + ASSERT(uv_last_error().code == UV_EOF); + if (buf.base) { + free(buf.base); + } + got_eof = 1; + puts("got EOF"); + } +} + + +static void shutdown_cb(uv_req_t *req, int status) { + ASSERT(req == &shutdown_req); + + ASSERT(called_connect_cb == 1); + ASSERT(!got_eof); + ASSERT(called_tcp_close_cb == 0); + ASSERT(called_timer_close_cb == 0); + ASSERT(called_timer_cb == 0); + + called_shutdown_cb++; +} + + +static void connect_cb(uv_req_t *req, int status) { + ASSERT(status == 0); + ASSERT(req == &connect_req); + + /* Start reading from our connection so we can receive the EOF. */ + uv_read_start(&tcp, alloc_cb, read_cb); + + /* + * Write the letter 'Q' to gracefully kill the echo-server. This will not + * effect our connection. + */ + uv_req_init(&write_req, (uv_handle_t*)&tcp, NULL); + uv_write(&write_req, &qbuf, 1); + + /* Shutdown our end of the connection. */ + uv_req_init(&shutdown_req, (uv_handle_t*)&tcp, shutdown_cb); + uv_shutdown(&shutdown_req); + + called_connect_cb++; + ASSERT(called_shutdown_cb == 0); +} + + +void tcp_close_cb(uv_handle_t* handle, int status) { + ASSERT(handle == (uv_handle_t*) &tcp); + + ASSERT(called_connect_cb == 1); + ASSERT(got_q); + ASSERT(got_eof); + ASSERT(called_timer_cb == 1); + + called_tcp_close_cb++; +} + + +void timer_close_cb(uv_handle_t* handle, int status) { + ASSERT(handle == (uv_handle_t*) &timer); + called_timer_close_cb++; +} + + +void timer_cb(uv_handle_t* handle, int status) { + ASSERT(handle == (uv_handle_t*) &timer); + uv_close(handle); + + /* + * The most important assert of the test: we have not received + * tcp_close_cb yet. + */ + ASSERT(called_tcp_close_cb == 0); + uv_close((uv_handle_t*) &tcp); + + called_timer_cb++; +} + + +/* + * This test has a client which connects to the echo_server and immediately + * issues a shutdown. The echo-server, in response, will also shutdown their + * connection. We check, with a timer, that libuv is not automatically + * calling uv_close when the client receives the EOF from echo-server. + */ +TEST_IMPL(shutdown_eof) { + int r; + + uv_init(); + + qbuf.base = "Q"; + qbuf.len = 1; + + uv_timer_init(&timer, timer_close_cb, NULL); + uv_timer_start(&timer, timer_cb, 100, 0); + + struct sockaddr_in server_addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + r = uv_tcp_init(&tcp, tcp_close_cb, NULL); + ASSERT(!r); + + uv_req_init(&connect_req, (uv_handle_t*) &tcp, connect_cb); + r = uv_connect(&connect_req, server_addr); + ASSERT(!r); + + uv_run(); + + ASSERT(called_connect_cb == 1); + ASSERT(called_shutdown_cb == 1); + ASSERT(got_eof); + ASSERT(got_q); + ASSERT(called_tcp_close_cb == 1); + ASSERT(called_timer_close_cb == 1); + ASSERT(called_timer_cb == 1); + + return 0; +} +