aboutsummaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
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 *);