diff options
| -rw-r--r-- | docs/man/nng_options.5.adoc | 67 | ||||
| -rw-r--r-- | docs/man/nng_tcp.7.adoc | 18 | ||||
| -rw-r--r-- | docs/man/nng_tls.7.adoc | 16 | ||||
| -rw-r--r-- | src/core/platform.h | 8 | ||||
| -rw-r--r-- | src/nng.h | 59 | ||||
| -rw-r--r-- | src/platform/posix/posix_aio.h | 2 | ||||
| -rw-r--r-- | src/platform/posix/posix_pipedesc.c | 26 | ||||
| -rw-r--r-- | src/platform/posix/posix_tcp.c | 12 | ||||
| -rw-r--r-- | src/platform/windows/win_tcp.c | 24 | ||||
| -rw-r--r-- | src/supplemental/tls/mbedtls/tls.c | 17 | ||||
| -rw-r--r-- | src/supplemental/tls/tls_api.h | 2 | ||||
| -rw-r--r-- | src/transport/tcp/tcp.c | 100 | ||||
| -rw-r--r-- | src/transport/tls/tls.c | 100 | ||||
| -rw-r--r-- | tests/tcp.c | 102 | ||||
| -rw-r--r-- | tests/tls.c | 112 |
15 files changed, 615 insertions, 50 deletions
diff --git a/docs/man/nng_options.5.adoc b/docs/man/nng_options.5.adoc index d71a7b54..e6ed1f7b 100644 --- a/docs/man/nng_options.5.adoc +++ b/docs/man/nng_options.5.adoc @@ -19,21 +19,23 @@ nng_options - socket, dialer, listener, and pipe options ---- #include <nng/nng.h> -#define NNG_OPT_SOCKNAME "socket-name" -#define NNG_OPT_RAW "raw" -#define NNG_OPT_RECVBUF "recv-buffer" -#define NNG_OPT_SENDBUF "send-buffer" -#define NNG_OPT_RECVFD "recv-fd" -#define NNG_OPT_SENDFD "send-fd" -#define NNG_OPT_RECVTIMEO "recv-timeout" -#define NNG_OPT_SENDTIMEO "send-timeout" -#define NNG_OPT_LOCADDR "local-address" -#define NNG_OPT_REMADDR "remote-address" -#define NNG_OPT_URL "url" -#define NNG_OPT_MAXTTL "ttl-max" -#define NNG_OPT_RECVMAXSZ "recv-size-max" -#define NNG_OPT_RECONNMINT "reconnect-time-min" -#define NNG_OPT_RECONNMAXT "reconnect-time-max" +#define NNG_OPT_SOCKNAME "socket-name" +#define NNG_OPT_RAW "raw" +#define NNG_OPT_RECVBUF "recv-buffer" +#define NNG_OPT_SENDBUF "send-buffer" +#define NNG_OPT_RECVFD "recv-fd" +#define NNG_OPT_SENDFD "send-fd" +#define NNG_OPT_RECVTIMEO "recv-timeout" +#define NNG_OPT_SENDTIMEO "send-timeout" +#define NNG_OPT_LOCADDR "local-address" +#define NNG_OPT_REMADDR "remote-address" +#define NNG_OPT_URL "url" +#define NNG_OPT_MAXTTL "ttl-max" +#define NNG_OPT_RECVMAXSZ "recv-size-max" +#define NNG_OPT_RECONNMINT "reconnect-time-min" +#define NNG_OPT_RECONNMAXT "reconnect-time-max" +#define NNG_OPT_TCP_NODELAY "tcp-nodelay" +#define NNG_OPT_TCP_KEEPALIVE "tcp-nodelay" ---- == DESCRIPTION @@ -286,6 +288,41 @@ Accordingly it can only be used with dialers, listeners, and pipes. NOTE: Some transports will canonify URLs before returning them to the application. +[[NNG_OPT_TCP_NODELAY]] +((`NNG_OPT_TCP_NODELAY`)):: +(`bool``) +This option is used to disable (or enable) the use of Nagle's algorithm +for TCP connections. +When `true` (the default), messages are sent immediately by the underlying +TCP stream without waiting to gather more data. +When `false`, Nagle's algorithm is enabled, and the TCP stream may +wait briefly in attempt to coalesce messages. +Nagle's algorithm is useful on low-bandwidth connections to reduce overhead, +but it comes at a cost to latency. + +NOTE: This setting may apply to transports that are built on top of TCP. +See the transport documentation for each transport for details. + +[[NNG_OPT_TCP_KEEPALIVE]] +((`NNG_OPT_TCP_KEEPALIVE`)):: +(`bool``) +This option is used to enable the sending of keep-alive messages on +the underlying TCP stream. +This option is `false` by default. +When enabled, if no messages are seen for a period of time, then +a zero length TCP message is sent with the ACK flag set in an attempt +to tickle some traffic from the peer. +If none is still seen (after some platform-specific number of retries and +timeouts), then the remote peer is presumed dead, and the connection is closed. + +NOTE: This setting may apply to transports that are built on top of TCP. +See the transport documentation for each transport for details. + +TIP: This option has two purposes. +First, it can be used to detect dead peers on an otherwise quiescent network. +Second, it can be used to keep connection table entries in NAT and other +middleware from being expiring due to lack of activity. + == SEE ALSO <<nng_dialer_getopt.3#,nng_dialer_getopt(3)>>, diff --git a/docs/man/nng_tcp.7.adoc b/docs/man/nng_tcp.7.adoc index 9828b658..1d925b79 100644 --- a/docs/man/nng_tcp.7.adoc +++ b/docs/man/nng_tcp.7.adoc @@ -81,10 +81,22 @@ the actual structure is either of type === Transport Options -The _nng_tcp_ transport has no special options. +((`NNG_OPT_TCP_KEEPALIVE`)):: + +This option is used to configure TCP keep-alives. +The value is of type `bool`, and defaults to `false`. + +((`NNG_OPT_TCP_NODELAY`)):: + +This option is used to configure Nagle's algorithm. +When enabled (`false`), the underlying TCP stream will attempt +to buffer and coalesce messages before sending them on, waiting +a short interval to improve buffering and reduce the overhead +caused by sending too-small messages. +This comes at a cost to latency, and is not recommended with modern +high speed networks. +The value is of type `bool` and defaults to `true`. -NOTE: Options for TCP keepalive, linger, and nodelay are planned. - == SEE ALSO <<nng_sockaddr.5#,nng_sockaddr(5)>>, diff --git a/docs/man/nng_tls.7.adoc b/docs/man/nng_tls.7.adoc index da4adeb6..c783f739 100644 --- a/docs/man/nng_tls.7.adoc +++ b/docs/man/nng_tls.7.adoc @@ -110,6 +110,22 @@ the actual structure is either of type The following transport options are available. Note that setting these must be done before the transport is started. +((`NNG_OPT_TCP_KEEPALIVE`)):: + +This option is used to configure TCP keep-alives. +The value is of type `bool`, and defaults to `false`. + +((`NNG_OPT_TCP_NODELAY`)):: + +This option is used to configure Nagle's algorithm. +When enabled (`false`), the underlying TCP stream will attempt +to buffer and coalesce messages before sending them on, waiting +a short interval to improve buffering and reduce the overhead +caused by sending too-small messages. +This comes at a cost to latency, and is not recommended with modern +high speed networks. +The value is of type `bool` and defaults to `true`. + ((`NNG_OPT_TLS_CONFIG`)):: This option is used on an endpoint to access the underlying TLS diff --git a/src/core/platform.h b/src/core/platform.h index 5666b0ed..d6191038 100644 --- a/src/core/platform.h +++ b/src/core/platform.h @@ -248,6 +248,14 @@ extern int nni_plat_tcp_pipe_peername(nni_plat_tcp_pipe *, nni_sockaddr *); // nni_plat_tcp_pipe_sockname gets the local name. extern int nni_plat_tcp_pipe_sockname(nni_plat_tcp_pipe *, nni_sockaddr *); +// nni_plat_tcp_pipe_set_nodelay sets nodelay, disabling Nagle, according +// to the parameter. true disables Nagle; false enables Nagle. +extern int nni_plat_tcp_pipe_set_nodelay(nni_plat_tcp_pipe *, bool); + +// nni_plat_tcp_pipe_set_keepalive indicates that the TCP pipe should send +// keepalive probes. Tuning of these keepalives is current unsupported. +extern int nni_plat_tcp_pipe_set_keepalive(nni_plat_tcp_pipe *, bool); + // nni_plat_tcp_ntop obtains the IP address for the socket (enclosing it // in brackets if it is IPv6) and port. Enough space for both must // be present (48 bytes and 6 bytes each), although if either is NULL then @@ -13,7 +13,7 @@ // NNG (nanomsg-next-gen) is an improved implementation of the SP protocols. // The APIs have changed, and there is no attempt to provide API compatibility -// with legacy libnanomsg. This file defines the library consumer-facing +// with legacy libnanomsg. This file defines the library consumer-facing // Public API. Use of definitions or declarations not found in this header // file is specfically unsupported and strongly discouraged. @@ -26,10 +26,10 @@ extern "C" { #include <stdint.h> // NNG_DECL is used on declarations to deal with scope. -// For building Windows DLLs, it should be the appropriate -// __declspec(). (We recommend *not* building this library -// as a DLL, but instead linking it statically for your projects -// to minimize questions about link dependencies later.) +// For building Windows DLLs, it should be the appropriate __declspec(). +// (We recommend *not* building this library as a DLL, but instead linking +// it statically for your project to minimize concerns about link +// dependencies later.) #ifndef NNG_DECL #if defined(_WIN32) && !defined(NNG_STATIC_LIB) #if defined(NNG_SHARED_LIB) @@ -51,8 +51,11 @@ extern "C" { #define NNG_MINOR_VERSION 9 #define NNG_PATCH_VERSION 0 -// Maximum length of a socket address. This includes the terminating NUL. +// Maximum length of a socket address. This includes the terminating NUL. // This limit is built into other implementations, so do not change it. +// Note that some transports are quite happy to let you use addresses +// in excess of this, but if you do you may not be able to communicate +// with other implementations. #define NNG_MAXADDRLEN (128) // Types common to nng. @@ -67,7 +70,7 @@ typedef struct nng_snapshot nng_snapshot; typedef struct nng_stat nng_stat; typedef struct nng_aio nng_aio; -// Some address details. This is in some ways like a traditional sockets +// Some address details. This is in some ways like a traditional sockets // sockaddr, but we have our own to cope with our unique families, etc. // The details of this structure are directly exposed to applications. // These structures can be obtained via property lookups, etc. @@ -156,7 +159,7 @@ NNG_DECL void nng_fini(void); // resources. NNG_DECL int nng_close(nng_socket); -// nng_closeall closes all open sockets. Do not call this from +// nng_closeall closes all open sockets. Do not call this from // a library; it will affect all sockets. NNG_DECL void nng_closeall(void); @@ -614,31 +617,51 @@ enum nng_flag_enum { // on listeners. #define NNG_OPT_TLS_SERVER_NAME "tls-server-name" -// NNG_OPT_TLS_VERIFIED returns a single integer, indicating whether the peer -// has been verified (1) or not (0). Typically this is read-only, and only -// available for pipes. This option may return incorrect results if peer -// authentication is disabled with `NNG_TLS_AUTH_MODE_NONE`. +// NNG_OPT_TLS_VERIFIED returns a boolean indicating whether the peer has +// been verified (true) or not (false). Typically this is read-only, and +// only available for pipes. This option may return incorrect results if +// peer authentication is disabled with `NNG_TLS_AUTH_MODE_NONE`. #define NNG_OPT_TLS_VERIFIED "tls-verified" +// TCP options. These may be supported on various transports that use +// TCP underneath such as TLS, or not. + +// TCP nodelay disables the use of Nagle, so that messages are sent +// as soon as data is available. This tends to reduce latency, but +// can come at the cost of extra messages being sent, and may have +// a detrimental effect on performance. For most uses, we recommend +// enabling this. (Disable it if you are on a very slow network.) +// This is a boolean. +#define NNG_OPT_TCP_NODELAY "tcp-nodelay" + +// TCP keepalive causes the underlying transport to send keep-alive +// messages, and keep the session active. Keepalives are zero length +// messages with the ACK flag turned on. If we don't get an ACK back, +// then we know the other side is gone. This is useful for detecting +// dead peers, and is also used to prevent disconnections caused by +// middle boxes thinking the session has gone idle (e.g. keeping NAT +// state current). This is a boolean. +#define NNG_OPT_TCP_KEEPALIVE "tcp-keepalive" + // XXX: TBD: priorities, ipv4only, TCP options -// Statistics. These are for informational purposes only, and subject -// to change without notice. The API for accessing these is stable, +// Statistics. These are for informational purposes only, and subject +// to change without notice. The API for accessing these is stable, // but the individual statistic names, values, and meanings are all // subject to change. -// nng_snapshot_create creates a statistics snapshot. The snapshot +// nng_snapshot_create creates a statistics snapshot. The snapshot // object must be deallocated expressly by the user, and may persist beyond -// the lifetime of any socket object used to update it. Note that the +// the lifetime of any socket object used to update it. Note that the // values of the statistics are initially unset. // NNG_DECL int nng_snapshot_create(nng_socket, nng_snapshot **); -// nng_snapshot_free frees a snapshot object. All statistic objects +// nng_snapshot_free frees a snapshot object. All statistic objects // contained therein are destroyed as well. // NNG_DECL void nng_snapshot_free(nng_snapshot *); // nng_snapshot_update updates a snapshot of all the statistics -// relevant to a particular socket. All prior values are overwritten. +// relevant to a particular socket. All prior values are overwritten. // NNG_DECL int nng_snapshot_update(nng_snapshot *); // nng_snapshot_next is used to iterate over the individual statistic diff --git a/src/platform/posix/posix_aio.h b/src/platform/posix/posix_aio.h index 2073f6d6..15dae06c 100644 --- a/src/platform/posix/posix_aio.h +++ b/src/platform/posix/posix_aio.h @@ -31,6 +31,8 @@ extern void nni_posix_pipedesc_send(nni_posix_pipedesc *, nni_aio *); extern void nni_posix_pipedesc_close(nni_posix_pipedesc *); extern int nni_posix_pipedesc_peername(nni_posix_pipedesc *, nni_sockaddr *); extern int nni_posix_pipedesc_sockname(nni_posix_pipedesc *, nni_sockaddr *); +extern int nni_posix_pipedesc_set_nodelay(nni_posix_pipedesc *, bool); +extern int nni_posix_pipedesc_set_keepalive(nni_posix_pipedesc *, bool); extern int nni_posix_epdesc_init(nni_posix_epdesc **); extern void nni_posix_epdesc_set_local(nni_posix_epdesc *, void *, size_t); diff --git a/src/platform/posix/posix_pipedesc.c b/src/platform/posix/posix_pipedesc.c index 72251372..f9cbb94b 100644 --- a/src/platform/posix/posix_pipedesc.c +++ b/src/platform/posix/posix_pipedesc.c @@ -16,6 +16,8 @@ #include <errno.h> #include <fcntl.h> +#include <netinet/in.h> +#include <netinet/tcp.h> #include <poll.h> #include <stdbool.h> #include <stdlib.h> @@ -335,6 +337,30 @@ nni_posix_pipedesc_sockname(nni_posix_pipedesc *pd, nni_sockaddr *sa) } int +nni_posix_pipedesc_set_nodelay(nni_posix_pipedesc *pd, bool nodelay) +{ + int val = nodelay ? 1 : 0; + + if (setsockopt(pd->node.fd, IPPROTO_TCP, TCP_NODELAY, &val, + sizeof(val)) != 0) { + return (nni_plat_errno(errno)); + } + return (0); +} + +int +nni_posix_pipedesc_set_keepalive(nni_posix_pipedesc *pd, bool keep) +{ + int val = keep ? 1 : 0; + + if (setsockopt(pd->node.fd, SOL_SOCKET, SO_KEEPALIVE, &val, + sizeof(val)) != 0) { + return (nni_plat_errno(errno)); + } + return (0); +} + +int nni_posix_pipedesc_init(nni_posix_pipedesc **pdp, int fd) { nni_posix_pipedesc *pd; diff --git a/src/platform/posix/posix_tcp.c b/src/platform/posix/posix_tcp.c index 9caa157f..81ec330b 100644 --- a/src/platform/posix/posix_tcp.c +++ b/src/platform/posix/posix_tcp.c @@ -125,6 +125,18 @@ nni_plat_tcp_pipe_sockname(nni_plat_tcp_pipe *p, nni_sockaddr *sa) } int +nni_plat_tcp_pipe_set_keepalive(nni_plat_tcp_pipe *p, bool v) +{ + return (nni_posix_pipedesc_set_keepalive((void *) p, v)); +} + +int +nni_plat_tcp_pipe_set_nodelay(nni_plat_tcp_pipe *p, bool v) +{ + return (nni_posix_pipedesc_set_nodelay((void *) p, v)); +} + +int nni_plat_tcp_ntop(const nni_sockaddr *sa, char *ipstr, char *portstr) { const void *ap; diff --git a/src/platform/windows/win_tcp.c b/src/platform/windows/win_tcp.c index 17f7845d..4d7d6027 100644 --- a/src/platform/windows/win_tcp.c +++ b/src/platform/windows/win_tcp.c @@ -243,6 +243,30 @@ nni_plat_tcp_pipe_sockname(nni_plat_tcp_pipe *pipe, nni_sockaddr *sa) return (0); } +int +nni_plat_tcp_pipe_set_nodelay(nni_plat_tcp_pipe *pipe, bool val) +{ + BOOL b; + b = val ? TRUE : FALSE; + if (setsockopt(pipe->s, IPPROTO_TCP, TCP_NODELAY, (void *) &b, + sizeof(b)) != 0) { + return (nni_win_error(WSAGetLastError())); + } + return (0); +} + +int +nni_plat_tcp_pipe_set_keepalive(nni_plat_tcp_pipe *pipe, bool val) +{ + BOOL b; + b = val ? TRUE : FALSE; + if (setsockopt(pipe->s, SOL_SOCKET, SO_KEEPALIVE, (void *) &b, + sizeof(b)) != 0) { + return (nni_win_error(WSAGetLastError())); + } + return (0); +} + void nni_plat_tcp_pipe_fini(nni_plat_tcp_pipe *pipe) { diff --git a/src/supplemental/tls/mbedtls/tls.c b/src/supplemental/tls/mbedtls/tls.c index c2a0fb52..5246e06c 100644 --- a/src/supplemental/tls/mbedtls/tls.c +++ b/src/supplemental/tls/mbedtls/tls.c @@ -311,6 +311,11 @@ nni_tls_init(nni_tls **tpp, nng_tls_config *cfg, nni_plat_tcp_pipe *tcp) nni_tls *tp; int rv; + // During the handshake, disable Nagle to shorten the + // negotiation. Once things are set up the caller can + // re-enable Nagle if so desired. + (void) nni_plat_tcp_pipe_set_nodelay(tcp, true); + if ((tp = NNI_ALLOC_STRUCT(tp)) == NULL) { return (NNG_ENOMEM); } @@ -606,6 +611,18 @@ nni_tls_sockname(nni_tls *tp, nni_sockaddr *sa) return (nni_plat_tcp_pipe_sockname(tp->tcp, sa)); } +int +nni_tls_set_nodelay(nni_tls *tp, bool val) +{ + return (nni_plat_tcp_pipe_set_nodelay(tp->tcp, val)); +} + +int +nni_tls_set_keepalive(nni_tls *tp, bool val) +{ + return (nni_plat_tcp_pipe_set_keepalive(tp->tcp, val)); +} + static void nni_tls_do_handshake(nni_tls *tp) { diff --git a/src/supplemental/tls/tls_api.h b/src/supplemental/tls/tls_api.h index 24d8e578..8a40bcfb 100644 --- a/src/supplemental/tls/tls_api.h +++ b/src/supplemental/tls/tls_api.h @@ -38,6 +38,8 @@ extern void nni_tls_send(nni_tls *, nng_aio *); extern void nni_tls_recv(nni_tls *, nng_aio *); extern int nni_tls_sockname(nni_tls *, nni_sockaddr *); extern int nni_tls_peername(nni_tls *, nni_sockaddr *); +extern int nni_tls_set_nodelay(nni_tls *, bool); +extern int nni_tls_set_keepalive(nni_tls *, bool); // nni_tls_verified returns true if the peer, or false if the peer did not // verify. (During the handshake phase, the peer is not verified, so this diff --git a/src/transport/tcp/tcp.c b/src/transport/tcp/tcp.c index a551c5be..d7920f72 100644 --- a/src/transport/tcp/tcp.c +++ b/src/transport/tcp/tcp.c @@ -26,6 +26,8 @@ struct nni_tcp_pipe { uint16_t peer; uint16_t proto; size_t rcvmax; + bool nodelay; + bool keepalive; nni_list recvq; nni_list sendq; @@ -48,6 +50,8 @@ struct nni_tcp_ep { nni_plat_tcp_ep *tep; uint16_t proto; size_t rcvmax; + bool nodelay; + bool keepalive; nni_aio * aio; nni_aio * user_aio; nni_url * url; @@ -123,9 +127,17 @@ nni_tcp_pipe_init(nni_tcp_pipe **pipep, nni_tcp_ep *ep, void *tpp) nni_aio_list_init(&p->recvq); nni_aio_list_init(&p->sendq); - p->proto = ep->proto; - p->rcvmax = ep->rcvmax; - p->tpp = tpp; + p->proto = ep->proto; + p->rcvmax = ep->rcvmax; + p->nodelay = ep->nodelay; + p->keepalive = ep->keepalive; + p->tpp = tpp; + + // We try to set the nodelay and keepalive, but if these fail for + // some reason, its not really fatal to the communication channel. + // So ignore the return values. + (void) nni_plat_tcp_pipe_set_nodelay(tpp, p->nodelay); + (void) nni_plat_tcp_pipe_set_keepalive(tpp, p->keepalive); *pipep = p; return (0); @@ -502,6 +514,20 @@ nni_tcp_pipe_getopt_remaddr(void *arg, void *v, size_t *szp, int typ) return (rv); } +static int +nni_tcp_pipe_getopt_keepalive(void *arg, void *v, size_t *szp, int typ) +{ + nni_tcp_pipe *p = arg; + return (nni_copyout_bool(p->keepalive, v, szp, typ)); +} + +static int +nni_tcp_pipe_getopt_nodelay(void *arg, void *v, size_t *szp, int typ) +{ + nni_tcp_pipe *p = arg; + return (nni_copyout_bool(p->nodelay, v, szp, typ)); +} + // Note that the url *must* be in a modifiable buffer. static void nni_tcp_pipe_start(void *arg, nni_aio *aio) @@ -624,8 +650,10 @@ nni_tcp_ep_init(void **epp, nni_url *url, nni_sock *sock, int mode) nni_tcp_ep_fini(ep); return (rv); } - ep->proto = nni_sock_proto(sock); - ep->mode = mode; + ep->proto = nni_sock_proto(sock); + ep->mode = mode; + ep->nodelay = true; + ep->keepalive = false; *epp = ep; return (0); @@ -767,6 +795,46 @@ nni_tcp_ep_setopt_recvmaxsz(void *arg, const void *v, size_t sz, int typ) } static int +nni_tcp_ep_setopt_nodelay(void *arg, const void *v, size_t sz, int typ) +{ + nni_tcp_ep *ep = arg; + bool val; + int rv; + rv = nni_copyin_bool(&val, v, sz, typ); + if ((rv == 0) && (ep != NULL)) { + ep->nodelay = val; + } + return (rv); +} + +static int +nni_tcp_ep_getopt_nodelay(void *arg, void *v, size_t *szp, int typ) +{ + nni_tcp_ep *ep = arg; + return (nni_copyout_bool(ep->nodelay, v, szp, typ)); +} + +static int +nni_tcp_ep_setopt_keepalive(void *arg, const void *v, size_t sz, int typ) +{ + nni_tcp_ep *ep = arg; + bool val; + int rv; + rv = nni_copyin_bool(&val, v, sz, typ); + if ((rv == 0) && (ep != NULL)) { + ep->keepalive = val; + } + return (rv); +} + +static int +nni_tcp_ep_getopt_keepalive(void *arg, void *v, size_t *szp, int typ) +{ + nni_tcp_ep *ep = arg; + return (nni_copyout_bool(ep->keepalive, v, szp, typ)); +} + +static int nni_tcp_ep_getopt_url(void *arg, void *v, size_t *szp, int typ) { nni_tcp_ep *ep = arg; @@ -800,6 +868,16 @@ static nni_tran_pipe_option nni_tcp_pipe_options[] = { .po_type = NNI_TYPE_SOCKADDR, .po_getopt = nni_tcp_pipe_getopt_remaddr, }, + { + .po_name = NNG_OPT_TCP_KEEPALIVE, + .po_type = NNI_TYPE_BOOL, + .po_getopt = nni_tcp_pipe_getopt_keepalive, + }, + { + .po_name = NNG_OPT_TCP_NODELAY, + .po_type = NNI_TYPE_BOOL, + .po_getopt = nni_tcp_pipe_getopt_nodelay, + }, // terminate list { .po_name = NULL, @@ -829,6 +907,18 @@ static nni_tran_ep_option nni_tcp_ep_options[] = { .eo_getopt = nni_tcp_ep_getopt_url, .eo_setopt = NULL, }, + { + .eo_name = NNG_OPT_TCP_NODELAY, + .eo_type = NNI_TYPE_BOOL, + .eo_getopt = nni_tcp_ep_getopt_nodelay, + .eo_setopt = nni_tcp_ep_setopt_nodelay, + }, + { + .eo_name = NNG_OPT_TCP_KEEPALIVE, + .eo_type = NNI_TYPE_BOOL, + .eo_getopt = nni_tcp_ep_getopt_keepalive, + .eo_setopt = nni_tcp_ep_setopt_keepalive, + }, // terminate list { .eo_name = NULL, diff --git a/src/transport/tls/tls.c b/src/transport/tls/tls.c index 2ea14e93..73d1a29b 100644 --- a/src/transport/tls/tls.c +++ b/src/transport/tls/tls.c @@ -31,6 +31,8 @@ struct nni_tls_pipe { uint16_t peer; uint16_t proto; size_t rcvmax; + bool nodelay; + bool keepalive; nni_list sendq; nni_list recvq; @@ -62,6 +64,8 @@ struct nni_tls_ep { nng_sockaddr bsa; nni_url * url; int mode; + bool nodelay; + bool keepalive; }; static void nni_tls_pipe_dorecv(nni_tls_pipe *); @@ -132,9 +136,11 @@ nni_tls_pipe_init(nni_tls_pipe **pipep, nni_tls_ep *ep, void *tpp) nni_aio_list_init(&p->recvq); nni_aio_list_init(&p->sendq); - p->proto = ep->proto; - p->rcvmax = ep->rcvmax; - p->tcp = tcp; + p->proto = ep->proto; + p->rcvmax = ep->rcvmax; + p->tcp = tcp; + p->keepalive = ep->keepalive; + p->nodelay = ep->nodelay; *pipep = p; return (0); @@ -207,6 +213,12 @@ nni_tls_pipe_nego_cb(void *arg) NNI_GET16(&p->rxlen[4], p->peer); done: + if (rv == 0) { + // These can fail. Note that the TLS stack automatically + // starts out in NODELAY to make the handshake performant. + (void) nni_tls_set_nodelay(p->tls, p->nodelay); + (void) nni_tls_set_keepalive(p->tls, p->keepalive); + } if ((aio = p->user_negaio) != NULL) { p->user_negaio = NULL; nni_aio_finish(aio, rv, 0); @@ -510,6 +522,20 @@ nni_tls_pipe_getopt_remaddr(void *arg, void *v, size_t *szp, int typ) return (rv); } +static int +nni_tls_pipe_getopt_keepalive(void *arg, void *v, size_t *szp, int typ) +{ + nni_tls_pipe *p = arg; + return (nni_copyout_bool(p->keepalive, v, szp, typ)); +} + +static int +nni_tls_pipe_getopt_nodelay(void *arg, void *v, size_t *szp, int typ) +{ + nni_tls_pipe *p = arg; + return (nni_copyout_bool(p->nodelay, v, szp, typ)); +} + static void nni_tls_pipe_start(void *arg, nni_aio *aio) { @@ -628,8 +654,10 @@ nni_tls_ep_init(void **epp, nni_url *url, nni_sock *sock, int mode) return (NNG_ENOMEM); } nni_mtx_init(&ep->mtx); - ep->url = url; - ep->mode = mode; + ep->url = url; + ep->mode = mode; + ep->keepalive = false; + ep->nodelay = true; if (((rv = nni_plat_tcp_ep_init(&ep->tep, &lsa, &rsa, mode)) != 0) || ((rv = nni_tls_config_init(&ep->cfg, tlsmode)) != 0) || @@ -770,6 +798,46 @@ nni_tls_ep_connect(void *arg, nni_aio *aio) } static int +nni_tls_ep_setopt_nodelay(void *arg, const void *v, size_t sz, int typ) +{ + nni_tls_ep *ep = arg; + bool val; + int rv; + rv = nni_copyin_bool(&val, v, sz, typ); + if ((rv == 0) && (ep != NULL)) { + ep->nodelay = val; + } + return (rv); +} + +static int +nni_tls_ep_getopt_nodelay(void *arg, void *v, size_t *szp, int typ) +{ + nni_tls_ep *ep = arg; + return (nni_copyout_bool(ep->nodelay, v, szp, typ)); +} + +static int +nni_tls_ep_setopt_keepalive(void *arg, const void *v, size_t sz, int typ) +{ + nni_tls_ep *ep = arg; + bool val; + int rv; + rv = nni_copyin_bool(&val, v, sz, typ); + if ((rv == 0) && (ep != NULL)) { + ep->keepalive = val; + } + return (rv); +} + +static int +nni_tls_ep_getopt_keepalive(void *arg, void *v, size_t *szp, int typ) +{ + nni_tls_ep *ep = arg; + return (nni_copyout_bool(ep->keepalive, v, szp, typ)); +} + +static int nni_tls_ep_getopt_url(void *arg, void *v, size_t *szp, int typ) { nni_tls_ep *ep = arg; @@ -928,6 +996,16 @@ static nni_tran_pipe_option nni_tls_pipe_options[] = { .po_type = NNI_TYPE_BOOL, .po_getopt = tls_getopt_verified, }, + { + .po_name = NNG_OPT_TCP_KEEPALIVE, + .po_type = NNI_TYPE_BOOL, + .po_getopt = nni_tls_pipe_getopt_keepalive, + }, + { + .po_name = NNG_OPT_TCP_NODELAY, + .po_type = NNI_TYPE_BOOL, + .po_getopt = nni_tls_pipe_getopt_nodelay, + }, // terminate list { .po_name = NULL, @@ -987,6 +1065,18 @@ static nni_tran_ep_option nni_tls_ep_options[] = { .eo_getopt = NULL, .eo_setopt = tls_setopt_server_name, }, + { + .eo_name = NNG_OPT_TCP_NODELAY, + .eo_type = NNI_TYPE_BOOL, + .eo_getopt = nni_tls_ep_getopt_nodelay, + .eo_setopt = nni_tls_ep_setopt_nodelay, + }, + { + .eo_name = NNG_OPT_TCP_KEEPALIVE, + .eo_type = NNI_TYPE_BOOL, + .eo_getopt = nni_tls_ep_getopt_keepalive, + .eo_setopt = nni_tls_ep_setopt_keepalive, + }, // terminate list { .eo_name = NULL, diff --git a/tests/tcp.c b/tests/tcp.c index 58b4f15c..2677222d 100644 --- a/tests/tcp.c +++ b/tests/tcp.c @@ -27,6 +27,7 @@ check_props_v4(nng_msg *msg) size_t z; nng_sockaddr la; nng_sockaddr ra; + bool b; p = nng_msg_get_pipe(msg); So(p > 0); @@ -48,6 +49,12 @@ check_props_v4(nng_msg *msg) z = 1; So(nng_pipe_getopt(p, NNG_OPT_REMADDR, &ra, &z) == NNG_EINVAL); + So(nng_pipe_getopt_bool(p, NNG_OPT_TCP_KEEPALIVE, &b) == 0); + So(b == false); // default + + So(nng_pipe_getopt_bool(p, NNG_OPT_TCP_NODELAY, &b) == 0); + So(b == true); // default + return (0); } @@ -118,5 +125,100 @@ TestMain("TCP Transport", { NNG_EADDRINVAL); }); + Convey("No delay option", { + nng_socket s; + nng_dialer d; + nng_listener l; + bool v; + int x; + + So(nng_pair_open(&s) == 0); + Reset({ nng_close(s); }); + So(nng_getopt_bool(s, NNG_OPT_TCP_NODELAY, &v) == NNG_ENOTSUP); + So(nng_dialer_create(&d, s, "tcp://127.0.0.1:4999") == 0); + So(nng_dialer_getopt_bool(d, NNG_OPT_TCP_NODELAY, &v) == 0); + So(v == true); + So(nng_dialer_setopt_bool(d, NNG_OPT_TCP_NODELAY, false) == 0); + So(nng_dialer_getopt_bool(d, NNG_OPT_TCP_NODELAY, &v) == 0); + So(v == false); + So(nng_dialer_getopt_int(d, NNG_OPT_TCP_NODELAY, &x) == + NNG_EBADTYPE); + x = 0; + So(nng_dialer_setopt_int(d, NNG_OPT_TCP_NODELAY, x) == + NNG_EBADTYPE); + // This assumes sizeof (bool) != sizeof (int) + So(nng_dialer_setopt(d, NNG_OPT_TCP_NODELAY, &x, sizeof(x)) == + NNG_EINVAL); + + So(nng_listener_create(&l, s, "tcp://127.0.0.1:4999") == 0); + So(nng_listener_getopt_bool(l, NNG_OPT_TCP_NODELAY, &v) == 0); + So(v == true); + x = 0; + So(nng_listener_setopt_int(l, NNG_OPT_TCP_NODELAY, x) == + NNG_EBADTYPE); + // This assumes sizeof (bool) != sizeof (int) + So(nng_listener_setopt( + l, NNG_OPT_TCP_NODELAY, &x, sizeof(x)) == NNG_EINVAL); + + nng_dialer_close(d); + nng_listener_close(l); + + // Make sure socket wide defaults apply. + So(nng_setopt_bool(s, NNG_OPT_TCP_NODELAY, true) == 0); + v = false; + So(nng_getopt_bool(s, NNG_OPT_TCP_NODELAY, &v) == 0); + So(v == true); + So(nng_setopt_bool(s, NNG_OPT_TCP_NODELAY, false) == 0); + So(nng_dialer_create(&d, s, "tcp://127.0.0.1:4999") == 0); + So(nng_dialer_getopt_bool(d, NNG_OPT_TCP_NODELAY, &v) == 0); + So(v == false); + }); + + Convey("Keepalive option", { + nng_socket s; + nng_dialer d; + nng_listener l; + bool v; + int x; + + So(nng_pair_open(&s) == 0); + Reset({ nng_close(s); }); + So(nng_getopt_bool(s, NNG_OPT_TCP_KEEPALIVE, &v) == + NNG_ENOTSUP); + So(nng_dialer_create(&d, s, "tcp://127.0.0.1:4999") == 0); + So(nng_dialer_getopt_bool(d, NNG_OPT_TCP_KEEPALIVE, &v) == 0); + So(v == false); + So(nng_dialer_setopt_bool(d, NNG_OPT_TCP_KEEPALIVE, true) == + 0); + So(nng_dialer_getopt_bool(d, NNG_OPT_TCP_KEEPALIVE, &v) == 0); + So(v == true); + So(nng_dialer_getopt_int(d, NNG_OPT_TCP_KEEPALIVE, &x) == + NNG_EBADTYPE); + x = 1; + So(nng_dialer_setopt_int(d, NNG_OPT_TCP_KEEPALIVE, x) == + NNG_EBADTYPE); + + So(nng_listener_create(&l, s, "tcp://127.0.0.1:4999") == 0); + So(nng_listener_getopt_bool(l, NNG_OPT_TCP_KEEPALIVE, &v) == + 0); + So(v == false); + x = 1; + So(nng_listener_setopt_int(l, NNG_OPT_TCP_KEEPALIVE, x) == + NNG_EBADTYPE); + + nng_dialer_close(d); + nng_listener_close(l); + + // Make sure socket wide defaults apply. + So(nng_setopt_bool(s, NNG_OPT_TCP_KEEPALIVE, false) == 0); + v = true; + So(nng_getopt_bool(s, NNG_OPT_TCP_KEEPALIVE, &v) == 0); + So(v == false); + So(nng_setopt_bool(s, NNG_OPT_TCP_KEEPALIVE, true) == 0); + So(nng_dialer_create(&d, s, "tcp://127.0.0.1:4999") == 0); + So(nng_dialer_getopt_bool(d, NNG_OPT_TCP_KEEPALIVE, &v) == 0); + So(v == true); + }); + nng_fini(); }) diff --git a/tests/tls.c b/tests/tls.c index 087234ff..4ce12ea8 100644 --- a/tests/tls.c +++ b/tests/tls.c @@ -107,13 +107,14 @@ static const char key[] = static int check_props_v4(nng_msg *msg) { - nng_pipe p; - size_t z; + nng_pipe p; + size_t z; + bool b; + nng_sockaddr la; + nng_sockaddr ra; p = nng_msg_get_pipe(msg); So(p > 0); - nng_sockaddr la; - nng_sockaddr ra; // Typed access So(nng_pipe_getopt_sockaddr(p, NNG_OPT_LOCADDR, &la) == 0); @@ -130,12 +131,19 @@ check_props_v4(nng_msg *msg) So(ra.s_in.sa_port != 0); So(ra.s_in.sa_addr == htonl(0x7f000001)); + So(nng_pipe_getopt_bool(p, NNG_OPT_TCP_KEEPALIVE, &b) == 0); + So(b == false); // default + + So(nng_pipe_getopt_bool(p, NNG_OPT_TCP_NODELAY, &b) == 0); + So(b == true); // default + // Check for type enforcement int i; So(nng_pipe_getopt_int(p, NNG_OPT_REMADDR, &i) == NNG_EBADTYPE); z = 1; So(nng_pipe_getopt(p, NNG_OPT_REMADDR, &ra, &z) == NNG_EINVAL); + return (0); } @@ -468,4 +476,100 @@ TestMain("TLS Transport", { NNG_EBADTYPE); nng_msg_free(msg); }); + + Convey("No delay option", { + nng_socket s; + nng_dialer d; + nng_listener l; + bool v; + int x; + + So(nng_pair_open(&s) == 0); + Reset({ nng_close(s); }); + So(nng_getopt_bool(s, NNG_OPT_TCP_NODELAY, &v) == NNG_ENOTSUP); + So(nng_dialer_create(&d, s, "tcp://127.0.0.1:4999") == 0); + So(nng_dialer_getopt_bool(d, NNG_OPT_TCP_NODELAY, &v) == 0); + So(v == true); + So(nng_dialer_setopt_bool(d, NNG_OPT_TCP_NODELAY, false) == 0); + So(nng_dialer_getopt_bool(d, NNG_OPT_TCP_NODELAY, &v) == 0); + So(v == false); + So(nng_dialer_getopt_int(d, NNG_OPT_TCP_NODELAY, &x) == + NNG_EBADTYPE); + x = 0; + So(nng_dialer_setopt_int(d, NNG_OPT_TCP_NODELAY, x) == + NNG_EBADTYPE); + // This assumes sizeof (bool) != sizeof (int) + So(nng_dialer_setopt(d, NNG_OPT_TCP_NODELAY, &x, sizeof(x)) == + NNG_EINVAL); + + So(nng_listener_create(&l, s, "tcp://127.0.0.1:4999") == 0); + So(nng_listener_getopt_bool(l, NNG_OPT_TCP_NODELAY, &v) == 0); + So(v == true); + x = 0; + So(nng_listener_setopt_int(l, NNG_OPT_TCP_NODELAY, x) == + NNG_EBADTYPE); + // This assumes sizeof (bool) != sizeof (int) + So(nng_listener_setopt( + l, NNG_OPT_TCP_NODELAY, &x, sizeof(x)) == NNG_EINVAL); + + nng_dialer_close(d); + nng_listener_close(l); + + // Make sure socket wide defaults apply. + So(nng_setopt_bool(s, NNG_OPT_TCP_NODELAY, true) == 0); + v = false; + So(nng_getopt_bool(s, NNG_OPT_TCP_NODELAY, &v) == 0); + So(v == true); + So(nng_setopt_bool(s, NNG_OPT_TCP_NODELAY, false) == 0); + So(nng_dialer_create(&d, s, "tcp://127.0.0.1:4999") == 0); + So(nng_dialer_getopt_bool(d, NNG_OPT_TCP_NODELAY, &v) == 0); + So(v == false); + }); + + Convey("Keepalive option", { + nng_socket s; + nng_dialer d; + nng_listener l; + bool v; + int x; + + So(nng_pair_open(&s) == 0); + Reset({ nng_close(s); }); + So(nng_getopt_bool(s, NNG_OPT_TCP_KEEPALIVE, &v) == + NNG_ENOTSUP); + So(nng_dialer_create(&d, s, "tcp://127.0.0.1:4999") == 0); + So(nng_dialer_getopt_bool(d, NNG_OPT_TCP_KEEPALIVE, &v) == 0); + So(v == false); + So(nng_dialer_setopt_bool(d, NNG_OPT_TCP_KEEPALIVE, true) == + 0); + So(nng_dialer_getopt_bool(d, NNG_OPT_TCP_KEEPALIVE, &v) == 0); + So(v == true); + So(nng_dialer_getopt_int(d, NNG_OPT_TCP_KEEPALIVE, &x) == + NNG_EBADTYPE); + x = 1; + So(nng_dialer_setopt_int(d, NNG_OPT_TCP_KEEPALIVE, x) == + NNG_EBADTYPE); + + So(nng_listener_create(&l, s, "tcp://127.0.0.1:4999") == 0); + So(nng_listener_getopt_bool(l, NNG_OPT_TCP_KEEPALIVE, &v) == + 0); + So(v == false); + x = 1; + So(nng_listener_setopt_int(l, NNG_OPT_TCP_KEEPALIVE, x) == + NNG_EBADTYPE); + + nng_dialer_close(d); + nng_listener_close(l); + + // Make sure socket wide defaults apply. + So(nng_setopt_bool(s, NNG_OPT_TCP_KEEPALIVE, false) == 0); + v = true; + So(nng_getopt_bool(s, NNG_OPT_TCP_KEEPALIVE, &v) == 0); + So(v == false); + So(nng_setopt_bool(s, NNG_OPT_TCP_KEEPALIVE, true) == 0); + So(nng_dialer_create(&d, s, "tcp://127.0.0.1:4999") == 0); + So(nng_dialer_getopt_bool(d, NNG_OPT_TCP_KEEPALIVE, &v) == 0); + So(v == true); + }); + }) |
