aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/man/nng_options.5.adoc67
-rw-r--r--docs/man/nng_tcp.7.adoc18
-rw-r--r--docs/man/nng_tls.7.adoc16
-rw-r--r--src/core/platform.h8
-rw-r--r--src/nng.h59
-rw-r--r--src/platform/posix/posix_aio.h2
-rw-r--r--src/platform/posix/posix_pipedesc.c26
-rw-r--r--src/platform/posix/posix_tcp.c12
-rw-r--r--src/platform/windows/win_tcp.c24
-rw-r--r--src/supplemental/tls/mbedtls/tls.c17
-rw-r--r--src/supplemental/tls/tls_api.h2
-rw-r--r--src/transport/tcp/tcp.c100
-rw-r--r--src/transport/tls/tls.c100
-rw-r--r--tests/tcp.c102
-rw-r--r--tests/tls.c112
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
diff --git a/src/nng.h b/src/nng.h
index cd65c874..52fc95a1 100644
--- a/src/nng.h
+++ b/src/nng.h
@@ -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);
+ });
+
})