diff options
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/aio.c | 70 | ||||
| -rw-r--r-- | src/core/aio.h | 50 | ||||
| -rw-r--r-- | src/core/defs.h | 3 | ||||
| -rw-r--r-- | src/core/device.c | 2 | ||||
| -rw-r--r-- | src/core/endpt.c | 4 | ||||
| -rw-r--r-- | src/core/init.c | 52 | ||||
| -rw-r--r-- | src/core/init.h | 23 | ||||
| -rw-r--r-- | src/core/list.c | 7 | ||||
| -rw-r--r-- | src/core/message.c | 1 | ||||
| -rw-r--r-- | src/core/nng_impl.h | 1 | ||||
| -rw-r--r-- | src/core/reap.c | 89 | ||||
| -rw-r--r-- | src/core/reap.h | 36 | ||||
| -rw-r--r-- | src/core/socket.c | 30 | ||||
| -rw-r--r-- | src/core/socket.h | 18 | ||||
| -rw-r--r-- | src/core/strs.c | 91 | ||||
| -rw-r--r-- | src/core/strs.h | 3 | ||||
| -rw-r--r-- | src/core/transport.c | 72 | ||||
| -rw-r--r-- | src/core/transport.h | 3 |
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 *); |
