aboutsummaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2017-11-27 14:21:20 -0800
committerGarrett D'Amore <garrett@damore.org>2017-12-26 15:31:53 -0800
commit93db6fe3aaff421d61a15993ba6827b742ab00d1 (patch)
treed4d6372cb5d606ba9bcdb60b88b6271086940895 /src/core
parentc9bf5a76b0d6aead6ae91af71ada51a17881ac0a (diff)
downloadnng-93db6fe3aaff421d61a15993ba6827b742ab00d1.tar.gz
nng-93db6fe3aaff421d61a15993ba6827b742ab00d1.tar.bz2
nng-93db6fe3aaff421d61a15993ba6827b742ab00d1.zip
fixes #2 Websocket transport
This is a rather large changeset -- it fundamentally adds websocket transport, but as part of this changeset we added a generic framework for both HTTP and websocket. We also made some supporting changes to the core, such as changing the way timeouts work for AIOs and adding additional state keeping for AIOs, and adding a common framework for deferred finalization (to avoid certain kinds of circular deadlocks during resource cleanup). We also invented a new initialization framework so that we can avoid wiring in knowledge about them into the master initialization framework. The HTTP framework is not yet complete, but it is good enough for simple static serving and building additional services on top of -- including websocket. We expect both websocket and HTTP support to evolve considerably, and so these are not part of the public API yet. Property support for the websocket transport (in particular address properties) is still missing, as is support for TLS. The websocket transport here is a bit more robust than the original nanomsg implementation, as it supports multiple sockets listening at the same port sharing the same HTTP server instance, discriminating between them based on URI (and possibly the virtual host). Websocket is enabled by default at present, and work to conditionalize HTTP and websocket further (to minimize bloat) is still pending.
Diffstat (limited to 'src/core')
-rw-r--r--src/core/aio.c70
-rw-r--r--src/core/aio.h50
-rw-r--r--src/core/defs.h3
-rw-r--r--src/core/device.c2
-rw-r--r--src/core/endpt.c4
-rw-r--r--src/core/init.c52
-rw-r--r--src/core/init.h23
-rw-r--r--src/core/list.c7
-rw-r--r--src/core/message.c1
-rw-r--r--src/core/nng_impl.h1
-rw-r--r--src/core/reap.c89
-rw-r--r--src/core/reap.h36
-rw-r--r--src/core/socket.c30
-rw-r--r--src/core/socket.h18
-rw-r--r--src/core/strs.c91
-rw-r--r--src/core/strs.h3
-rw-r--r--src/core/transport.c72
-rw-r--r--src/core/transport.h3
18 files changed, 492 insertions, 63 deletions
diff --git a/src/core/aio.c b/src/core/aio.c
index cec2ff7c..6ce5641d 100644
--- a/src/core/aio.c
+++ b/src/core/aio.c
@@ -1,6 +1,7 @@
//
// Copyright 2017 Garrett D'Amore <garrett@damore.org>
// Copyright 2017 Capitar IT Group BV <info@capitar.com>
+// Copyright 2017 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
@@ -66,7 +67,8 @@ nni_aio_init(nni_aio **aiop, nni_cb cb, void *arg)
}
memset(aio, 0, sizeof(*aio));
nni_cv_init(&aio->a_cv, &nni_aio_lk);
- aio->a_expire = NNI_TIME_NEVER;
+ aio->a_expire = NNI_TIME_NEVER;
+ aio->a_timeout = NNG_DURATION_INFINITE;
if (arg == NULL) {
arg = aio;
}
@@ -116,9 +118,9 @@ nni_aio_stop(nni_aio *aio)
}
void
-nni_aio_set_timeout(nni_aio *aio, nni_time when)
+nni_aio_set_timeout(nni_aio *aio, nni_duration when)
{
- aio->a_expire = when;
+ aio->a_timeout = when;
}
void
@@ -158,15 +160,54 @@ nni_aio_get_ep(nni_aio *aio)
}
void
-nni_aio_set_data(nni_aio *aio, void *data)
+nni_aio_set_data(nni_aio *aio, int index, void *data)
{
- aio->a_data = data;
+ if ((index >= 0) && (index < NNI_NUM_ELEMENTS(aio->a_user_data))) {
+ aio->a_user_data[index] = data;
+ }
}
void *
-nni_aio_get_data(nni_aio *aio)
+nni_aio_get_data(nni_aio *aio, int index)
+{
+ if ((index >= 0) && (index < NNI_NUM_ELEMENTS(aio->a_user_data))) {
+ return (aio->a_user_data[index]);
+ }
+ return (NULL);
+}
+
+void
+nni_aio_set_input(nni_aio *aio, int index, void *data)
{
- return (aio->a_data);
+ if ((index >= 0) && (index < NNI_NUM_ELEMENTS(aio->a_inputs))) {
+ aio->a_inputs[index] = data;
+ }
+}
+
+void *
+nni_aio_get_input(nni_aio *aio, int index)
+{
+ if ((index >= 0) && (index < NNI_NUM_ELEMENTS(aio->a_inputs))) {
+ return (aio->a_inputs[index]);
+ }
+ return (NULL);
+}
+
+void
+nni_aio_set_output(nni_aio *aio, int index, void *data)
+{
+ if ((index >= 0) && (index < NNI_NUM_ELEMENTS(aio->a_outputs))) {
+ aio->a_outputs[index] = data;
+ }
+}
+
+void *
+nni_aio_get_output(nni_aio *aio, int index)
+{
+ if ((index >= 0) && (index < NNI_NUM_ELEMENTS(aio->a_outputs))) {
+ return (aio->a_outputs[index]);
+ }
+ return (NULL);
}
int
@@ -219,8 +260,21 @@ nni_aio_start(nni_aio *aio, nni_aio_cancelfn cancelfn, void *data)
aio->a_prov_cancel = cancelfn;
aio->a_prov_data = data;
aio->a_active = 1;
- if (aio->a_expire != NNI_TIME_NEVER) {
+
+ // Convert the relative timeout to an absolute timeout.
+ switch (aio->a_timeout) {
+ case NNG_DURATION_ZERO:
+ aio->a_expire = NNI_TIME_ZERO;
+ nni_aio_expire_add(aio);
+ break;
+ case NNG_DURATION_INFINITE:
+ case NNG_DURATION_DEFAULT:
+ aio->a_expire = NNI_TIME_NEVER;
+ break;
+ default:
+ aio->a_expire = nni_clock() + aio->a_timeout;
nni_aio_expire_add(aio);
+ break;
}
nni_mtx_unlock(&nni_aio_lk);
return (0);
diff --git a/src/core/aio.h b/src/core/aio.h
index 3bdcf433..c4c09421 100644
--- a/src/core/aio.h
+++ b/src/core/aio.h
@@ -1,6 +1,7 @@
//
// Copyright 2017 Garrett D'Amore <garrett@damore.org>
// Copyright 2017 Capitar IT Group BV <info@capitar.com>
+// Copyright 2017 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
@@ -22,9 +23,10 @@ typedef void (*nni_aio_cancelfn)(nni_aio *, int);
// An nni_aio is an async I/O handle.
struct nni_aio {
- int a_result; // Result code (nng_errno)
- size_t a_count; // Bytes transferred (I/O only)
- nni_time a_expire;
+ int a_result; // Result code (nng_errno)
+ size_t a_count; // Bytes transferred (I/O only)
+ nni_time a_expire; // Absolute timeout
+ nni_duration a_timeout; // Relative timeout
// These fields are private to the aio framework.
nni_cv a_cv;
@@ -35,8 +37,6 @@ struct nni_aio {
unsigned a_expiring : 1; // expiration callback in progress
unsigned a_waiting : 1; // a thread is waiting for this to finish
unsigned a_synch : 1; // run completion synchronously
- unsigned a_reltime : 1; // expiration time is relative
- unsigned a_pad : 25; // ensure 32-bit alignment
nni_task a_task;
// Read/write operations.
@@ -53,13 +53,21 @@ struct nni_aio {
// Resolver operations.
nni_sockaddr *a_addr;
- // Extra user data.
- void *a_data;
+ // User scratch data. Consumers may store values here, which
+ // must be preserved by providers and the framework.
+ void *a_user_data[4];
+
+ // Operation inputs & outputs. Up to 4 inputs and 4 outputs may be
+ // specified. The semantics of these will vary, and depend on the
+ // specific operation.
+ void *a_inputs[4];
+ void *a_outputs[4];
// Provider-use fields.
nni_aio_cancelfn a_prov_cancel;
void * a_prov_data;
nni_list_node a_prov_node;
+ void * a_prov_extra[4]; // Extra data used by provider
// Expire node.
nni_list_node a_expire_node;
@@ -96,12 +104,32 @@ extern void nni_aio_stop(nni_aio *);
// nni_aio_set_data sets user data. This should only be done by the
// consumer, initiating the I/O. The intention is to be able to store
// additional data for use when the operation callback is executed.
-extern void nni_aio_set_data(nni_aio *, void *);
+// The index represents the "index" at which to store the data. A maximum
+// of 4 elements can be stored with the (index >= 0 && index < 4).
+extern void nni_aio_set_data(nni_aio *, int, void *);
// nni_aio_get_data returns the user data that was previously stored
// with nni_aio_set_data.
-extern void *nni_aio_get_data(nni_aio *);
+extern void *nni_aio_get_data(nni_aio *, int);
+
+// nni_set_input sets input parameters on the AIO. The semantic details
+// of this will be determined by the specific AIO operation. AIOs can
+// carry up to 4 input parameters.
+extern void nni_aio_set_input(nni_aio *, int, void *);
+
+// nni_get_input returns the input value stored by nni_aio_set_input.
+extern void *nni_aio_get_input(nni_aio *, int);
+
+// nni_set_output sets output results on the AIO, allowing providers to
+// return results to consumers. The semantic details are determined by
+// the AIO operation. Up to 4 outputs can be carried on an AIO.
+extern void nni_aio_set_output(nni_aio *, int, void *);
+
+// nni_get_output returns an output previously stored on the AIO.
+extern void *nni_aio_get_output(nni_aio *, int);
+// XXX: These should be refactored in terms of the generic inputs and
+// outputs.
extern void nni_aio_set_msg(nni_aio *, nni_msg *);
extern nni_msg *nni_aio_get_msg(nni_aio *);
extern void nni_aio_set_pipe(nni_aio *, void *);
@@ -122,10 +150,10 @@ extern void * nni_aio_get_ep(nni_aio *);
// completion callback.
void nni_aio_set_synch(nni_aio *);
-// nni_aio_set_timeout sets the timeout (absolute) when the AIO will
+// nni_aio_set_timeout sets the timeout (relative) when the AIO will
// be canceled. The cancelation does not happen until after nni_aio_start
// is called.
-extern void nni_aio_set_timeout(nni_aio *, nni_time);
+extern void nni_aio_set_timeout(nni_aio *, nni_duration);
// nni_aio_result returns the result code (0 on success, or an NNG errno)
// for the operation. It is only valid to call this when the operation is
diff --git a/src/core/defs.h b/src/core/defs.h
index 5a9ded92..3a714f85 100644
--- a/src/core/defs.h
+++ b/src/core/defs.h
@@ -25,6 +25,9 @@
#define NNI_ASSERT(x)
#endif
+// Returns the size of an array in elements. (Convenience.)
+#define NNI_NUM_ELEMENTS(x) (sizeof(x) / sizeof((x)[0]))
+
// These types are common but have names shared with user space.
typedef struct nng_msg nni_msg;
typedef struct nng_sockaddr nni_sockaddr;
diff --git a/src/core/device.c b/src/core/device.c
index 2e000d7e..e6b75897 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -159,7 +159,7 @@ nni_device_init(nni_device_data **dp, nni_sock *s1, nni_sock *s2)
return (rv);
}
- nni_aio_set_timeout(p->aio, NNI_TIME_NEVER);
+ nni_aio_set_timeout(p->aio, NNG_DURATION_INFINITE);
}
dd->npath = npath;
*dp = dd;
diff --git a/src/core/endpt.c b/src/core/endpt.c
index fa30bf77..3058f5c0 100644
--- a/src/core/endpt.c
+++ b/src/core/endpt.c
@@ -333,8 +333,8 @@ nni_ep_tmo_start(nni_ep *ep)
// have a statistically perfect distribution with the modulo of
// the random number, but this really doesn't matter.
- nni_aio_set_timeout(ep->ep_tmo_aio,
- nni_clock() + (backoff ? nni_random() % backoff : 0));
+ nni_aio_set_timeout(
+ ep->ep_tmo_aio, (backoff ? nni_random() % backoff : 0));
ep->ep_tmo_run = 1;
if (nni_aio_start(ep->ep_tmo_aio, nni_ep_tmo_cancel, ep) != 0) {
diff --git a/src/core/init.c b/src/core/init.c
index 4a7fd974..cb6dbeee 100644
--- a/src/core/init.c
+++ b/src/core/init.c
@@ -1,6 +1,7 @@
//
// Copyright 2017 Garrett D'Amore <garrett@damore.org>
// Copyright 2017 Capitar IT Group BV <info@capitar.com>
+// Copyright 2017 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
@@ -10,15 +11,25 @@
#include "core/nng_impl.h"
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
+static nni_mtx nni_init_mtx;
+static nni_list nni_init_list;
+static bool nni_inited = false;
+
static int
nni_init_helper(void)
{
int rv;
+ nni_mtx_init(&nni_init_mtx);
+ NNI_LIST_INIT(&nni_init_list, nni_initializer, i_node);
+ nni_inited = true;
+
if (((rv = nni_taskq_sys_init()) != 0) ||
+ ((rv = nni_reap_sys_init()) != 0) ||
((rv = nni_timer_sys_init()) != 0) ||
((rv = nni_aio_sys_init()) != 0) ||
((rv = nni_random_sys_init()) != 0) ||
@@ -29,6 +40,7 @@ nni_init_helper(void)
((rv = nni_tran_sys_init()) != 0)) {
nni_fini();
}
+
return (rv);
}
@@ -41,14 +53,54 @@ nni_init(void)
void
nni_fini(void)
{
+ if (!nni_inited) {
+ return;
+ }
+ if (!nni_list_empty(&nni_init_list)) {
+ nni_initializer *init;
+
+ nni_mtx_lock(&nni_init_mtx);
+ while ((init = nni_list_first(&nni_init_list)) != NULL) {
+ if (init->i_fini != NULL) {
+ init->i_fini();
+ }
+ init->i_once = 0;
+ nni_list_remove(&nni_init_list, init);
+ }
+ nni_mtx_unlock(&nni_init_mtx);
+ }
nni_tran_sys_fini();
nni_proto_sys_fini();
nni_pipe_sys_fini();
nni_ep_sys_fini();
nni_sock_sys_fini();
nni_random_sys_fini();
+ nni_reap_sys_fini(); // must be before timer and aio (expire)
nni_aio_sys_fini();
nni_timer_sys_fini();
nni_taskq_sys_fini();
+
+ nni_mtx_fini(&nni_init_mtx);
nni_plat_fini();
+ nni_inited = false;
+}
+
+int
+nni_initialize(nni_initializer *init)
+{
+ int rv;
+ if (init->i_once) {
+ return (0);
+ }
+ nni_mtx_lock(&nni_init_mtx);
+ if (init->i_once) {
+ nni_mtx_unlock(&nni_init_mtx);
+ return (0);
+ }
+ if ((rv = init->i_init()) == 0) {
+ init->i_once = 1;
+ nni_list_append(&nni_init_list, init);
+ }
+ nni_mtx_unlock(&nni_init_mtx);
+ return (rv);
}
diff --git a/src/core/init.h b/src/core/init.h
index ffcebf64..d21bb4c5 100644
--- a/src/core/init.h
+++ b/src/core/init.h
@@ -1,5 +1,7 @@
//
-// Copyright 2016 Garrett D'Amore <garrett@damore.org>
+// Copyright 2017 Garrett D'Amore <garrett@damore.org>
+// Copyright 2017 Capitar IT Group BV <info@capitar.com>
+// Copyright 2017 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
@@ -21,4 +23,23 @@ int nni_init(void);
// that all resources used by the library are released back to the system.
void nni_fini(void);
+typedef struct nni_initializer {
+ int (*i_init)(void); // i_init is called exactly once
+ void (*i_fini)(void); // i_fini is called on shutdown
+ int i_once; // private -- initialize to zero
+ nni_list_node i_node; // private -- initialize to zero
+} nni_initializer;
+
+// nni_initialize will call the initialization routine exactly once. This is
+// done efficiently, so that if the caller has initialized already, then
+// subsequent calls are "cheap" (no synchronization cost). The initialization
+// function must not itself cause any further calls to nni_initialize; the
+// function should limit itself to initialization of locks and static data
+// structures. When shutting down, the finalizer will be called. The
+// order in which finalizers are called is unspecified.
+//
+// An initializer may fail (due to resource exhaustion), in which case the
+// return value of nni_initialize will be non-zero.
+int nni_initialize(nni_initializer *);
+
#endif // CORE_INIT_H
diff --git a/src/core/list.c b/src/core/list.c
index 8b26b64a..f489a705 100644
--- a/src/core/list.c
+++ b/src/core/list.c
@@ -1,5 +1,7 @@
//
// Copyright 2017 Garrett D'Amore <garrett@damore.org>
+// Copyright 2017 Capitar IT Group BV <info@capitar.com>
+// Copyright 2017 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
@@ -151,7 +153,10 @@ nni_list_active(nni_list *list, void *item)
int
nni_list_empty(nni_list *list)
{
- return (list->ll_head.ln_next == &list->ll_head);
+ // The first check ensures that we treat an uninitialized list
+ // as empty. This use useful for statically initialized lists.
+ return ((list->ll_head.ln_next == NULL) ||
+ (list->ll_head.ln_next == &list->ll_head));
}
int
diff --git a/src/core/message.c b/src/core/message.c
index b44dfdf6..35153f01 100644
--- a/src/core/message.c
+++ b/src/core/message.c
@@ -9,7 +9,6 @@
//
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
#include "core/nng_impl.h"
diff --git a/src/core/nng_impl.h b/src/core/nng_impl.h
index 87dd2de1..1f2297c1 100644
--- a/src/core/nng_impl.h
+++ b/src/core/nng_impl.h
@@ -38,6 +38,7 @@
#include "core/panic.h"
#include "core/protocol.h"
#include "core/random.h"
+#include "core/reap.h"
#include "core/strs.h"
#include "core/taskq.h"
#include "core/thread.h"
diff --git a/src/core/reap.c b/src/core/reap.c
new file mode 100644
index 00000000..8191dba3
--- /dev/null
+++ b/src/core/reap.c
@@ -0,0 +1,89 @@
+//
+// Copyright 2017 Garrett D'Amore <garrett@damore.org>
+// Copyright 2017 Capitar IT Group BV <info@capitar.com>
+//
+// 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.
+//
+
+#include "core/nng_impl.h"
+
+#include "reap.h"
+
+#include <stdbool.h>
+
+static nni_list nni_reap_list;
+static nni_mtx nni_reap_mtx;
+static nni_cv nni_reap_cv;
+static bool nni_reap_exit = false;
+static nni_thr nni_reap_thr;
+
+static void
+nni_reap_stuff(void *notused)
+{
+ NNI_ARG_UNUSED(notused);
+
+ nni_mtx_lock(&nni_reap_mtx);
+ for (;;) {
+ nni_reap_item *item;
+ if ((item = nni_list_first(&nni_reap_list)) != NULL) {
+ nni_list_remove(&nni_reap_list, item);
+ nni_mtx_unlock(&nni_reap_mtx);
+
+ item->r_func(item->r_ptr);
+ nni_mtx_lock(&nni_reap_mtx);
+ continue;
+ }
+
+ if (nni_reap_exit) {
+ break;
+ }
+
+ nni_cv_wait(&nni_reap_cv);
+ }
+ nni_mtx_unlock(&nni_reap_mtx);
+}
+
+void
+nni_reap(nni_reap_item *item, nni_cb func, void *ptr)
+{
+ nni_mtx_lock(&nni_reap_mtx);
+ item->r_func = func;
+ item->r_ptr = ptr;
+ nni_list_append(&nni_reap_list, item);
+ nni_cv_wake(&nni_reap_cv);
+ nni_mtx_unlock(&nni_reap_mtx);
+}
+
+int
+nni_reap_sys_init(void)
+{
+ int rv;
+
+ NNI_LIST_INIT(&nni_reap_list, nni_reap_item, r_link);
+ nni_mtx_init(&nni_reap_mtx);
+ nni_cv_init(&nni_reap_cv, &nni_reap_mtx);
+ nni_reap_exit = false;
+
+ // If this fails, we don't fail init, instead we will try to
+ // start up at reap time.
+ if ((rv = nni_thr_init(&nni_reap_thr, nni_reap_stuff, NULL)) != 0) {
+ nni_cv_fini(&nni_reap_cv);
+ nni_mtx_fini(&nni_reap_mtx);
+ return (rv);
+ }
+ nni_thr_run(&nni_reap_thr);
+ return (0);
+}
+
+void
+nni_reap_sys_fini(void)
+{
+ nni_mtx_lock(&nni_reap_mtx);
+ nni_reap_exit = true;
+ nni_cv_wake(&nni_reap_cv);
+ nni_mtx_unlock(&nni_reap_mtx);
+ nni_thr_fini(&nni_reap_thr);
+}
diff --git a/src/core/reap.h b/src/core/reap.h
new file mode 100644
index 00000000..fbc008b2
--- /dev/null
+++ b/src/core/reap.h
@@ -0,0 +1,36 @@
+//
+// Copyright 2017 Garrett D'Amore <garrett@damore.org>
+// Copyright 2017 Capitar IT Group BV <info@capitar.com>
+//
+// 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_REAP_H
+#define CORE_REAP_H
+
+#include "core/defs.h"
+#include "core/list.h"
+
+// nni_reap_item is defined here so that it can be inlined into
+// structures. Callers must access its members directly.
+typedef struct nni_reap_item {
+ nni_list_node r_link;
+ void * r_ptr;
+ nni_cb r_func;
+} nni_reap_item;
+
+// nni_reap performs an asynchronous reap of an item. This allows functions
+// it calls to acquire locks or resources without worrying about deadlocks
+// (such as from a completion callback.) The called function should avoid
+// blocking for too long if possible, since only one reap thread is present
+// in the system. The intended usage is for an nni_reap_item to be a member
+// of the structure to be reaped, and and then this function is called to
+// finalize it.
+extern void nni_reap(nni_reap_item *, nni_cb, void *);
+extern int nni_reap_sys_init(void);
+extern void nni_reap_sys_fini(void);
+
+#endif // CORE_REAP_H
diff --git a/src/core/socket.c b/src/core/socket.c
index 54e1bd6a..409e4f66 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -775,22 +775,8 @@ nni_sock_closeall(void)
static void
nni_sock_normalize_expiration(nni_aio *aio, nni_duration def)
{
- if (aio->a_reltime) {
- if (aio->a_expire == (nni_time) -2) {
- aio->a_expire = def;
- }
- switch (aio->a_expire) {
- case (nni_time) 0:
- aio->a_expire = NNI_TIME_ZERO;
- break;
- case (nni_time) -1:
- aio->a_expire = NNI_TIME_NEVER;
- break;
- default:
- aio->a_expire = nni_clock() + aio->a_expire;
- break;
- }
- aio->a_reltime = 0;
+ if (aio->a_timeout == (nni_duration) -2) {
+ aio->a_timeout = def;
}
}
@@ -821,6 +807,18 @@ nni_sock_peer(nni_sock *sock)
return (sock->s_peer_id.p_id);
}
+const char *
+nni_sock_proto_name(nni_sock *sock)
+{
+ return (sock->s_self_id.p_name);
+}
+
+const char *
+nni_sock_peer_name(nni_sock *sock)
+{
+ return (sock->s_peer_id.p_name);
+}
+
void
nni_sock_reconntimes(nni_sock *sock, nni_duration *rcur, nni_duration *rmax)
{
diff --git a/src/core/socket.h b/src/core/socket.h
index 52310ef3..37c67436 100644
--- a/src/core/socket.h
+++ b/src/core/socket.h
@@ -14,14 +14,16 @@
extern int nni_sock_sys_init(void);
extern void nni_sock_sys_fini(void);
-extern int nni_sock_find(nni_sock **, uint32_t);
-extern void nni_sock_rele(nni_sock *);
-extern int nni_sock_open(nni_sock **, const nni_proto *);
-extern void nni_sock_close(nni_sock *);
-extern void nni_sock_closeall(void);
-extern int nni_sock_shutdown(nni_sock *);
-extern uint16_t nni_sock_proto(nni_sock *);
-extern uint16_t nni_sock_peer(nni_sock *);
+extern int nni_sock_find(nni_sock **, uint32_t);
+extern void nni_sock_rele(nni_sock *);
+extern int nni_sock_open(nni_sock **, const nni_proto *);
+extern void nni_sock_close(nni_sock *);
+extern void nni_sock_closeall(void);
+extern int nni_sock_shutdown(nni_sock *);
+extern uint16_t nni_sock_proto(nni_sock *);
+extern uint16_t nni_sock_peer(nni_sock *);
+extern const char *nni_sock_proto_name(nni_sock *);
+extern const char *nni_sock_peer_name(nni_sock *);
extern int nni_sock_setopt(nni_sock *, const char *, const void *, size_t);
extern int nni_sock_getopt(nni_sock *, const char *, void *, size_t *);
extern int nni_sock_recvmsg(nni_sock *, nni_msg **, int);
diff --git a/src/core/strs.c b/src/core/strs.c
index 6cee605b..a03c0bb5 100644
--- a/src/core/strs.c
+++ b/src/core/strs.c
@@ -8,6 +8,9 @@
// found online at https://opensource.org/licenses/MIT.
//
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -17,35 +20,28 @@
// part of standard C99. (C11 has added some things here, but we cannot
// count on them.)
+// Note that we supply our own version of strdup and strfree unconditionally,
+// so that these can be freed with nni_free(strlen(s)+1) if desired. (Likewise
+// a string buffer allocated with nni_alloc can be freed with nni_strfree
+// provided the length is correct.)
+
char *
nni_strdup(const char *src)
{
-#ifdef NNG_HAVE_STRDUP
-#ifdef _WIN32
- return (_strdup(src));
-#else
- return (strdup(src));
-#endif
-#else
char * dst;
- size_t len = strlen(src);
+ size_t len = strlen(src) + 1;
if ((dst = nni_alloc(len)) != NULL) {
memcpy(dst, src, len);
}
return (dst);
-#endif
}
void
nni_strfree(char *s)
{
if (s != NULL) {
-#ifdef NNG_HAVE_STRDUP
- free(s);
-#else
nni_free(s, strlen(s) + 1);
-#endif
}
}
@@ -114,3 +110,72 @@ nni_strnlen(const char *s, size_t len)
return (n);
#endif
}
+
+char *
+nni_strcasestr(const char *s1, const char *s2)
+{
+#ifdef NNG_HAVE_STRCASESTR
+ return (strcasestr(s1, s2));
+#else
+ const char *t1, *t2;
+ while (*s1) {
+ for (t1 = s1, t2 = s2; *t1 && *t2; t2++, t1++) {
+ if (tolower(*t1) != tolower(*t2)) {
+ break;
+ }
+ }
+ if (*t2 == 0) {
+ return ((char *) s1);
+ }
+ s1++;
+ }
+ return (NULL);
+#endif
+}
+
+int
+nni_strncasecmp(const char *s1, const char *s2, size_t n)
+{
+#ifdef NNG_HAVE_STRNCASECMP
+#ifdef _WIN32
+ return (_strnicmp(s1, s2, n));
+#else
+ return (strncasecmp(s1, s2, n));
+#endif
+#else
+ for (int i = 0; i < n; i++) {
+ uint8_t c1 = (uint8_t) tolower(*s1++);
+ uint8_t c2 = (uint8_t) tolower(*s2++);
+ if (c1 == c2) {
+ if (c1 == 0) {
+ return (0);
+ }
+ continue;
+ }
+ return (c1 < c2 ? -1 : 1);
+ }
+ return (0);
+#endif
+}
+
+int
+nni_asprintf(char **sp, const char *fmt, ...)
+{
+ va_list ap;
+ size_t len;
+ char * s;
+
+ va_start(ap, fmt);
+ len = vsnprintf(NULL, 0, fmt, ap);
+ va_end(ap);
+ len++;
+
+ if ((s = nni_alloc(len)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ va_start(ap, fmt);
+ (void) vsnprintf(s, len, fmt, ap);
+ va_end(ap);
+ *sp = s;
+ return (0);
+} \ No newline at end of file
diff --git a/src/core/strs.h b/src/core/strs.h
index 42ec4997..3b369fe4 100644
--- a/src/core/strs.h
+++ b/src/core/strs.h
@@ -18,5 +18,8 @@ extern void nni_strfree(char *);
extern size_t nni_strlcpy(char *, const char *, size_t);
extern size_t nni_strlcat(char *, const char *, size_t);
extern size_t nni_strnlen(const char *, size_t);
+extern char * nni_strcasestr(const char *, const char *);
+extern int nni_strncasecmp(const char *, const char *, size_t);
+extern int nni_asprintf(char **, const char *, ...);
#endif // CORE_STRS_H
diff --git a/src/core/transport.c b/src/core/transport.c
index 359b03fd..31da773f 100644
--- a/src/core/transport.c
+++ b/src/core/transport.c
@@ -13,6 +13,7 @@
#include "transport/ipc/ipc.h"
#include "transport/tcp/tcp.h"
#include "transport/tls/tls.h"
+#include "transport/ws/websocket.h"
#include "transport/zerotier/zerotier.h"
#include <stdio.h>
@@ -105,6 +106,72 @@ nni_tran_find(const char *addr)
return (NULL);
}
+// nni_tran_parse_host_port is a convenience routine to parse the host portion
+// of a URL (which includes a DNS name or IP address and an optional service
+// name or port, separated by a colon) into its host and port name parts. It
+// understands IPv6 address literals when surrounded by brackets ([]).
+// If either component is empty, then NULL is passed back for the value,
+// otherwise a string suitable for freeing with nni_strfree is supplied.
+int
+nni_tran_parse_host_port(const char *pair, char **hostp, char **portp)
+{
+ const char *hstart;
+ const char *pstart;
+ char * host;
+ char * port;
+ size_t hlen, plen;
+
+ if (pair[0] == '[') {
+ hstart = pair + 1;
+ hlen = 0;
+ while (hstart[hlen] != ']') {
+ if (hstart[hlen] == '\0') {
+ return (NNG_EADDRINVAL);
+ }
+ hlen++;
+ }
+ pstart = hstart + hlen + 1; // skip over the trailing ']'
+ } else {
+ // Normal thing.
+ hstart = pair;
+ hlen = 0;
+ while ((hstart[hlen] != ':') && (hstart[hlen] != '\0')) {
+ hlen++;
+ }
+ pstart = hstart + hlen;
+ }
+ if (pstart[0] == ':') {
+ pstart++;
+ }
+ plen = strlen(pstart);
+
+ host = NULL;
+ if (hostp) {
+ if ((hlen > 1) || ((hlen == 1) && (*hstart != '*'))) {
+ if ((host = nni_alloc(hlen + 1)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ memcpy(host, hstart, hlen);
+ host[hlen] = '\0';
+ }
+ }
+
+ port = NULL;
+ if ((plen != 0) && (portp)) {
+ if ((port = nni_strdup(pstart)) == NULL) {
+ nni_strfree(host);
+ return (NNG_ENOMEM);
+ }
+ }
+ if (hostp) {
+ *hostp = host;
+ }
+ if (portp) {
+ *portp = port;
+ }
+ return (0);
+}
+
int
nni_tran_chkopt(const char *name, const void *v, size_t sz)
{
@@ -154,9 +221,12 @@ static nni_tran_ctor nni_tran_ctors[] = {
#ifdef NNG_HAVE_TLS
nng_tls_register,
#endif
-#ifdef NNI_HAVE_ZEROTIER
+#ifdef NNG_HAVE_ZEROTIER
nng_zt_register,
#endif
+#ifdef NNG_HAVE_WEBSOCKET
+ nng_ws_register,
+#endif
NULL,
};
diff --git a/src/core/transport.h b/src/core/transport.h
index b82e2c92..e8a1f620 100644
--- a/src/core/transport.h
+++ b/src/core/transport.h
@@ -163,6 +163,9 @@ struct nni_tran_pipe {
nni_tran_pipe_option *p_options;
};
+// Utility for transports.
+extern int nni_tran_parse_host_port(const char *, char **, char **);
+
// These APIs are used by the framework internally, and not for use by
// transport implementations.
extern nni_tran *nni_tran_find(const char *);