aboutsummaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2019-01-21 22:40:10 -0800
committerGarrett D'Amore <garrett@damore.org>2019-02-16 19:22:27 -0800
commit5cf750697624d4fd63cfe26921209d7c30e1a2d2 (patch)
treebf11695e5f1ec5e400c87da0cc6ff23935a2eeff /src/core
parentca655b9db689ee0e655248b1a9f166b8db6cc984 (diff)
downloadnng-5cf750697624d4fd63cfe26921209d7c30e1a2d2.tar.gz
nng-5cf750697624d4fd63cfe26921209d7c30e1a2d2.tar.bz2
nng-5cf750697624d4fd63cfe26921209d7c30e1a2d2.zip
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.
Diffstat (limited to 'src/core')
-rw-r--r--src/core/aio.c20
-rw-r--r--src/core/aio.h5
-rw-r--r--src/core/nng_impl.h1
-rw-r--r--src/core/options.c16
-rw-r--r--src/core/options.h9
-rw-r--r--src/core/platform.h157
-rw-r--r--src/core/stream.c366
-rw-r--r--src/core/stream.h69
-rw-r--r--src/core/tcp.h24
-rw-r--r--src/core/transport.c13
-rw-r--r--src/core/transport.h6
-rw-r--r--src/core/url.c45
-rw-r--r--src/core/url.h2
13 files changed, 568 insertions, 165 deletions
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. <info@staysail.tech>
+// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
//
// 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. <info@staysail.tech>
+// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
//
// 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. <info@staysail.tech>
+// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
// Copyright 2018 Devolutions <info@devolutions.net>
//
@@ -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. <info@staysail.tech>
+//
+// 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 <string.h>
+
+#include "core/nng_impl.h"
+#include <nng/supplemental/tls/tls.h>
+
+#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. <info@staysail.tech>
+//
+// 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. <info@staysail.tech>
+//
+// 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. <info@staysail.tech>
+// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
+// Copyright 2019 Devolutions <info@devolutions.net>
//
// 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. <info@staysail.tech>
+// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
//
// This software is supplied under the terms of the MIT License, a