diff --git a/.travis.yml b/.travis.yml index 16d87cd..bdfb9f5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,6 +31,7 @@ script: - cd build - cmake .. - make +- export ARGS="-VV" - make test after_success: - codecov diff --git a/CMakeLists.txt b/CMakeLists.txt index 81e1ff8..15054a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,6 +67,7 @@ ExternalProject_Add(nanomsg PREFIX ${CMAKE_CURRENT_BINARY_DIR}/_prefix/nanomsg GIT_REPOSITORY https://github.com/nanomsg/nanomsg.git GIT_TAG "master" + PATCH_COMMAND patch -p1 < ${PATCHES_DIR}/btcp.patch CMAKE_ARGS += -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} ) add_library(libnanomsg STATIC SHARED IMPORTED) diff --git a/patches/btcp.patch b/patches/btcp.patch new file mode 100644 index 0000000..70f5bc1 --- /dev/null +++ b/patches/btcp.patch @@ -0,0 +1,13 @@ +diff --git a/src/transports/tcp/btcp.c b/src/transports/tcp/btcp.c +index 4fe8b58..b87f0a1 100644 +--- a/src/transports/tcp/btcp.c ++++ b/src/transports/tcp/btcp.c +@@ -154,7 +154,7 @@ int nn_btcp_create (struct nn_ep *ep) + + rc = nn_btcp_listen (self); + if (rc != 0) { +- // I suspect we might need to do nn_free here. ++ nn_free (self); + return rc; + } + diff --git a/src/libparodus.c b/src/libparodus.c index 8718f16..31fda17 100644 --- a/src/libparodus.c +++ b/src/libparodus.c @@ -54,9 +54,9 @@ typedef struct { int reconnect_count; libpd_cfg_t cfg; bool connect_on_every_send; // always false, currently - int rcv_sock; - int stop_rcv_sock; - int send_sock; + nn_sock_t rcv_sock; + nn_sock_t stop_rcv_sock; + nn_sock_t send_sock; char wrp_queue_name[QNAME_SIZE]; libpd_mq_t wrp_queue; pthread_t wrp_receiver_tid; @@ -118,6 +118,12 @@ static void getParodusUrl(__instance_t *inst) libpd_log (LEVEL_INFO, ("LIBPARODUS: client url is %s\n", inst->client_url)); } +static void init_nn_sock (nn_sock_t *sock) +{ + sock->sock = -1; + sock->eid = -1; +} + static __instance_t *make_new_instance (libpd_cfg_t *cfg) { size_t qname_len; @@ -128,6 +134,9 @@ static __instance_t *make_new_instance (libpd_cfg_t *cfg) if (qname_len >= QNAME_SIZE) return NULL; memset ((void*) inst, 0, sizeof(__instance_t)); + init_nn_sock (&inst->rcv_sock); + init_nn_sock (&inst->stop_rcv_sock); + init_nn_sock (&inst->send_sock); pthread_mutex_init (&inst->send_mutex, NULL); //inst->cfg = *cfg; memcpy (&inst->cfg, cfg, sizeof(libpd_cfg_t)); @@ -160,13 +169,24 @@ bool is_auth_received (libpd_instance_t instance) nn_shutdown ((sock), 0); \ (sock) = 0; -void shutdown_socket (int *sock) +void shutdown_socket (nn_sock_t *sock) { - if (*sock >= 0) { - nn_shutdown (*sock, 0); - nn_close (*sock); + int rtn; + if (sock->sock < 0) + return; + if (sock->eid >= 0) { + rtn = nn_shutdown (sock->sock, sock->eid); + if (rtn != 0) { + libpd_log_err (LEVEL_DEBUG, errno, ("socket shutdown error\n")); + } + sock->eid = -1; } - *sock = -1; + rtn = nn_close (sock->sock); + if (rtn != 0) { + libpd_log_err (LEVEL_DEBUG, errno, ("Unable to close socket\n")); + } + + sock->sock = -1; } typedef enum { @@ -195,38 +215,40 @@ typedef enum { /** * Open receive socket and bind to it. */ -int connect_receiver (const char *rcv_url, int keepalive_timeout_secs, int *exterr) +int connect_receiver (const char *rcv_url, int keepalive_timeout_secs, + nn_sock_t *sock, int *exterr) { int rcv_timeout; - int sock; + init_nn_sock (sock); *exterr = 0; if (NULL == rcv_url) { return CONN_RCV_ERR_NULL_URL; } - sock = nn_socket (AF_SP, NN_PULL); - if (sock < 0) { + sock->sock = nn_socket (AF_SP, NN_PULL); + if (sock->sock < 0) { *exterr = errno; libpd_log_err (LEVEL_ERROR, errno, ("Unable to create rcv socket %s\n", rcv_url)); return CONN_RCV_ERR_CREATE; } if (keepalive_timeout_secs > 0) { rcv_timeout = keepalive_timeout_secs * 1000; - if (nn_setsockopt (sock, NN_SOL_SOCKET, NN_RCVTIMEO, + if (nn_setsockopt (sock->sock, NN_SOL_SOCKET, NN_RCVTIMEO, &rcv_timeout, sizeof (rcv_timeout)) < 0) { *exterr = errno; libpd_log_err (LEVEL_ERROR, errno, ("Unable to set socket timeout: %s\n", rcv_url)); - shutdown_socket (&sock); + shutdown_socket (sock); return CONN_RCV_ERR_SETOPT; } } - if (nn_bind (sock, rcv_url) < 0) { + sock->eid = nn_bind (sock->sock, rcv_url); + if (sock->eid < 0) { *exterr = errno; libpd_log_err (LEVEL_ERROR, errno, ("Unable to bind to receive socket %s\n", rcv_url)); - shutdown_socket (&sock); + shutdown_socket (sock); return CONN_RCV_ERR_BIND; } - return sock; + return 0; } typedef enum { @@ -255,36 +277,37 @@ typedef enum { /** * Open send socket and connect to it. */ -int connect_sender (const char *send_url, int *exterr) +int connect_sender (const char *send_url, nn_sock_t *sock, int *exterr) { - int sock; int send_timeout = SOCK_SEND_TIMEOUT_MS; + init_nn_sock (sock); *exterr = 0; if (NULL == send_url) { return CONN_SEND_ERR_NULL; } - sock = nn_socket (AF_SP, NN_PUSH); - if (sock < 0) { + sock->sock = nn_socket (AF_SP, NN_PUSH); + if (sock->sock < 0) { *exterr = errno; libpd_log_err (LEVEL_ERROR, errno, ("Unable to create send socket: %s\n", send_url)); return CONN_SEND_ERR_CREATE; } - if (nn_setsockopt (sock, NN_SOL_SOCKET, NN_SNDTIMEO, + if (nn_setsockopt (sock->sock, NN_SOL_SOCKET, NN_SNDTIMEO, &send_timeout, sizeof (send_timeout)) < 0) { *exterr = errno; libpd_log_err (LEVEL_ERROR, errno, ("Unable to set socket timeout: %s\n", send_url)); - shutdown_socket (&sock); + shutdown_socket (sock); return CONN_SEND_ERR_SETOPT; } - if (nn_connect (sock, send_url) < 0) { + sock->eid = nn_connect (sock->sock, send_url); + if (sock->eid < 0) { *exterr = errno; libpd_log_err (LEVEL_ERROR, errno, ("Unable to connect to send socket %s\n", send_url)); - shutdown_socket (&sock); + shutdown_socket (sock); return CONN_SEND_ERR_CONN; } - return sock; + return 0; } static int create_thread (pthread_t *tid, void *(*thread_func) (void*), @@ -414,32 +437,30 @@ int libparodus_init (libpd_instance_t *instance, libpd_cfg_t *libpd_cfg) show_options (libpd_cfg); if (inst->cfg.receive) { libpd_log (LEVEL_INFO, ("LIBPARODUS: connecting receiver to %s\n", inst->client_url)); - err = connect_receiver (inst->client_url, inst->cfg.keepalive_timeout_secs, &oserr); - if (err < 0) { + err = connect_receiver (inst->client_url, + inst->cfg.keepalive_timeout_secs, &inst->rcv_sock, &oserr); + if (err != 0) { SETERR(oserr, LIBPD_ERR_INIT_RCV + err); return CONNECT_ERR (oserr); } - inst->rcv_sock = err; } if (!inst->connect_on_every_send) { libpd_log (LEVEL_INFO, ("LIBPARODUS: connecting sender to %s\n", inst->parodus_url)); - err = connect_sender (inst->parodus_url, &oserr); - if (err < 0) { + err = connect_sender (inst->parodus_url, &inst->send_sock, &oserr); + if (err != 0) { abort_init (inst, "r"); SETERR (oserr, LIBPD_ERR_INIT_SEND + err); return CONNECT_ERR (oserr); } - inst->send_sock = err; } if (inst->cfg.receive) { // We use the stop_rcv_sock to send a stop msg to our own receive socket. - err = connect_sender (inst->client_url, &oserr); - if (err < 0) { + err = connect_sender (inst->client_url, &inst->stop_rcv_sock, &oserr); + if (err != 0) { abort_init (inst, "rs"); SETERR (oserr, LIBPD_ERR_INIT_TERMSOCK + err); return CONNECT_ERR (oserr); } - inst->stop_rcv_sock = err; libpd_log (LEVEL_INFO, ("LIBPARODUS: Opened sockets\n")); err = libpd_qcreate (&inst->wrp_queue, inst->wrp_queue_name, WRP_QUEUE_SIZE, &oserr); if (err != 0) { @@ -491,13 +512,13 @@ int libparodus_init (libpd_instance_t *instance, libpd_cfg_t *libpd_cfg) } // When msg_len is given as -1, then msg is a null terminated string -static int sock_send (int sock, const char *msg, int msg_len, int *exterr) +static int sock_send (nn_sock_t *sock, const char *msg, int msg_len, int *exterr) { int bytes; *exterr = 0; if (msg_len < 0) msg_len = strlen (msg) + 1; // include terminating null - bytes = nn_send (sock, msg, msg_len, 0); + bytes = nn_send (sock->sock, msg, msg_len, 0); if (bytes < 0) { *exterr = errno; libpd_log_err (LEVEL_ERROR, errno, ("Error sending msg\n")); @@ -511,10 +532,10 @@ static int sock_send (int sock, const char *msg, int msg_len, int *exterr) } // returns 0 OK, 1 timedout, -1 error -static int sock_receive (int rcv_sock, raw_msg_t *msg, int *exterr) +static int sock_receive (nn_sock_t *rcv_sock, raw_msg_t *msg, int *exterr) { char *buf = NULL; - msg->len = nn_recv (rcv_sock, &buf, NN_MSG, 0); + msg->len = nn_recv (rcv_sock->sock, &buf, NN_MSG, 0); *exterr = 0; if (msg->len < 0) { @@ -538,7 +559,7 @@ static void libparodus_shutdown__ (__instance_t *inst) inst->run_state = RUN_STATE_DONE; libpd_log (LEVEL_INFO, ("LIBPARODUS: Shutting Down\n")); if (inst->cfg.receive) { - sock_send (inst->stop_rcv_sock, end_msg, -1, &exterr); + sock_send (&inst->stop_rcv_sock, end_msg, -1, &exterr); rtn = pthread_join (inst->wrp_receiver_tid, NULL); if (rtn != 0) { libpd_log_err (LEVEL_ERROR, rtn, ("Error terminating wrp receiver thread\n")); @@ -743,17 +764,16 @@ static int wrp_sock_send (__instance_t *inst, wrp_msg_t *msg) SST (sst_start_total_timing (&sst_times);) if (inst->connect_on_every_send) { - rtn = connect_sender (inst->parodus_url, &inst->exterr); - if (rtn < 0) { + rtn = connect_sender (inst->parodus_url, &inst->send_sock, &inst->exterr); + if (rtn != 0) { free (msg_bytes); pthread_mutex_unlock (&inst->send_mutex); return -0x1200 + rtn; } - inst->send_sock = rtn; } SST (sst_start_send_timing (&sst_times);) - rtn = sock_send (inst->send_sock, (const char *)msg_bytes, msg_len, &inst->exterr); + rtn = sock_send (&inst->send_sock, (const char *)msg_bytes, msg_len, &inst->exterr); SST (sst_update_send_time (&sst_times);) if (inst->connect_on_every_send) { @@ -834,9 +854,8 @@ static void wrp_receiver_reconnect (__instance_t *inst) } sleep (retry_delay); libpd_log (LEVEL_DEBUG, ("Retrying receiver connection\n")); - inst->rcv_sock = connect_receiver - (inst->client_url, inst->cfg.keepalive_timeout_secs, &exterr); - if (inst->rcv_sock < 0) + if (connect_receiver (inst->client_url, + inst->cfg.keepalive_timeout_secs, &inst->rcv_sock, &exterr) != 0) continue; if (send_registration_msg (inst) != 0) continue; @@ -859,7 +878,7 @@ static void *wrp_receiver_thread (void *arg) libpd_log (LEVEL_INFO, ("LIBPARODUS: Starting wrp receiver thread\n")); while (1) { - rtn = sock_receive (inst->rcv_sock, &raw_msg, &exterr); + rtn = sock_receive (&inst->rcv_sock, &raw_msg, &exterr); if (rtn != 0) { if (rtn == 1) { // timed out wrp_receiver_reconnect (inst); diff --git a/src/libparodus_log.h b/src/libparodus_log.h index e8e026e..66bebdb 100644 --- a/src/libparodus_log.h +++ b/src/libparodus_log.h @@ -24,7 +24,7 @@ // if TEST_ENVIRONMENT is not defined, then the macros libpd_log and libpd_log_err // generate nothing -//#define TEST_ENVIRONMENT 1 +#define TEST_ENVIRONMENT 1 #ifndef TEST_ENVIRONMENT #define libpd_log(level,msg) diff --git a/src/libparodus_private.h b/src/libparodus_private.h index 527a811..54e37f4 100644 --- a/src/libparodus_private.h +++ b/src/libparodus_private.h @@ -19,6 +19,12 @@ #include "libparodus.h" +typedef struct { + int sock; + int eid; // nanomsg endpoint ID +} nn_sock_t; + + /** * @brief libparodus internal error rtn codes * diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f969e9e..619df51 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -23,49 +23,53 @@ add_test(NAME NMsgTest COMMAND nmsg) add_executable (nmsg nmsg.c) target_link_libraries (nmsg gcov cunit -lnanomsg -lm -lpthread) +#------------------------------------------------------------------------------- +# test tcp +#------------------------------------------------------------------------------- +add_test(NAME TcpTest COMMAND tcp) +add_executable (tcp ./tcp/tcp.c) +target_link_libraries (tcp gcov cunit -lnanomsg -lm -lpthread) #------------------------------------------------------------------------------- # test libparodus #------------------------------------------------------------------------------- -add_test(NAME LibPDTest COMMAND libpd) -add_executable (libpd - libpd_test.c - ../src/libparodus.c - ../src/libparodus_time.c - ../src/libparodus_queues.c) - -target_link_libraries (libpd - gcov - cunit - -lwrp-c - -lmsgpackc - -ltrower-base64 - -lnanomsg - -lcimplog - -lm - -lpthread - -lrt) +#add_test(NAME LibPDTest COMMAND libpd) +#add_executable (libpd + #libpd_test.c + #../src/libparodus.c + #../src/libparodus_time.c + #../src/libparodus_ +#target_link_libraries (libpd + #gcov + #cunit + #-lwrp-c + #-lmsgpackc + #-ltrower-base64 + #-lnanomsg + #-lm + #-lpthread + #-lrt) #------------------------------------------------------------------------------- # mock code #------------------------------------------------------------------------------- -add_executable(mock_parodus mock_parodus.c) +#add_executable(mock_parodus mock_parodus.c) + +#target_link_libraries (mock_parodus + #-lwrp-c + #-lmsgpackc + #-ltrower-base64 + #-lnanomsg + #-lm + #-lpthread +#) -target_link_libraries (mock_parodus - -lwrp-c - -lmsgpackc - -ltrower-base64 - -lnanomsg - -lcimplog - -lm - -lpthread -) #------------------------------------------------------------------------------- # coverage #------------------------------------------------------------------------------- -add_custom_target(coverage - COMMAND lcov -q --capture --directory ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/libpd.dir/__/src --output-file coverage.info - COMMAND genhtml coverage.info - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +#add_custom_target(coverage + #COMMAND lcov -q --capture --directory ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/libpd.dir/__/src --output-file coverage.info + #COMMAND genhtml coverage.info + #WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/tests/libpd_test.c b/tests/libpd_test.c index ce38e73..41ff1e5 100644 --- a/tests/libpd_test.c +++ b/tests/libpd_test.c @@ -103,10 +103,11 @@ static libpd_instance_t test_instance2; // libparodus functions to be tested extern void test_set_cfg (libpd_cfg_t *new_cfg); extern int flush_wrp_queue (libpd_mq_t wrp_queue, uint32_t delay_ms); -extern int connect_receiver - (const char *rcv_url, int keepalive_timeout_secs, int *exterr); -extern int connect_sender (const char *send_url, int *exterr); -extern void shutdown_socket (int *sock); +extern int connect_receiver (const char *rcv_url, + int keepalive_timeout_secs, nn_sock_t *sock, int *exterr); +extern int connect_sender (const char *send_url, nn_sock_t *sock, + int *exterr); +extern void shutdown_socket (nn_sock_t *sock); extern bool is_auth_received (void); extern int libparodus_receive__ (libpd_mq_t wrp_queue, @@ -700,7 +701,8 @@ void test_1(void) unsigned reply_error_count = 0; int reconnect_count, keep_alive_count; int rtn, exterr; - int test_sock, dup_sock; + nn_sock_t test_sock; + nn_sock_t dup_sock; libpd_mq_t test_queue; wrp_msg_t *wrp_msg; unsigned event_num = 0; @@ -720,31 +722,32 @@ void test_1(void) //test_set_cfg (&cfg); libpd_log (LEVEL_INFO, ("LIBPD_TEST: test connect receiver, good IP\n")); - test_sock = connect_receiver (TEST_RCV_URL, 20, &exterr); - CU_ASSERT (test_sock >= 0) ; - if (test_sock >= 0) + rtn = connect_receiver (TEST_RCV_URL, 20, &test_sock, &exterr); + CU_ASSERT (rtn == 0) ; + if (rtn == 0) shutdown_socket(&test_sock); libpd_log (LEVEL_INFO, ("LIBPD_TEST: test connect receiver, bad IP\n")); - test_sock = connect_receiver (BAD_RCV_URL, 0, &exterr); - CU_ASSERT (test_sock < 0); + rtn = connect_receiver (BAD_RCV_URL, 0, &test_sock, &exterr); + CU_ASSERT (rtn != 0); CU_ASSERT (exterr == EINVAL); libpd_log (LEVEL_INFO, ("LIBPD_TEST: test connect receiver, good IP\n")); - test_sock = connect_receiver (TEST_RCV_URL, 20, &exterr); - CU_ASSERT (test_sock >= 0) ; - libpd_log (LEVEL_INFO, ("LIBPD_TEST: test connect duplicate receiver\n")); - dup_sock = connect_receiver (TEST_RCV_URL, 20, &exterr); - CU_ASSERT (dup_sock < 0); - CU_ASSERT (exterr == EADDRINUSE); - if (test_sock >= 0) + rtn = connect_receiver (TEST_RCV_URL, 20, &test_sock, &exterr); + CU_ASSERT (rtn == 0) ; + if (rtn == 0) { + libpd_log (LEVEL_INFO, ("LIBPD_TEST: test connect duplicate receiver\n")); + rtn = connect_receiver (TEST_RCV_URL, 20, &dup_sock, &exterr); + CU_ASSERT (rtn != 0); + CU_ASSERT (exterr == EADDRINUSE); shutdown_socket(&test_sock); + } libpd_log (LEVEL_INFO, ("LIBPD_TEST: test connect sender, good IP\n")); - test_sock = connect_sender (TEST_SEND_URL, &exterr); - CU_ASSERT (test_sock >= 0) ; - if (test_sock >= 0) + rtn = connect_sender (TEST_SEND_URL, &test_sock, &exterr); + CU_ASSERT (rtn == 0) ; + if (rtn == 0) shutdown_socket(&test_sock); libpd_log (LEVEL_INFO, ("LIBPD_TEST: test connect sender, bad IP\n")); - test_sock = connect_sender (BAD_SEND_URL, &exterr); - CU_ASSERT (test_sock < 0); + rtn = connect_sender (BAD_SEND_URL, &test_sock, &exterr); + CU_ASSERT (rtn != 0); CU_ASSERT (exterr == EINVAL); libpd_log (LEVEL_INFO, ("LIBPD_TEST: test create wrp queue\n")); CU_ASSERT (test_create_wrp_queue (&test_queue, "/TEST_QUEUE", &exterr) == 0); diff --git a/tests/tcp/attr.h b/tests/tcp/attr.h new file mode 100644 index 0000000..9b010e5 --- /dev/null +++ b/tests/tcp/attr.h @@ -0,0 +1,32 @@ +/* + Copyright (c) 2013 Insollo Entertainment, LLC. 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. +*/ + +#ifndef NN_ATTR_INCLUDED +#define NN_ATTR_INCLUDED + +#if defined __GNUC__ || defined __llvm__ +#define NN_UNUSED __attribute__ ((unused)) +#else +#define NN_UNUSED +#endif + +#endif diff --git a/tests/tcp/err.c b/tests/tcp/err.c new file mode 100644 index 0000000..a213c4b --- /dev/null +++ b/tests/tcp/err.c @@ -0,0 +1,213 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + Copyright 2016 Garrett D'Amore + + 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 "err.h" + +#ifdef NN_HAVE_WINDOWS +#include "win.h" +#endif + +#ifdef NN_HAVE_BACKTRACE +#include + +void nn_backtrace_print (void) +{ + void *frames[50]; + int size; + size = backtrace (frames, sizeof (frames) / sizeof (frames[0])); + if (size > 1) { + /* Don't include the frame nn_backtrace_print itself. */ + backtrace_symbols_fd (&frames[1], size-1, fileno (stderr)); + } +} + +/* XXX: Add Windows backtraces */ + +#else +void nn_backtrace_print (void) +{ +} +#endif + +#include +#include + +void nn_err_abort (void) +{ + abort (); +} + +int nn_err_errno (void) +{ + return errno; +} + +const char *nn_err_strerror (int errnum) +{ + switch (errnum) { +#if defined NN_HAVE_WINDOWS + case ENOTSUP: + return "Not supported"; + case EPROTONOSUPPORT: + return "Protocol not supported"; + case ENOBUFS: + return "No buffer space available"; + case ENETDOWN: + return "Network is down"; + case EADDRINUSE: + return "Address in use"; + case EADDRNOTAVAIL: + return "Address not available"; + case ECONNREFUSED: + return "Connection refused"; + case EINPROGRESS: + return "Operation in progress"; + case ENOTSOCK: + return "Not a socket"; + case EAFNOSUPPORT: + return "Address family not supported"; + case EPROTO: + return "Protocol error"; + case EAGAIN: + return "Resource unavailable, try again"; + case EBADF: + return "Bad file descriptor"; + case EINVAL: + return "Invalid argument"; + case EMFILE: + return "Too many open files"; + case EFAULT: + return "Bad address"; + case EACCES: + return "Permission denied"; + case ENETRESET: + return "Connection aborted by network"; + case ENETUNREACH: + return "Network unreachable"; + case EHOSTUNREACH: + return "Host is unreachable"; + case ENOTCONN: + return "The socket is not connected"; + case EMSGSIZE: + return "Message too large"; + case ETIMEDOUT: + return "Timed out"; + case ECONNABORTED: + return "Connection aborted"; + case ECONNRESET: + return "Connection reset"; + case ENOPROTOOPT: + return "Protocol not available"; + case EISCONN: + return "Socket is connected"; +#endif + case ETERM: + return "Nanomsg library was terminated"; + case EFSM: + return "Operation cannot be performed in this state"; + default: +#if defined _MSC_VER +#pragma warning (push) +#pragma warning (disable:4996) +#endif + return strerror (errnum); +#if defined _MSC_VER +#pragma warning (pop) +#endif + } +} + +#ifdef NN_HAVE_WINDOWS + +int nn_err_wsa_to_posix (int wsaerr) +{ + switch (wsaerr) { + case WSAEINPROGRESS: + return EAGAIN; + case WSAEBADF: + return EBADF; + case WSAEINVAL: + return EINVAL; + case WSAEMFILE: + return EMFILE; + case WSAEFAULT: + return EFAULT; + case WSAEPROTONOSUPPORT: + return EPROTONOSUPPORT; + case WSAENOBUFS: + return ENOBUFS; + case WSAENETDOWN: + return ENETDOWN; + case WSAEADDRINUSE: + return EADDRINUSE; + case WSAEADDRNOTAVAIL: + return EADDRNOTAVAIL; + case WSAEAFNOSUPPORT: + return EAFNOSUPPORT; + case WSAEACCES: + return EACCES; + case WSAENETRESET: + return ENETRESET; + case WSAENETUNREACH: + return ENETUNREACH; + case WSAEHOSTUNREACH: + return EHOSTUNREACH; + case WSAENOTCONN: + return ENOTCONN; + case WSAEMSGSIZE: + return EMSGSIZE; + case WSAETIMEDOUT: + return ETIMEDOUT; + case WSAECONNREFUSED: + return ECONNREFUSED; + case WSAECONNABORTED: + return ECONNABORTED; + case WSAECONNRESET: + return ECONNRESET; + case WSAENOTSOCK: + return ENOTSOCK; + case ERROR_BROKEN_PIPE: + return ECONNRESET; + case WSAESOCKTNOSUPPORT: + return ESOCKTNOSUPPORT; + case ERROR_NOT_CONNECTED: + return ENOTCONN; + case ERROR_PIPE_NOT_CONNECTED: + return ENOTCONN; + case ERROR_NO_DATA: + return EPIPE; + default: + nn_assert (0); + } +} + +void nn_win_error (int err, char *buf, size_t bufsize) +{ + DWORD rc = FormatMessageA ( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, (DWORD) err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + buf, (DWORD) bufsize, NULL ); + nn_assert (rc); +} + +#endif diff --git a/tests/tcp/err.h b/tests/tcp/err.h new file mode 100644 index 0000000..3a50470 --- /dev/null +++ b/tests/tcp/err.h @@ -0,0 +1,174 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + Copyright 2016 Garrett D'Amore + + 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. +*/ + +#ifndef NN_ERR_INCLUDED +#define NN_ERR_INCLUDED + +#include +#include +#include + +/* Include nn.h header to define nanomsg-specific error codes. */ +#include "nn.h" + +#include "fast.h" + +#if defined _MSC_VER +#define NN_NORETURN __declspec(noreturn) +#elif defined __GNUC__ +#define NN_NORETURN __attribute__ ((noreturn)) +#else +#define NN_NORETURN +#endif + +/* Same as system assert(). However, under Win32 assert has some deficiencies. + Thus this macro. */ +#define nn_assert(x) \ + do {\ + if (nn_slow (!(x))) {\ + nn_backtrace_print (); \ + fprintf (stderr, "Assertion failed: %s (%s:%d)\n", #x, \ + __FILE__, __LINE__);\ + fflush (stderr);\ + nn_err_abort ();\ + }\ + } while (0) + +#define nn_assert_state(obj, state_name) \ + do {\ + if (nn_slow ((obj)->state != state_name)) {\ + nn_backtrace_print (); \ + fprintf (stderr, \ + "Assertion failed: %d == %s (%s:%d)\n", \ + (obj)->state, #state_name, \ + __FILE__, __LINE__);\ + fflush (stderr);\ + nn_err_abort ();\ + }\ + } while (0) + +/* Checks whether memory allocation was successful. */ +#define alloc_assert(x) \ + do {\ + if (nn_slow (!x)) {\ + nn_backtrace_print (); \ + fprintf (stderr, "Out of memory (%s:%d)\n",\ + __FILE__, __LINE__);\ + fflush (stderr);\ + nn_err_abort ();\ + }\ + } while (0) + +/* Check the condition. If false prints out the errno. */ +#define errno_assert(x) \ + do {\ + if (nn_slow (!(x))) {\ + nn_backtrace_print (); \ + fprintf (stderr, "%s [%d] (%s:%d)\n", nn_err_strerror (errno),\ + (int) errno, __FILE__, __LINE__);\ + fflush (stderr);\ + nn_err_abort ();\ + }\ + } while (0) + +/* Checks whether supplied errno number is an error. */ +#define errnum_assert(cond, err) \ + do {\ + if (nn_slow (!(cond))) {\ + nn_backtrace_print (); \ + fprintf (stderr, "%s [%d] (%s:%d)\n", nn_err_strerror (err),\ + (int) (err), __FILE__, __LINE__);\ + fflush (stderr);\ + nn_err_abort ();\ + }\ + } while (0) + +/* Checks the condition. If false prints out the GetLastError info. */ +#define win_assert(x) \ + do {\ + if (nn_slow (!(x))) {\ + char errstr [256];\ + DWORD errnum = WSAGetLastError ();\ + nn_backtrace_print (); \ + nn_win_error ((int) errnum, errstr, 256);\ + fprintf (stderr, "%s [%d] (%s:%d)\n",\ + errstr, (int) errnum, __FILE__, __LINE__);\ + fflush (stderr);\ + nn_err_abort ();\ + }\ + } while (0) + +/* Checks the condition. If false prints out the WSAGetLastError info. */ +#define wsa_assert(x) \ + do {\ + if (nn_slow (!(x))) {\ + char errstr [256];\ + DWORD errnum = WSAGetLastError ();\ + nn_backtrace_print (); \ + nn_win_error (errnum, errstr, 256);\ + fprintf (stderr, "%s [%d] (%s:%d)\n",\ + errstr, (int) errnum, __FILE__, __LINE__);\ + fflush (stderr);\ + nn_err_abort ();\ + }\ + } while (0) + +/* Assertion-like macros for easier fsm debugging. */ +#define nn_fsm_error(message, state, src, type) \ + do {\ + nn_backtrace_print(); \ + fprintf (stderr, "%s: state=%d source=%d action=%d (%s:%d)\n", \ + message, state, src, type, __FILE__, __LINE__);\ + fflush (stderr);\ + nn_err_abort ();\ + } while (0) + +#define nn_fsm_bad_action(state, src, type) nn_fsm_error(\ + "Unexpected action", state, src, type) +#define nn_fsm_bad_state(state, src, type) nn_fsm_error(\ + "Unexpected state", state, src, type) +#define nn_fsm_bad_source(state, src, type) nn_fsm_error(\ + "Unexpected source", state, src, type) + +/* Compile-time assert. */ +#define CT_ASSERT_HELPER2(prefix, line) prefix##line +#define CT_ASSERT_HELPER1(prefix, line) CT_ASSERT_HELPER2(prefix, line) +#if defined __COUNTER__ +#define CT_ASSERT(x) \ + typedef int CT_ASSERT_HELPER1(ct_assert_,__COUNTER__) [(x) ? 1 : -1] +#else +#define CT_ASSERT(x) \ + typedef int CT_ASSERT_HELPER1(ct_assert_,__LINE__) [(x) ? 1 : -1] +#endif + +NN_NORETURN void nn_err_abort (void); +int nn_err_errno (void); +const char *nn_err_strerror (int errnum); +void nn_backtrace_print (void); + +#ifdef NN_HAVE_WINDOWS +int nn_err_wsa_to_posix (int wsaerr); +void nn_win_error (int err, char *buf, size_t bufsize); +#endif + +#endif diff --git a/tests/tcp/fast.h b/tests/tcp/fast.h new file mode 100644 index 0000000..1dc94a5 --- /dev/null +++ b/tests/tcp/fast.h @@ -0,0 +1,34 @@ +/* + Copyright (c) 2012Martin Sustrik 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. +*/ + +#ifndef NN_FAST_INCLUDED +#define NN_FAST_INCLUDED + +#if defined __GNUC__ || defined __llvm__ +#define nn_fast(x) __builtin_expect ((x), 1) +#define nn_slow(x) __builtin_expect ((x), 0) +#else +#define nn_fast(x) (x) +#define nn_slow(x) (x) +#endif + +#endif diff --git a/tests/tcp/nn.h b/tests/tcp/nn.h new file mode 100644 index 0000000..ce5efe3 --- /dev/null +++ b/tests/tcp/nn.h @@ -0,0 +1,412 @@ +/* + Copyright (c) 2012-2014 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + Copyright 2016 Garrett D'Amore + Copyright (c) 2015-2016 Jack R. Dunaway. 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. +*/ + +#ifndef NN_H_INCLUDED +#define NN_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/* Handle DSO symbol visibility. */ +#if !defined(NN_EXPORT) +# if defined(_WIN32) && !defined(NN_STATIC_LIB) +# if defined NN_SHARED_LIB +# define NN_EXPORT __declspec(dllexport) +# else +# define NN_EXPORT __declspec(dllimport) +# endif +# else +# define NN_EXPORT extern +# endif +#endif + +/******************************************************************************/ +/* ABI versioning support. */ +/******************************************************************************/ + +/* Don't change this unless you know exactly what you're doing and have */ +/* read and understand the following documents: */ +/* www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html */ +/* www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html */ + +/* The current interface version. */ +#define NN_VERSION_CURRENT 5 + +/* The latest revision of the current interface. */ +#define NN_VERSION_REVISION 0 + +/* How many past interface versions are still supported. */ +#define NN_VERSION_AGE 0 + +/******************************************************************************/ +/* Errors. */ +/******************************************************************************/ + +/* A number random enough not to collide with different errno ranges on */ +/* different OSes. The assumption is that error_t is at least 32-bit type. */ +#define NN_HAUSNUMERO 156384712 + +/* On some platforms some standard POSIX errnos are not defined. */ +#ifndef ENOTSUP +#define ENOTSUP (NN_HAUSNUMERO + 1) +#endif +#ifndef EPROTONOSUPPORT +#define EPROTONOSUPPORT (NN_HAUSNUMERO + 2) +#endif +#ifndef ENOBUFS +#define ENOBUFS (NN_HAUSNUMERO + 3) +#endif +#ifndef ENETDOWN +#define ENETDOWN (NN_HAUSNUMERO + 4) +#endif +#ifndef EADDRINUSE +#define EADDRINUSE (NN_HAUSNUMERO + 5) +#endif +#ifndef EADDRNOTAVAIL +#define EADDRNOTAVAIL (NN_HAUSNUMERO + 6) +#endif +#ifndef ECONNREFUSED +#define ECONNREFUSED (NN_HAUSNUMERO + 7) +#endif +#ifndef EINPROGRESS +#define EINPROGRESS (NN_HAUSNUMERO + 8) +#endif +#ifndef ENOTSOCK +#define ENOTSOCK (NN_HAUSNUMERO + 9) +#endif +#ifndef EAFNOSUPPORT +#define EAFNOSUPPORT (NN_HAUSNUMERO + 10) +#endif +#ifndef EPROTO +#define EPROTO (NN_HAUSNUMERO + 11) +#endif +#ifndef EAGAIN +#define EAGAIN (NN_HAUSNUMERO + 12) +#endif +#ifndef EBADF +#define EBADF (NN_HAUSNUMERO + 13) +#endif +#ifndef EINVAL +#define EINVAL (NN_HAUSNUMERO + 14) +#endif +#ifndef EMFILE +#define EMFILE (NN_HAUSNUMERO + 15) +#endif +#ifndef EFAULT +#define EFAULT (NN_HAUSNUMERO + 16) +#endif +#ifndef EACCES +#define EACCES (NN_HAUSNUMERO + 17) +#endif +#ifndef EACCESS +#define EACCESS (EACCES) +#endif +#ifndef ENETRESET +#define ENETRESET (NN_HAUSNUMERO + 18) +#endif +#ifndef ENETUNREACH +#define ENETUNREACH (NN_HAUSNUMERO + 19) +#endif +#ifndef EHOSTUNREACH +#define EHOSTUNREACH (NN_HAUSNUMERO + 20) +#endif +#ifndef ENOTCONN +#define ENOTCONN (NN_HAUSNUMERO + 21) +#endif +#ifndef EMSGSIZE +#define EMSGSIZE (NN_HAUSNUMERO + 22) +#endif +#ifndef ETIMEDOUT +#define ETIMEDOUT (NN_HAUSNUMERO + 23) +#endif +#ifndef ECONNABORTED +#define ECONNABORTED (NN_HAUSNUMERO + 24) +#endif +#ifndef ECONNRESET +#define ECONNRESET (NN_HAUSNUMERO + 25) +#endif +#ifndef ENOPROTOOPT +#define ENOPROTOOPT (NN_HAUSNUMERO + 26) +#endif +#ifndef EISCONN +#define EISCONN (NN_HAUSNUMERO + 27) +#define NN_EISCONN_DEFINED +#endif +#ifndef ESOCKTNOSUPPORT +#define ESOCKTNOSUPPORT (NN_HAUSNUMERO + 28) +#endif + +/* Native nanomsg error codes. */ +#ifndef ETERM +#define ETERM (NN_HAUSNUMERO + 53) +#endif +#ifndef EFSM +#define EFSM (NN_HAUSNUMERO + 54) +#endif + +/* This function retrieves the errno as it is known to the library. */ +/* The goal of this function is to make the code 100% portable, including */ +/* where the library is compiled with certain CRT library (on Windows) and */ +/* linked to an application that uses different CRT library. */ +NN_EXPORT int nn_errno (void); + +/* Resolves system errors and native errors to human-readable string. */ +NN_EXPORT const char *nn_strerror (int errnum); + + +/* Returns the symbol name (e.g. "NN_REQ") and value at a specified index. */ +/* If the index is out-of-range, returns NULL and sets errno to EINVAL */ +/* General usage is to start at i=0 and iterate until NULL is returned. */ +NN_EXPORT const char *nn_symbol (int i, int *value); + +/* Constants that are returned in `ns` member of nn_symbol_properties */ +#define NN_NS_NAMESPACE 0 +#define NN_NS_VERSION 1 +#define NN_NS_DOMAIN 2 +#define NN_NS_TRANSPORT 3 +#define NN_NS_PROTOCOL 4 +#define NN_NS_OPTION_LEVEL 5 +#define NN_NS_SOCKET_OPTION 6 +#define NN_NS_TRANSPORT_OPTION 7 +#define NN_NS_OPTION_TYPE 8 +#define NN_NS_OPTION_UNIT 9 +#define NN_NS_FLAG 10 +#define NN_NS_ERROR 11 +#define NN_NS_LIMIT 12 +#define NN_NS_EVENT 13 +#define NN_NS_STATISTIC 14 + +/* Constants that are returned in `type` member of nn_symbol_properties */ +#define NN_TYPE_NONE 0 +#define NN_TYPE_INT 1 +#define NN_TYPE_STR 2 + +/* Constants that are returned in the `unit` member of nn_symbol_properties */ +#define NN_UNIT_NONE 0 +#define NN_UNIT_BYTES 1 +#define NN_UNIT_MILLISECONDS 2 +#define NN_UNIT_PRIORITY 3 +#define NN_UNIT_BOOLEAN 4 +#define NN_UNIT_MESSAGES 5 +#define NN_UNIT_COUNTER 6 + +/* Structure that is returned from nn_symbol */ +struct nn_symbol_properties { + + /* The constant value */ + int value; + + /* The constant name */ + const char* name; + + /* The constant namespace, or zero for namespaces themselves */ + int ns; + + /* The option type for socket option constants */ + int type; + + /* The unit for the option value for socket option constants */ + int unit; +}; + +/* Fills in nn_symbol_properties structure and returns it's length */ +/* If the index is out-of-range, returns 0 */ +/* General usage is to start at i=0 and iterate until zero is returned. */ +NN_EXPORT int nn_symbol_info (int i, + struct nn_symbol_properties *buf, int buflen); + +/******************************************************************************/ +/* Helper function for shutting down multi-threaded applications. */ +/******************************************************************************/ + +NN_EXPORT void nn_term (void); + +/******************************************************************************/ +/* Zero-copy support. */ +/******************************************************************************/ + +#define NN_MSG ((size_t) -1) + +NN_EXPORT void *nn_allocmsg (size_t size, int type); +NN_EXPORT void *nn_reallocmsg (void *msg, size_t size); +NN_EXPORT int nn_freemsg (void *msg); + +/******************************************************************************/ +/* Socket definition. */ +/******************************************************************************/ + +struct nn_iovec { + void *iov_base; + size_t iov_len; +}; + +struct nn_msghdr { + struct nn_iovec *msg_iov; + int msg_iovlen; + void *msg_control; + size_t msg_controllen; +}; + +struct nn_cmsghdr { + size_t cmsg_len; + int cmsg_level; + int cmsg_type; +}; + +/* Internal stuff. Not to be used directly. */ +NN_EXPORT struct nn_cmsghdr *nn_cmsg_nxthdr_ ( + const struct nn_msghdr *mhdr, + const struct nn_cmsghdr *cmsg); +#define NN_CMSG_ALIGN_(len) \ + (((len) + sizeof (size_t) - 1) & (size_t) ~(sizeof (size_t) - 1)) + +/* POSIX-defined msghdr manipulation. */ + +#define NN_CMSG_FIRSTHDR(mhdr) \ + nn_cmsg_nxthdr_ ((struct nn_msghdr*) (mhdr), NULL) + +#define NN_CMSG_NXTHDR(mhdr, cmsg) \ + nn_cmsg_nxthdr_ ((struct nn_msghdr*) (mhdr), (struct nn_cmsghdr*) (cmsg)) + +#define NN_CMSG_DATA(cmsg) \ + ((unsigned char*) (((struct nn_cmsghdr*) (cmsg)) + 1)) + +/* Extensions to POSIX defined by RFC 3542. */ + +#define NN_CMSG_SPACE(len) \ + (NN_CMSG_ALIGN_ (len) + NN_CMSG_ALIGN_ (sizeof (struct nn_cmsghdr))) + +#define NN_CMSG_LEN(len) \ + (NN_CMSG_ALIGN_ (sizeof (struct nn_cmsghdr)) + (len)) + +/* SP address families. */ +#define AF_SP 1 +#define AF_SP_RAW 2 + +/* Max size of an SP address. */ +#define NN_SOCKADDR_MAX 128 + +/* Socket option levels: Negative numbers are reserved for transports, + positive for socket types. */ +#define NN_SOL_SOCKET 0 + +/* Generic socket options (NN_SOL_SOCKET level). */ +#define NN_LINGER 1 +#define NN_SNDBUF 2 +#define NN_RCVBUF 3 +#define NN_SNDTIMEO 4 +#define NN_RCVTIMEO 5 +#define NN_RECONNECT_IVL 6 +#define NN_RECONNECT_IVL_MAX 7 +#define NN_SNDPRIO 8 +#define NN_RCVPRIO 9 +#define NN_SNDFD 10 +#define NN_RCVFD 11 +#define NN_DOMAIN 12 +#define NN_PROTOCOL 13 +#define NN_IPV4ONLY 14 +#define NN_SOCKET_NAME 15 +#define NN_RCVMAXSIZE 16 +#define NN_MAXTTL 17 + +/* Send/recv options. */ +#define NN_DONTWAIT 1 + +/* Ancillary data. */ +#define PROTO_SP 1 +#define SP_HDR 1 + +NN_EXPORT int nn_socket (int domain, int protocol); +NN_EXPORT int nn_close (int s); +NN_EXPORT int nn_setsockopt (int s, int level, int option, const void *optval, + size_t optvallen); +NN_EXPORT int nn_getsockopt (int s, int level, int option, void *optval, + size_t *optvallen); +NN_EXPORT int nn_bind (int s, const char *addr); +NN_EXPORT int nn_connect (int s, const char *addr); +NN_EXPORT int nn_shutdown (int s, int how); +NN_EXPORT int nn_send (int s, const void *buf, size_t len, int flags); +NN_EXPORT int nn_recv (int s, void *buf, size_t len, int flags); +NN_EXPORT int nn_sendmsg (int s, const struct nn_msghdr *msghdr, int flags); +NN_EXPORT int nn_recvmsg (int s, struct nn_msghdr *msghdr, int flags); + +/******************************************************************************/ +/* Socket mutliplexing support. */ +/******************************************************************************/ + +#define NN_POLLIN 1 +#define NN_POLLOUT 2 + +struct nn_pollfd { + int fd; + short events; + short revents; +}; + +NN_EXPORT int nn_poll (struct nn_pollfd *fds, int nfds, int timeout); + +/******************************************************************************/ +/* Built-in support for devices. */ +/******************************************************************************/ + +NN_EXPORT int nn_device (int s1, int s2); + +/******************************************************************************/ +/* Statistics. */ +/******************************************************************************/ + +/* Transport statistics */ +#define NN_STAT_ESTABLISHED_CONNECTIONS 101 +#define NN_STAT_ACCEPTED_CONNECTIONS 102 +#define NN_STAT_DROPPED_CONNECTIONS 103 +#define NN_STAT_BROKEN_CONNECTIONS 104 +#define NN_STAT_CONNECT_ERRORS 105 +#define NN_STAT_BIND_ERRORS 106 +#define NN_STAT_ACCEPT_ERRORS 107 + +#define NN_STAT_CURRENT_CONNECTIONS 201 +#define NN_STAT_INPROGRESS_CONNECTIONS 202 +#define NN_STAT_CURRENT_EP_ERRORS 203 + +/* The socket-internal statistics */ +#define NN_STAT_MESSAGES_SENT 301 +#define NN_STAT_MESSAGES_RECEIVED 302 +#define NN_STAT_BYTES_SENT 303 +#define NN_STAT_BYTES_RECEIVED 304 +/* Protocol statistics */ +#define NN_STAT_CURRENT_SND_PRIORITY 401 + +NN_EXPORT uint64_t nn_get_statistic (int s, int stat); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tests/tcp/pair.h b/tests/tcp/pair.h new file mode 100644 index 0000000..3409418 --- /dev/null +++ b/tests/tcp/pair.h @@ -0,0 +1,39 @@ +/* + Copyright (c) 2012 Martin Sustrik 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. +*/ + +#ifndef PAIR_H_INCLUDED +#define PAIR_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_PROTO_PAIR 1 + +#define NN_PAIR (NN_PROTO_PAIR * 16 + 0) + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/tests/tcp/pubsub.h b/tests/tcp/pubsub.h new file mode 100644 index 0000000..04abb4f --- /dev/null +++ b/tests/tcp/pubsub.h @@ -0,0 +1,43 @@ +/* + Copyright (c) 2012 Martin Sustrik 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. +*/ + +#ifndef PUBSUB_H_INCLUDED +#define PUBSUB_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_PROTO_PUBSUB 2 + +#define NN_PUB (NN_PROTO_PUBSUB * 16 + 0) +#define NN_SUB (NN_PROTO_PUBSUB * 16 + 1) + +#define NN_SUB_SUBSCRIBE 1 +#define NN_SUB_UNSUBSCRIBE 2 + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/tests/tcp/sleep.c b/tests/tcp/sleep.c new file mode 100644 index 0000000..6ae30bc --- /dev/null +++ b/tests/tcp/sleep.c @@ -0,0 +1,50 @@ +/* + Copyright (c) 2012 Martin Sustrik 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 "sleep.h" +#include "err.h" + +#ifdef NN_HAVE_WINDOWS + +#include "win.h" + +void nn_sleep (int milliseconds) +{ + Sleep (milliseconds); +} + +#else + +#include + +void nn_sleep (int milliseconds) +{ + int rc; + struct timespec ts; + + ts.tv_sec = milliseconds / 1000; + ts.tv_nsec = milliseconds % 1000 * 1000000; + rc = nanosleep (&ts, NULL); + errno_assert (rc == 0); +} + +#endif diff --git a/tests/tcp/sleep.h b/tests/tcp/sleep.h new file mode 100644 index 0000000..de943c4 --- /dev/null +++ b/tests/tcp/sleep.h @@ -0,0 +1,30 @@ +/* + Copyright (c) 2012 Martin Sustrik 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. +*/ + +#ifndef NN_SLEEP_INCLUDED +#define NN_SLEEP_INCLUDED + +/* Platform independent implementation of sleeping. */ + +void nn_sleep (int milliseconds); + +#endif diff --git a/tests/tcp/tcp.c b/tests/tcp/tcp.c new file mode 100644 index 0000000..0f17fde --- /dev/null +++ b/tests/tcp/tcp.c @@ -0,0 +1,237 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + Copyright 2015 Garrett D'Amore + Copyright 2016 Franklin "Snaipe" Mathieu + + 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 "nn.h" +#include "pair.h" +#include "pubsub.h" +#include "tcp.h" + +#include "testutil.h" + +#define DBG(msg) printf msg + +/* Tests TCP transport. */ + +int sc; + +int main (int argc, const char *argv[]) +{ + int rc; + int sb; + int i; + int opt; + size_t sz; + int s1, s2; + void * dummy_buf; + char addr[128]; + char socket_address[128]; + + int port = get_test_port(argc, argv); + + test_addr_from(socket_address, "tcp", "127.0.0.1", port); + + /* Try closing bound but unconnected socket. */ + DBG (("Try closing bound but unconnected socket\n")); + sb = test_socket (AF_SP, NN_PAIR); + test_bind (sb, socket_address); + test_close (sb); + + /* Try closing a TCP socket while it not connected. At the same time + test specifying the local address for the connection. */ + sc = test_socket (AF_SP, NN_PAIR); + test_addr_from(addr, "tcp", "127.0.0.1;127.0.0.1", port); + test_connect (sc, addr); + test_close (sc); + + /* Open the socket anew. */ + sc = test_socket (AF_SP, NN_PAIR); + + /* Check NODELAY socket option. */ + DBG (("Check NODELAY socket option\n")); + sz = sizeof (opt); + rc = nn_getsockopt (sc, NN_TCP, NN_TCP_NODELAY, &opt, &sz); + errno_assert (rc == 0); + nn_assert (sz == sizeof (opt)); + nn_assert (opt == 0); + opt = 2; + rc = nn_setsockopt (sc, NN_TCP, NN_TCP_NODELAY, &opt, sizeof (opt)); + nn_assert (rc < 0 && nn_errno () == EINVAL); + opt = 1; + rc = nn_setsockopt (sc, NN_TCP, NN_TCP_NODELAY, &opt, sizeof (opt)); + errno_assert (rc == 0); + sz = sizeof (opt); + rc = nn_getsockopt (sc, NN_TCP, NN_TCP_NODELAY, &opt, &sz); + errno_assert (rc == 0); + nn_assert (sz == sizeof (opt)); + nn_assert (opt == 1); + + /* Try using invalid address strings. */ + DBG (("Test invalid address strings\n")); + rc = nn_connect (sc, "tcp://*:"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "tcp://*:1000000"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "tcp://*:some_port"); + nn_assert (rc < 0); + rc = nn_connect (sc, "tcp://eth10000;127.0.0.1:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == ENODEV); + rc = nn_connect (sc, "tcp://127.0.0.1"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_bind (sc, "tcp://127.0.0.1:"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_bind (sc, "tcp://127.0.0.1:1000000"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_bind (sc, "tcp://eth10000:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == ENODEV); + rc = nn_connect (sc, "tcp://:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "tcp://-hostname:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "tcp://abc.123.---.#:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "tcp://[::1]:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "tcp://abc.123.:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "tcp://abc...123:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "tcp://.123:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + + /* Connect correctly. Do so before binding the peer socket. */ + test_connect (sc, socket_address); + + /* Leave enough time for at least on re-connect attempt. */ + nn_sleep (200); + + sb = test_socket (AF_SP, NN_PAIR); + test_bind (sb, socket_address); + + /* Ping-pong test. */ + DBG (("Ping pong test\n")); + for (i = 0; i != 100; ++i) { + + test_send (sc, "ABC"); + test_recv (sb, "ABC"); + + test_send (sb, "DEF"); + test_recv (sc, "DEF"); + } + + /* Batch transfer test. */ + for (i = 0; i != 100; ++i) { + test_send (sc, "0123456789012345678901234567890123456789"); + } + for (i = 0; i != 100; ++i) { + test_recv (sb, "0123456789012345678901234567890123456789"); + } + + test_close (sc); + test_close (sb); + + /* Test whether connection rejection is handled decently. */ + DBG (("Test Connection Rejection\n")); + sb = test_socket (AF_SP, NN_PAIR); + test_bind (sb, socket_address); + s1 = test_socket (AF_SP, NN_PAIR); + test_connect (s1, socket_address); + s2 = test_socket (AF_SP, NN_PAIR); + test_connect (s2, socket_address); + nn_sleep (100); + test_close (s2); + test_close (s1); + test_close (sb); + + /* Test two sockets binding to the same address. */ + DBG (("Test rebinding to same address\n")); + sb = test_socket (AF_SP, NN_PAIR); + test_bind (sb, socket_address); + s1 = test_socket (AF_SP, NN_PAIR); + + DBG (("Rebind\n")); + rc = nn_bind (s1, socket_address); + nn_assert (rc < 0); + errno_assert (nn_errno () == EADDRINUSE); + + DBG (("Connect to same address\n")); + sc = test_socket (AF_SP, NN_PAIR); + test_connect (sc, socket_address); + nn_sleep (100); + test_send (sb, "ABC"); + test_recv (sc, "ABC"); + test_close (sb); + test_close (sc); + test_close (s1); + + /* Test NN_RCVMAXSIZE limit */ + sb = test_socket (AF_SP, NN_PAIR); + test_bind (sb, socket_address); + s1 = test_socket (AF_SP, NN_PAIR); + test_connect (s1, socket_address); + opt = 4; + rc = nn_setsockopt (sb, NN_SOL_SOCKET, NN_RCVMAXSIZE, &opt, sizeof (opt)); + nn_assert (rc == 0); + nn_sleep (100); + test_send (s1, "ABC"); + test_recv (sb, "ABC"); + test_send (s1, "0123456789012345678901234567890123456789"); + rc = nn_recv (sb, &dummy_buf, NN_MSG, NN_DONTWAIT); + nn_assert (rc < 0); + errno_assert (nn_errno () == EAGAIN); + test_close (sb); + test_close (s1); + + /* Test that NN_RCVMAXSIZE can be -1, but not lower */ + sb = test_socket (AF_SP, NN_PAIR); + opt = -1; + rc = nn_setsockopt (sb, NN_SOL_SOCKET, NN_RCVMAXSIZE, &opt, sizeof (opt)); + nn_assert (rc >= 0); + opt = -2; + rc = nn_setsockopt (sb, NN_SOL_SOCKET, NN_RCVMAXSIZE, &opt, sizeof (opt)); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + test_close (sb); + + /* Test closing a socket that is waiting to connect. */ + sc = test_socket (AF_SP, NN_PAIR); + test_connect (sc, socket_address); + nn_sleep (100); + test_close (sc); + DBG (("End test tcp\n")); + return 0; +} diff --git a/tests/tcp/tcp.h b/tests/tcp/tcp.h new file mode 100644 index 0000000..1d90776 --- /dev/null +++ b/tests/tcp/tcp.h @@ -0,0 +1,39 @@ +/* + Copyright (c) 2012 Martin Sustrik 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. +*/ + +#ifndef TCP_H_INCLUDED +#define TCP_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_TCP -3 + +#define NN_TCP_NODELAY 1 + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/tests/tcp/testutil.h b/tests/tcp/testutil.h new file mode 100644 index 0000000..7d14a7b --- /dev/null +++ b/tests/tcp/testutil.h @@ -0,0 +1,216 @@ +/* + Copyright (c) 2013 Insollo Entertainment, LLC. All rights reserved. + Copyright 2015 Garrett D'Amore + Copyright 2016 Franklin "Snaipe" Mathieu + + 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. +*/ + +#ifndef TESTUTIL_H_INCLUDED +#define TESTUTIL_H_INCLUDED + +#include +#include "attr.h" +#include "err.c" +#include "sleep.c" + +static int test_socket_impl (char *file, int line, int family, int protocol); +static int test_connect_impl (char *file, int line, int sock, char *address); +static int test_bind_impl (char *file, int line, int sock, char *address); +static void test_close_impl (char *file, int line, int sock); +static void test_send_impl (char *file, int line, int sock, char *data); +static void test_recv_impl (char *file, int line, int sock, char *data); +static void test_drop_impl (char *file, int line, int sock, int err); +static int test_setsockopt_impl (char *file, int line, int sock, int level, + int option, const void *optval, size_t optlen); + +#define test_socket(f, p) test_socket_impl (__FILE__, __LINE__, (f), (p)) +#define test_connect(s, a) test_connect_impl (__FILE__, __LINE__, (s), (a)) +#define test_bind(s, a) test_bind_impl (__FILE__, __LINE__, (s), (a)) +#define test_send(s, d) test_send_impl (__FILE__, __LINE__, (s), (d)) +#define test_recv(s, d) test_recv_impl (__FILE__, __LINE__, (s), (d)) +#define test_drop(s, e) test_drop_impl (__FILE__, __LINE__, (s), (e)) +#define test_close(s) test_close_impl (__FILE__, __LINE__, (s)) +#define test_setsockopt(s, l, o, v, z) test_setsockopt_impl (__FILE__, \ + __LINE__, (s), (l), (o), (v), (z)) + +static int NN_UNUSED test_socket_impl (char *file, int line, int family, + int protocol) +{ + int sock; + + sock = nn_socket (family, protocol); + if (sock == -1) { + fprintf (stderr, "Failed create socket: %s [%d] (%s:%d)\n", + nn_err_strerror (errno), + (int) errno, file, line); + nn_err_abort (); + } + + return sock; +} + +static int NN_UNUSED test_connect_impl (char *file, int line, + int sock, char *address) +{ + int rc; + + rc = nn_connect (sock, address); + if(rc < 0) { + fprintf (stderr, "Failed connect to \"%s\": %s [%d] (%s:%d)\n", + address, + nn_err_strerror (errno), + (int) errno, file, line); + nn_err_abort (); + } + return rc; +} + +static int NN_UNUSED test_bind_impl (char *file, int line, + int sock, char *address) +{ + int rc; + + rc = nn_bind (sock, address); + if(rc < 0) { + fprintf (stderr, "Failed bind to \"%s\": %s [%d] (%s:%d)\n", + address, + nn_err_strerror (errno), + (int) errno, file, line); + nn_err_abort (); + } + return rc; +} + +static int NN_UNUSED test_setsockopt_impl (char *file, int line, + int sock, int level, int option, const void *optval, size_t optlen) +{ + int rc; + + rc = nn_setsockopt (sock, level, option, optval, optlen); + if(rc < 0) { + fprintf (stderr, "Failed set option \"%d\": %s [%d] (%s:%d)\n", + option, + nn_err_strerror (errno), + (int) errno, file, line); + nn_err_abort (); + } + return rc; +} + +static void NN_UNUSED test_close_impl (char *file, int line, int sock) +{ + int rc; + + rc = nn_close (sock); + if ((rc != 0) && (errno != EBADF && errno != ETERM)) { + fprintf (stderr, "Failed to close socket: %s [%d] (%s:%d)\n", + nn_err_strerror (errno), + (int) errno, file, line); + nn_err_abort (); + } +} + +static void NN_UNUSED test_send_impl (char *file, int line, + int sock, char *data) +{ + size_t data_len; + int rc; + + data_len = strlen (data); + + rc = nn_send (sock, data, data_len, 0); + if (rc < 0) { + fprintf (stderr, "Failed to send: %s [%d] (%s:%d)\n", + nn_err_strerror (errno), + (int) errno, file, line); + nn_err_abort (); + } + if (rc != (int)data_len) { + fprintf (stderr, "Data to send is truncated: %d != %d (%s:%d)\n", + rc, (int) data_len, + file, line); + nn_err_abort (); + } +} + +static void NN_UNUSED test_recv_impl (char *file, int line, int sock, char *data) +{ + size_t data_len; + int rc; + char *buf; + + data_len = strlen (data); + /* We allocate plus one byte so that we are sure that message received + has correct length and not truncated */ + buf = malloc (data_len+1); + alloc_assert (buf); + + rc = nn_recv (sock, buf, data_len+1, 0); + if (rc < 0) { + fprintf (stderr, "Failed to recv: %s [%d] (%s:%d)\n", + nn_err_strerror (errno), + (int) errno, file, line); + nn_err_abort (); + } + if (rc != (int)data_len) { + fprintf (stderr, "Received data has wrong length: %d != %d (%s:%d)\n", + rc, (int) data_len, + file, line); + nn_err_abort (); + } + if (memcmp (data, buf, data_len) != 0) { + /* We don't print the data as it may have binary garbage */ + fprintf (stderr, "Received data is wrong (%s:%d)\n", file, line); + nn_err_abort (); + } + + free (buf); +} + +static void NN_UNUSED test_drop_impl (char *file, int line, int sock, int err) +{ + int rc; + char buf[1024]; + + rc = nn_recv (sock, buf, sizeof (buf), 0); + if (rc < 0 && err != errno) { + fprintf (stderr, "Got wrong err to recv: %s [%d != %d] (%s:%d)\n", + nn_err_strerror (errno), + (int) errno, err, file, line); + nn_err_abort (); + } else if (rc >= 0) { + fprintf (stderr, "Did not drop message: [%d bytes] (%s:%d)\n", + rc, file, line); + nn_err_abort (); + } +} + +static int NN_UNUSED get_test_port (int argc, const char *argv[]) +{ + return atoi(argc < 2 ? "5555" : argv[1]); +} + +static void NN_UNUSED test_addr_from (char *out, const char *proto, + const char *ip, int port) +{ + sprintf(out, "%s://%s:%d", proto, ip, port); +} + +#endif