From 5cf750697624d4fd63cfe26921209d7c30e1a2d2 Mon Sep 17 00:00:00 2001 From: Garrett D'Amore Date: Mon, 21 Jan 2019 22:40:10 -0800 Subject: fixes #872 create unified nng_stream API This is a major change, and includes changes to use a polymorphic stream API for all transports. There have been related bugs fixed along the way. Additionally the man pages have changed. The old non-polymorphic APIs are removed now. This is a breaking change, but the old APIs were never part of any released public API. --- src/core/aio.c | 20 ++- src/core/aio.h | 5 +- src/core/nng_impl.h | 1 + src/core/options.c | 16 +++ src/core/options.h | 9 ++ src/core/platform.h | 157 ++-------------------- src/core/stream.c | 366 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/core/stream.h | 69 ++++++++++ src/core/tcp.h | 24 ++++ src/core/transport.c | 13 +- src/core/transport.h | 6 + src/core/url.c | 45 +++++-- src/core/url.h | 2 +- 13 files changed, 568 insertions(+), 165 deletions(-) create mode 100644 src/core/stream.c create mode 100644 src/core/stream.h create mode 100644 src/core/tcp.h (limited to 'src/core') diff --git a/src/core/aio.c b/src/core/aio.c index b67b7467..ee3d10a5 100644 --- a/src/core/aio.c +++ b/src/core/aio.c @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. +// Copyright 2019 Staysail Systems, Inc. // Copyright 2018 Capitar IT Group BV // // This software is supplied under the terms of the MIT License, a @@ -101,6 +101,12 @@ struct nng_aio { nni_list_node a_prov_node; void * a_prov_extra[4]; // Extra data used by provider + // Socket address. This turns out to be very useful, as we wind up + // needing socket addresses for numerous connection related routines. + // It would be cleaner to not have this and avoid burning the space, + // but having this hear dramatically simplifies lots of code. + nng_sockaddr a_sockaddr; + // Expire node. nni_list_node a_expire_node; }; @@ -765,3 +771,15 @@ nni_aio_sys_init(void) nni_thr_run(thr); return (0); } + +void +nni_aio_set_sockaddr(nni_aio *aio, const nng_sockaddr *sa) +{ + memcpy(&aio->a_sockaddr, sa, sizeof(*sa)); +} + +void +nni_aio_get_sockaddr(nni_aio *aio, nng_sockaddr *sa) +{ + memcpy(sa, &aio->a_sockaddr, sizeof(*sa)); +} \ No newline at end of file diff --git a/src/core/aio.h b/src/core/aio.h index fed0acd8..304f184c 100644 --- a/src/core/aio.h +++ b/src/core/aio.h @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. +// Copyright 2019 Staysail Systems, Inc. // Copyright 2018 Capitar IT Group BV // // This software is supplied under the terms of the MIT License, a @@ -148,6 +148,9 @@ extern void nni_aio_get_iov(nni_aio *, unsigned *, nni_iov **); extern void nni_aio_normalize_timeout(nni_aio *, nng_duration); extern void nni_aio_bump_count(nni_aio *, size_t); +extern void nni_aio_set_sockaddr(nni_aio *aio, const nng_sockaddr *); +extern void nni_aio_get_sockaddr(nni_aio *aio, nng_sockaddr *); + // nni_aio_schedule indicates that the AIO has begun, and is scheduled for // asychronous completion. This also starts the expiration timer. Note that // prior to this, the aio is uncancellable. If the operation has a zero diff --git a/src/core/nng_impl.h b/src/core/nng_impl.h index abf47f49..07fe44f5 100644 --- a/src/core/nng_impl.h +++ b/src/core/nng_impl.h @@ -42,6 +42,7 @@ #include "core/random.h" #include "core/reap.h" #include "core/stats.h" +#include "core/stream.h" #include "core/strs.h" #include "core/taskq.h" #include "core/thread.h" diff --git a/src/core/options.c b/src/core/options.c index 0b15d6e2..82735369 100644 --- a/src/core/options.c +++ b/src/core/options.c @@ -398,4 +398,20 @@ nni_setopt(const nni_option *opts, const char *nm, void *arg, const void *buf, opts++; } return (NNG_ENOTSUP); +} + +int +nni_chkopt(const nni_chkoption *opts, const char *nm, const void *buf, + size_t sz, nni_type t) +{ + while (opts->o_name != NULL) { + if (strcmp(opts->o_name, nm) == 0) { + if (opts->o_check == NULL) { + return (NNG_EREADONLY); + } + return (opts->o_check(buf, sz, t)); + } + opts++; + } + return (NNG_ENOTSUP); } \ No newline at end of file diff --git a/src/core/options.h b/src/core/options.h index 9c5d4817..7b66dbfb 100644 --- a/src/core/options.h +++ b/src/core/options.h @@ -74,11 +74,20 @@ struct nni_option_s { int (*o_set)(void *, const void *, size_t, nni_type); }; +typedef struct nni_chkoption_s nni_chkoption; +struct nni_chkoption_s { + const char *o_name; + // o_check can be NULL for read-only options + int (*o_check)(const void *, size_t, nni_type); +}; + // nni_getopt and nni_setopt are helper functions to implement options // based on arrays of nni_option structures. extern int nni_getopt( const nni_option *, const char *, void *, void *, size_t *, nni_type); extern int nni_setopt( const nni_option *, const char *, void *, const void *, size_t, nni_type); +extern int nni_chkopt( + const nni_chkoption *, const char *, const void *, size_t, nni_type); #endif // CORE_OPTIONS_H diff --git a/src/core/platform.h b/src/core/platform.h index 69fa5db6..e415b438 100644 --- a/src/core/platform.h +++ b/src/core/platform.h @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. +// Copyright 2019 Staysail Systems, Inc. // Copyright 2018 Capitar IT Group BV // Copyright 2018 Devolutions // @@ -228,53 +228,6 @@ typedef struct nni_tcp_conn nni_tcp_conn; typedef struct nni_tcp_dialer nni_tcp_dialer; typedef struct nni_tcp_listener nni_tcp_listener; -extern void nni_tcp_conn_fini(nni_tcp_conn *); - -// nni_tcp_conn_close closes the connection, which might actually be -// implemented as a shutdown() call. -// Further operations on it should return NNG_ECLOSED. -extern void nni_tcp_conn_close(nni_tcp_conn *); - -// nni_tcp_conn_send sends data in the iov buffers to the peer. -// The platform may modify the iovs. -extern void nni_tcp_conn_send(nni_tcp_conn *, nni_aio *); - -// nni_tcp_conn_recv receives data into the buffers provided by the -// I/O vector (iovs). The platform should attempt to scatter the received -// data into the iovs if possible. -// -// It is possible for the reader to return less data than is requested, -// in which case the caller is responsible for resubmitting. The platform -// must not return "zero" data however. (It is an error to attempt to -// receive zero bytes.) The platform may modify the iovs. -extern void nni_tcp_conn_recv(nni_tcp_conn *, nni_aio *); - -// nni_tcp_conn_peername gets the peer name. -extern int nni_tcp_conn_peername(nni_tcp_conn *, nni_sockaddr *); - -// nni_tcp_conn_sockname gets the local name. -extern int nni_tcp_conn_sockname(nni_tcp_conn *, nni_sockaddr *); - -// nni_tcp_conn_set_nodelay indicates that the TCP pipe should send -// data immediately, without any buffering. (Disable Nagle's algorithm.) -extern int nni_tcp_conn_set_nodelay(nni_tcp_conn *, bool); - -// nni_tcp_conn_set_keepalive indicates that the TCP pipe should send -// keepalive probes. Tuning of these keepalives is currently unsupported. -extern int nni_tcp_conn_set_keepalive(nni_tcp_conn *, bool); - -// nni_tcp_conn_setopt is like setsockopt, but uses string names. These -// are the same names from the TCP transport, generally. Examples are -// NNG_OPT_TCP_NODELAY and NNG_OPT_TCP_KEEPALIVE. -extern int nni_tcp_conn_setopt( - nni_tcp_conn *, const char *, const void *, size_t, nni_type); - -// nni_tcp_conn_getopt is like getsockopt, but uses string names. -// We support NNG_OPT_REMADDR and NNG_OPT_LOCADDR (with argument type -// nng_sockaddr), and NNG_OPT_TCP_NODELAY and NNG_OPT_TCP_KEEPALIVE. -extern int nni_tcp_conn_getopt( - nni_tcp_conn *, const char *, void *, size_t *, nni_type); - // nni_tcp_dialer_init creates a new dialer object. extern int nni_tcp_dialer_init(nni_tcp_dialer **); @@ -287,11 +240,10 @@ extern void nni_tcp_dialer_fini(nni_tcp_dialer *); // connection will be aborted. extern void nni_tcp_dialer_close(nni_tcp_dialer *); -// nni_tcp_dialer_dial attempts to create an outgoing connection, -// asynchronously, to the address specified. On success, the first (and only) +// nni_tcp_dial attempts to create an outgoing connection, +// asynchronously, to the address in the aio. On success, the first (and only) // output will be an nni_tcp_conn * associated with the remote server. -extern void nni_tcp_dialer_dial( - nni_tcp_dialer *, const nni_sockaddr *, nni_aio *); +extern void nni_tcp_dial(nni_tcp_dialer *, nni_aio *); // nni_tcp_dialer_getopt gets an option from the dialer. extern int nni_tcp_dialer_setopt( @@ -361,101 +313,12 @@ typedef struct nni_ipc_conn nni_ipc_conn; typedef struct nni_ipc_dialer nni_ipc_dialer; typedef struct nni_ipc_listener nni_ipc_listener; -// nni_ipc_conn_fini disposes of the connection. -extern void nni_ipc_conn_fini(nni_ipc_conn *); - -// nni_ipc_conn_close closes the connection, which might actually be -// implemented as a shutdown() call. -// Further operations on it should return NNG_ECLOSED. -extern void nni_ipc_conn_close(nni_ipc_conn *); - -// nni_ipc_conn_send sends data in the iov buffers to the peer. -// The platform may modify the iovs. -extern void nni_ipc_conn_send(nni_ipc_conn *, nni_aio *); - -// nni_ipc_conn_recv receives data into the buffers provided by the -// I/O vector (iovs). The platform should attempt to scatter the received -// data into the iovs if possible. -// -// It is possible for the reader to return less data than is requested, -// in which case the caller is responsible for resubmitting. The platform -// must not return "zero" data however. (It is an error to attempt to -// receive zero bytes.) The platform may modify the iovs. -extern void nni_ipc_conn_recv(nni_ipc_conn *, nni_aio *); - -// nni_ipc_conn_setopt is like setsockopt, but uses string names. These -// are the same names from the IPC transport, generally. There are no -// options that are generally settable on an IPC connection. -extern int nni_ipc_conn_setopt( - nni_ipc_conn *, const char *, const void *, size_t, nni_type); - -// nni_ipc_conn_getopt is like getsockopt, but uses string names. -// We support NNG_OPT_REMADDR and NNG_OPT_LOCADDR (with argument type -// nng_sockaddr), and on some platforms NNG_OPT_IPC_PEER_[UID,GID,ZONEID] -// (with type uint64_t.) -extern int nni_ipc_conn_getopt( - nni_ipc_conn *, const char *, void *, size_t *, nni_type); - -// nni_ipc_dialer_init creates a new dialer object. -extern int nni_ipc_dialer_init(nni_ipc_dialer **); - -// nni_ipc_dialer_fini finalizes the dialer, closing it and freeing -// all resources. -extern void nni_ipc_dialer_fini(nni_ipc_dialer *); - -// nni_ipc_dialer_close closes the dialer. -// Further operations on it should return NNG_ECLOSED. Any in-progress -// connection will be aborted. -extern void nni_ipc_dialer_close(nni_ipc_dialer *); - -// nni_ipc_dialer_dial attempts to create an outgoing connection, -// asynchronously, to the address specified. On success, the first (and only) -// output will be an nni_ipc_conn * associated with the remote server. -extern void nni_ipc_dialer_dial( - nni_ipc_dialer *, const nni_sockaddr *, nni_aio *); - -// nni_ipc_dialer_getopt is used to get options from the dialer. -// At present there aren't any defined options. -extern int nni_ipc_dialer_getopt( - nni_ipc_dialer *, const char *, void *, size_t *, nni_type); - -// nni_ipc_dialer_setopt sets an option for the dialer. There aren't -// any options defined at present. -extern int nni_ipc_dialer_setopt( - nni_ipc_dialer *, const char *, const void *, size_t, nni_type); - -// nni_ipc_listener_init creates a new listener object, unbound. -extern int nni_ipc_listener_init(nni_ipc_listener **); - -// nni_ipc_listener_fini frees the listener and all associated resources. -// It implictly closes the listener as well. -extern void nni_ipc_listener_fini(nni_ipc_listener *); - -// nni_ipc_listener_close closes the listener. This will unbind -// any bound socket, and further operations will result in NNG_ECLOSED. -extern void nni_ipc_listener_close(nni_ipc_listener *); - -// nni_ipc_listener_listen creates the socket in listening mode, bound -// to the specified address. Unlike TCP, this address does not change. -extern int nni_ipc_listener_listen(nni_ipc_listener *, const nni_sockaddr *); - -// nni_ipc_listener_accept accepts in incoming connect, asynchronously. -// On success, the first (and only) output will be an nni_ipc_conn * -// associated with the remote peer. -extern void nni_ipc_listener_accept(nni_ipc_listener *, nni_aio *); - -// nni_ipc_listener_getopt is used to get options from the listener. -// The only valid option is NNG_OPT_LOCADDR, which will only have -// a valid value if the socket is bound, otherwise the value returned -// will be of type NNG_AF_UNSPEC. -extern int nni_ipc_listener_getopt( - nni_ipc_listener *, const char *, void *, size_t *, nni_type); - -// nni_ipc_listener_setopt sets an option for the listener. The only -// valid options are NNG_OPT_IPC_SECURITY_DESCRIPTORS (Windows) and -// NNG_OPT_IPC_PERMISSIONS (POSIX). -extern int nni_ipc_listener_setopt( - nni_ipc_listener *, const char *, const void *, size_t, nni_type); +// IPC is so different from platform to platform. The following should +// be implemented. If IPC isn't supported, all of these functions should +// be stubs that just return NNG_ENOTSUP. +extern int nni_ipc_dialer_alloc(nng_stream_dialer **, const nng_url *); +extern int nni_ipc_listener_alloc(nng_stream_listener **, const nng_url *); +extern int nni_ipc_checkopt(const char *, const void *, size_t, nni_type); // // UDP support. UDP is not connection oriented, and only has the notion diff --git a/src/core/stream.c b/src/core/stream.c new file mode 100644 index 00000000..52c5b1a6 --- /dev/null +++ b/src/core/stream.c @@ -0,0 +1,366 @@ +// +// Copyright 2019 Staysail Systems, Inc. +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +// This provides an abstraction for byte streams, allowing polymorphic +// use of them in rather flexible contexts. + +#include + +#include "core/nng_impl.h" +#include + +#include "core/tcp.h" +#include "supplemental/tls/tls_api.h" +#include "supplemental/websocket/websocket.h" + +static struct { + const char *scheme; + int (*dialer_alloc)(nng_stream_dialer **, const nng_url *); + int (*listener_alloc)(nng_stream_listener **, const nng_url *); + int (*checkopt)(const char *, const void *, size_t, nni_type); + +} stream_drivers[] = { + { + .scheme = "ipc", + .dialer_alloc = nni_ipc_dialer_alloc, + .listener_alloc = nni_ipc_listener_alloc, + .checkopt = nni_ipc_checkopt, + }, + { + .scheme = "tcp", + .dialer_alloc = nni_tcp_dialer_alloc, + .listener_alloc = nni_tcp_listener_alloc, + .checkopt = nni_tcp_checkopt, + }, + { + .scheme = "tcp4", + .dialer_alloc = nni_tcp_dialer_alloc, + .listener_alloc = nni_tcp_listener_alloc, + .checkopt = nni_tcp_checkopt, + }, + { + .scheme = "tcp6", + .dialer_alloc = nni_tcp_dialer_alloc, + .listener_alloc = nni_tcp_listener_alloc, + .checkopt = nni_tcp_checkopt, + }, + { + .scheme = "tls+tcp", + .dialer_alloc = nni_tls_dialer_alloc, + .listener_alloc = nni_tls_listener_alloc, + .checkopt = nni_tls_checkopt, + }, + { + .scheme = "tls+tcp4", + .dialer_alloc = nni_tls_dialer_alloc, + .listener_alloc = nni_tls_listener_alloc, + .checkopt = nni_tls_checkopt, + }, + { + .scheme = "tls+tcp6", + .dialer_alloc = nni_tls_dialer_alloc, + .listener_alloc = nni_tls_listener_alloc, + .checkopt = nni_tls_checkopt, + }, + { + .scheme = "ws", + .dialer_alloc = nni_ws_dialer_alloc, + .listener_alloc = nni_ws_listener_alloc, + .checkopt = nni_ws_checkopt, + }, + { + .scheme = "wss", + .dialer_alloc = nni_ws_dialer_alloc, + .listener_alloc = nni_ws_listener_alloc, + .checkopt = nni_ws_checkopt, + }, + { + .scheme = NULL, + }, +}; + +void +nng_stream_close(nng_stream *s) +{ + s->s_close(s); +} + +void +nng_stream_free(nng_stream *s) +{ + if (s != NULL) { + s->s_free(s); + } +} + +void +nng_stream_send(nng_stream *s, nng_aio *aio) +{ + s->s_send(s, aio); +} + +void +nng_stream_recv(nng_stream *s, nng_aio *aio) +{ + s->s_recv(s, aio); +} + +int +nni_stream_getx( + nng_stream *s, const char *nm, void *data, size_t *szp, nni_type t) +{ + return (s->s_getx(s, nm, data, szp, t)); +} + +int +nni_stream_setx( + nng_stream *s, const char *nm, const void *data, size_t sz, nni_type t) +{ + return (s->s_setx(s, nm, data, sz, t)); +} + +void +nng_stream_dialer_close(nng_stream_dialer *d) +{ + d->sd_close(d); +} + +void +nng_stream_dialer_free(nng_stream_dialer *d) +{ + if (d != NULL) { + d->sd_free(d); + } +} + +void +nng_stream_dialer_dial(nng_stream_dialer *d, nng_aio *aio) +{ + d->sd_dial(d, aio); +} + +int +nng_stream_dialer_alloc_url(nng_stream_dialer **dp, const nng_url *url) +{ + int rv; + + if ((rv = nni_init()) != 0) { + return (rv); + } + + for (int i = 0; stream_drivers[i].scheme != NULL; i++) { + if (strcmp(stream_drivers[i].scheme, url->u_scheme) == 0) { + return (stream_drivers[i].dialer_alloc(dp, url)); + } + } + return (NNG_ENOTSUP); +} + +int +nng_stream_dialer_alloc(nng_stream_dialer **dp, const char *uri) +{ + nng_url *url; + int rv; + + if ((rv = nni_init()) != 0) { + return (rv); + } + if ((rv = nng_url_parse(&url, uri)) != 0) { + return (rv); + } + rv = nng_stream_dialer_alloc_url(dp, url); + nng_url_free(url); + return (rv); +} + +int +nni_stream_dialer_getx( + nng_stream_dialer *d, const char *nm, void *data, size_t *szp, nni_type t) +{ + return (d->sd_getx(d, nm, data, szp, t)); +} + +int +nni_stream_dialer_setx(nng_stream_dialer *d, const char *nm, const void *data, + size_t sz, nni_type t) +{ + return (d->sd_setx(d, nm, data, sz, t)); +} + +void +nng_stream_listener_close(nng_stream_listener *l) +{ + l->sl_close(l); +} +void +nng_stream_listener_free(nng_stream_listener *l) +{ + if (l != NULL) { + l->sl_free(l); + } +} +int +nng_stream_listener_listen(nng_stream_listener *l) +{ + return (l->sl_listen(l)); +} + +void +nng_stream_listener_accept(nng_stream_listener *l, nng_aio *aio) +{ + l->sl_accept(l, aio); +} + +int +nni_stream_listener_getx(nng_stream_listener *l, const char *nm, void *data, + size_t *szp, nni_type t) +{ + return (l->sl_getx(l, nm, data, szp, t)); +} + +int +nni_stream_listener_setx(nng_stream_listener *l, const char *nm, + const void *data, size_t sz, nni_type t) +{ + return (l->sl_setx(l, nm, data, sz, t)); +} + +int +nng_stream_listener_alloc_url(nng_stream_listener **lp, const nng_url *url) +{ + int rv; + + if ((rv = nni_init()) != 0) { + return (rv); + } + + for (int i = 0; stream_drivers[i].scheme != NULL; i++) { + if (strcmp(stream_drivers[i].scheme, url->u_scheme) == 0) { + return (stream_drivers[i].listener_alloc(lp, url)); + } + } + return (NNG_ENOTSUP); +} + +int +nng_stream_listener_alloc(nng_stream_listener **lp, const char *uri) +{ + nng_url *url; + int rv; + + if ((rv = nni_init()) != 0) { + return (rv); + } + + if ((rv = nng_url_parse(&url, uri)) != 0) { + return (rv); + } + rv = nng_stream_listener_alloc_url(lp, url); + nng_url_free(url); + return (rv); +} + +int +nni_stream_checkopt(const char *scheme, const char *name, const void *data, + size_t sz, nni_type t) +{ + for (int i = 0; stream_drivers[i].scheme != NULL; i++) { + if (strcmp(stream_drivers[i].scheme, scheme) != 0) { + continue; + } + if (stream_drivers[i].checkopt == NULL) { + return (NNG_ENOTSUP); + } + return (stream_drivers[i].checkopt(name, data, sz, t)); + } + return (NNG_ENOTSUP); +} + +// +// This next block sets up to define the various typed option functions. +// To make it easier to cover them all at once, we use macros. +// + +#define DEFGET(base) \ + int nng_##base##_get( \ + nng_##base *s, const char *nm, void *vp, size_t *szp) \ + { \ + return (nni_##base##_getx(s, nm, vp, szp, NNI_TYPE_OPAQUE)); \ + } + +#define DEFTYPEDGET(base, suffix, type, nnitype) \ + int nng_##base##_get_##suffix( \ + nng_##base *s, const char *nm, type *vp) \ + { \ + size_t sz = sizeof(*vp); \ + return (nni_##base##_getx(s, nm, vp, &sz, nnitype)); \ + } + +#define DEFGETALL(base) \ + DEFGET(base) \ + DEFTYPEDGET(base, int, int, NNI_TYPE_INT32) \ + DEFTYPEDGET(base, bool, bool, NNI_TYPE_BOOL) \ + DEFTYPEDGET(base, size, size_t, NNI_TYPE_SIZE) \ + DEFTYPEDGET(base, uint64, uint64_t, NNI_TYPE_UINT64) \ + DEFTYPEDGET(base, ptr, void *, NNI_TYPE_POINTER) \ + DEFTYPEDGET(base, ms, nng_duration, NNI_TYPE_DURATION) \ + DEFTYPEDGET(base, addr, nng_sockaddr, NNI_TYPE_SOCKADDR) + +DEFGETALL(stream) +DEFGETALL(stream_dialer) +DEFGETALL(stream_listener) + +#define DEFSET(base) \ + int nng_##base##_set( \ + nng_##base *s, const char *nm, const void *vp, size_t sz) \ + { \ + return (nni_##base##_setx(s, nm, vp, sz, NNI_TYPE_OPAQUE)); \ + } + +#define DEFTYPEDSETEX(base, suffix, type, len, nnitype) \ + int nng_##base##_set_##suffix(nng_##base *s, const char *nm, type v) \ + { \ + return (nni_##base##_setx(s, nm, &v, len, nnitype)); \ + } + +#define DEFTYPEDSET(base, suffix, type, nnitype) \ + int nng_##base##_set_##suffix(nng_##base *s, const char *nm, type v) \ + { \ + return (nni_##base##_setx(s, nm, &v, sizeof(v), nnitype)); \ + } + +#define DEFSTRINGSET(base) \ + int nng_##base##_set_string( \ + nng_##base *s, const char *nm, const char *v) \ + { \ + return (nni_##base##_setx(s, nm, v, \ + v != NULL ? strlen(v) + 1 : 0, NNI_TYPE_STRING)); \ + } + +#define DEFSOCKADDRSET(base) \ + int nng_##base##_set_adddr( \ + nng_##base *s, const char *nm, const nng_sockaddr *v) \ + { \ + return (nni_##base##_setx( \ + s, nm, v, sizeof(*v), NNI_TYPE_SOCKADDR)); \ + } + +#define DEFSETALL(base) \ + DEFSET(base) \ + DEFTYPEDSET(base, int, int, NNI_TYPE_INT32) \ + DEFTYPEDSET(base, bool, bool, NNI_TYPE_BOOL) \ + DEFTYPEDSET(base, size, size_t, NNI_TYPE_SIZE) \ + DEFTYPEDSET(base, ms, nng_duration, NNI_TYPE_DURATION) \ + DEFTYPEDSET(base, ptr, void *, NNI_TYPE_POINTER) \ + DEFSTRINGSET(base) \ + DEFSOCKADDRSET(base) + +DEFSETALL(stream) +DEFSETALL(stream_dialer) +DEFSETALL(stream_listener) \ No newline at end of file diff --git a/src/core/stream.h b/src/core/stream.h new file mode 100644 index 00000000..18914979 --- /dev/null +++ b/src/core/stream.h @@ -0,0 +1,69 @@ +// +// Copyright 2019 Staysail Systems, Inc. +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#ifndef CORE_STREAM_H +#define CORE_STREAM_H + +// This provides an abstraction for byte streams, allowing polymorphic +// use of them in rather flexible contexts. + +#include "core/nng_impl.h" + +// Private property operations (these include the types.) +extern int nni_stream_getx( + nng_stream *, const char *, void *, size_t *, nni_type); +extern int nni_stream_setx( + nng_stream *, const char *, const void *, size_t, nni_type); + +extern int nni_stream_dialer_getx( + nng_stream_dialer *, const char *, void *, size_t *, nni_type); +extern int nni_stream_dialer_setx( + nng_stream_dialer *, const char *, const void *, size_t, nni_type); + +extern int nni_stream_listener_getx( + nng_stream_listener *, const char *, void *, size_t *, nni_type); +extern int nni_stream_listener_setx( + nng_stream_listener *, const char *, const void *, size_t, nni_type); + +extern int nni_stream_checkopt( + const char *, const char *, const void *, size_t, nni_type); + +// This is the common implementation of a connected byte stream. It should be +// the first element of any implementation. Applications are not permitted to +// access it directly. +struct nng_stream { + void (*s_free)(void *); + void (*s_close)(void *); + void (*s_recv)(void *, nng_aio *); + void (*s_send)(void *, nng_aio *); + int (*s_getx)(void *, const char *, void *, size_t *, nni_type); + int (*s_setx)(void *, const char *, const void *, size_t, nni_type); +}; + +// Dialer implementation. Stream dialers create streams. +struct nng_stream_dialer { + void (*sd_free)(void *); + void (*sd_close)(void *); + void (*sd_dial)(void *, nng_aio *); + int (*sd_getx)(void *, const char *, void *, size_t *, nni_type); + int (*sd_setx)(void *, const char *, const void *, size_t, nni_type); +}; + +// Listener implementation. Stream listeners accept connections and create +// streams. +struct nng_stream_listener { + void (*sl_free)(void *); + void (*sl_close)(void *); + int (*sl_listen)(void *); + void (*sl_accept)(void *, nng_aio *); + int (*sl_getx)(void *, const char *, void *, size_t *, nni_type); + int (*sl_setx)(void *, const char *, const void *, size_t, nni_type); +}; + +#endif // CORE_STREAM_H diff --git a/src/core/tcp.h b/src/core/tcp.h new file mode 100644 index 00000000..ac4398d0 --- /dev/null +++ b/src/core/tcp.h @@ -0,0 +1,24 @@ +// +// Copyright 2019 Staysail Systems, Inc. +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#ifndef CORE_TCP_H +#define CORE_TCP_H + +#include "core/nng_impl.h" + +// These are interfaces we use for TCP internally. These are not exposed +// to the public API. + +extern int nni_tcp_dialer_alloc(nng_stream_dialer **, const nng_url *); +extern int nni_tcp_listener_alloc(nng_stream_listener **, const nng_url *); + +// nni_tcp_checkopt is used to validate (generically) options. +extern int nni_tcp_checkopt(const char *, const void *, size_t, nni_type); + +#endif // CORE_TCP_H diff --git a/src/core/transport.c b/src/core/transport.c index 54c4bcfe..071ea0c7 100644 --- a/src/core/transport.c +++ b/src/core/transport.c @@ -1,6 +1,7 @@ // -// Copyright 2018 Staysail Systems, Inc. +// Copyright 2019 Staysail Systems, Inc. // Copyright 2018 Capitar IT Group BV +// Copyright 2019 Devolutions // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -122,6 +123,16 @@ nni_tran_chkopt(const char *name, const void *v, size_t sz, int typ) const nni_tran_listener_ops *lops; const nni_option * o; + // Check option entry point is cleaner than endpoint hacks. + if (t->t_tran.tran_checkopt != NULL) { + rv = t->t_tran.tran_checkopt(name, v, sz, typ); + if (rv != NNG_ENOTSUP) { + nni_mtx_unlock(&nni_tran_lk); + return (rv); + } + continue; + } + // Generally we look for endpoint options. We check both // dialers and listeners. dops = t->t_tran.tran_dialer; diff --git a/src/core/transport.h b/src/core/transport.h index f3d252c4..84d9c1d9 100644 --- a/src/core/transport.h +++ b/src/core/transport.h @@ -183,6 +183,12 @@ struct nni_tran { // tran_fini, if not NULL, is called during library deinitialization. // It should release any global resources, close any open files, etc. void (*tran_fini)(void); + + // tran_chkopt is used to check option validity; this is used as + // an initial filter on the data, without actually setting anything. + // This can be useful, for example, before any transports are + // configured on the socket. + int (*tran_checkopt)(const char *, const void *, size_t, nni_type); }; // These APIs are used by the framework internally, and not for use by diff --git a/src/core/url.c b/src/core/url.c index 5e2317ea..b2cf77f8 100644 --- a/src/core/url.c +++ b/src/core/url.c @@ -231,8 +231,21 @@ nni_url_default_port(const char *scheme) const char *s; for (int i = 0; (s = nni_url_default_ports[i].scheme) != NULL; i++) { - if (strcmp(s, scheme) == 0) { + size_t l = strlen(s); + if (strncmp(s, scheme, strlen(s)) != 0) { + continue; + } + // It can have a suffix of either "4" or "6" to restrict + // the address family. This is an NNG extension. + switch (scheme[l]) { + case '\0': return (nni_url_default_ports[i].port); + case '4': + case '6': + if (scheme[l + 1] == '\0') { + return (nni_url_default_ports[i].port); + } + break; } } return (""); @@ -463,19 +476,23 @@ error: void nni_url_free(nni_url *url) { - nni_strfree(url->u_rawurl); - nni_strfree(url->u_scheme); - nni_strfree(url->u_userinfo); - nni_strfree(url->u_host); - nni_strfree(url->u_hostname); - nni_strfree(url->u_port); - nni_strfree(url->u_path); - nni_strfree(url->u_query); - nni_strfree(url->u_fragment); - nni_strfree(url->u_requri); - NNI_FREE_STRUCT(url); + if (url != NULL) { + nni_strfree(url->u_rawurl); + nni_strfree(url->u_scheme); + nni_strfree(url->u_userinfo); + nni_strfree(url->u_host); + nni_strfree(url->u_hostname); + nni_strfree(url->u_port); + nni_strfree(url->u_path); + nni_strfree(url->u_query); + nni_strfree(url->u_fragment); + nni_strfree(url->u_requri); + NNI_FREE_STRUCT(url); + } } +#define URL_COPYSTR(d, s) ((s != NULL) && ((d = nni_strdup(s)) == NULL)) + int nni_url_clone(nni_url **dstp, const nni_url *src) { @@ -484,7 +501,6 @@ nni_url_clone(nni_url **dstp, const nni_url *src) if ((dst = NNI_ALLOC_STRUCT(dst)) == NULL) { return (NNG_ENOMEM); } -#define URL_COPYSTR(d, s) ((s != NULL) && ((d = nni_strdup(s)) == NULL)) if (URL_COPYSTR(dst->u_rawurl, src->u_rawurl) || URL_COPYSTR(dst->u_scheme, src->u_scheme) || URL_COPYSTR(dst->u_userinfo, src->u_userinfo) || @@ -498,7 +514,8 @@ nni_url_clone(nni_url **dstp, const nni_url *src) nni_url_free(dst); return (NNG_ENOMEM); } -#undef URL_COPYSTR *dstp = dst; return (0); } + +#undef URL_COPYSTR diff --git a/src/core/url.h b/src/core/url.h index b96401bd..2358f6ba 100644 --- a/src/core/url.h +++ b/src/core/url.h @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. +// Copyright 2019 Staysail Systems, Inc. // Copyright 2018 Capitar IT Group BV // // This software is supplied under the terms of the MIT License, a -- cgit v1.2.3-70-g09d2