diff options
| author | Garrett D'Amore <garrett@damore.org> | 2017-08-17 23:57:09 -0700 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2017-08-18 01:57:35 -0700 |
| commit | 446b150032f24c34644f0ab91ac6ab9206250865 (patch) | |
| tree | e47b4e3812016716187201961c43b9e407a3ed50 | |
| parent | 76c1fc80c931b086493835d037245ebbb5f8d406 (diff) | |
| download | nng-446b150032f24c34644f0ab91ac6ab9206250865.tar.gz nng-446b150032f24c34644f0ab91ac6ab9206250865.tar.bz2 nng-446b150032f24c34644f0ab91ac6ab9206250865.zip | |
Endpoint API completely implemented.
This supports creating listeners and dialers, managing options
on them (though only a few options are supported at present),
starting them and closing them, all independently.
| -rw-r--r-- | src/core/endpt.c | 33 | ||||
| -rw-r--r-- | src/core/endpt.h | 2 | ||||
| -rw-r--r-- | src/core/options.c | 79 | ||||
| -rw-r--r-- | src/core/options.h | 7 | ||||
| -rw-r--r-- | src/core/pipe.c | 4 | ||||
| -rw-r--r-- | src/core/socket.c | 299 | ||||
| -rw-r--r-- | src/core/socket.h | 3 | ||||
| -rw-r--r-- | src/core/transport.c | 21 | ||||
| -rw-r--r-- | src/core/transport.h | 6 | ||||
| -rw-r--r-- | src/nng.c | 167 | ||||
| -rw-r--r-- | src/nng.h | 37 | ||||
| -rw-r--r-- | src/transport/ipc/ipc.c | 52 | ||||
| -rw-r--r-- | src/transport/tcp/tcp.c | 64 | ||||
| -rw-r--r-- | tests/sock.c | 192 |
14 files changed, 847 insertions, 119 deletions
diff --git a/src/core/endpt.c b/src/core/endpt.c index 34debc0e..f90bd068 100644 --- a/src/core/endpt.c +++ b/src/core/endpt.c @@ -418,6 +418,7 @@ nni_ep_dial(nni_ep *ep, int flags) } if ((flags & NNG_FLAG_NONBLOCK) != 0) { + ep->ep_started = 1; nni_ep_con_start(ep); nni_mtx_unlock(&ep->ep_mtx); return (0); @@ -567,6 +568,38 @@ nni_ep_pipe_remove(nni_ep *ep, nni_pipe *pipe) nni_mtx_unlock(&ep->ep_mtx); } +int +nni_ep_setopt(nni_ep *ep, int opt, const void *val, size_t sz, int check) +{ + int rv; + + if (ep->ep_ops.ep_setopt == NULL) { + return (NNG_ENOTSUP); + } + nni_mtx_lock(&ep->ep_mtx); + if (check && ep->ep_started) { + nni_mtx_unlock(&ep->ep_mtx); + return (NNG_ESTATE); + } + rv = ep->ep_ops.ep_setopt(ep->ep_data, opt, val, sz); + nni_mtx_unlock(&ep->ep_mtx); + return (rv); +} + +int +nni_ep_getopt(nni_ep *ep, int opt, void *valp, size_t *szp) +{ + int rv; + + if (ep->ep_ops.ep_getopt == NULL) { + return (NNG_ENOTSUP); + } + nni_mtx_lock(&ep->ep_mtx); + rv = ep->ep_ops.ep_getopt(ep->ep_data, opt, valp, szp); + nni_mtx_unlock(&ep->ep_mtx); + return (rv); +} + void nni_ep_list_init(nni_list *list) { diff --git a/src/core/endpt.h b/src/core/endpt.h index fbb10911..de058d4b 100644 --- a/src/core/endpt.h +++ b/src/core/endpt.h @@ -27,6 +27,8 @@ extern void nni_ep_close(nni_ep *); extern int nni_ep_dial(nni_ep *, int); extern int nni_ep_listen(nni_ep *, int); extern void nni_ep_list_init(nni_list *); +extern int nni_ep_setopt(nni_ep *, int, const void *, size_t, int); +extern int nni_ep_getopt(nni_ep *, int, void *, size_t *); extern int nni_ep_pipe_add(nni_ep *ep, nni_pipe *); extern void nni_ep_pipe_remove(nni_ep *, nni_pipe *); diff --git a/src/core/options.c b/src/core/options.c index b243b262..ecfa437e 100644 --- a/src/core/options.c +++ b/src/core/options.c @@ -13,57 +13,98 @@ #include <string.h> int -nni_setopt_usec(nni_duration *ptr, const void *val, size_t size) +nni_chkopt_usec(const void *v, size_t sz) +{ + nni_duration val; + if (sz != sizeof(val)) { + return (NNG_EINVAL); + } + memcpy(&val, v, sz); + if (val < -1) { + return (NNG_EINVAL); + } + return (0); +} + +int +nni_chkopt_int(const void *v, size_t sz, int minv, int maxv) +{ + int val; + if (sz != sizeof(val)) { + return (NNG_EINVAL); + } + memcpy(&val, v, sz); + if ((val < minv) || (val > maxv)) { + return (NNG_EINVAL); + } + return (0); +} + +int +nni_chkopt_size(const void *v, size_t sz, size_t minv, size_t maxv) +{ + size_t val; + if (sz != sizeof(val)) { + return (NNG_EINVAL); + } + memcpy(&val, v, sz); + if ((val < minv) || (val > maxv)) { + return (NNG_EINVAL); + } + return (0); +} + +int +nni_setopt_usec(nni_duration *dp, const void *v, size_t sz) { nni_duration dur; - if (size != sizeof(*ptr)) { + if (sz != sizeof(*dp)) { return (NNG_EINVAL); } - memcpy(&dur, val, sizeof(dur)); + memcpy(&dur, v, sizeof(dur)); if (dur < -1) { return (NNG_EINVAL); } - *ptr = dur; + *dp = dur; return (0); } int -nni_setopt_int(int *ptr, const void *val, size_t size, int minval, int maxval) +nni_setopt_int(int *ip, const void *v, size_t sz, int minv, int maxv) { - int v; + int i; - if (size != sizeof(v)) { + if (sz != sizeof(i)) { return (NNG_EINVAL); } - memcpy(&v, val, sizeof(v)); - if (v > maxval) { + memcpy(&i, v, sizeof(i)); + if (i > maxv) { return (NNG_EINVAL); } - if (v < minval) { + if (i < minv) { return (NNG_EINVAL); } - *ptr = v; + *ip = i; return (0); } int -nni_setopt_size( - size_t *ptr, const void *val, size_t size, size_t minval, size_t maxval) +nni_setopt_size(size_t *sp, const void *v, size_t sz, size_t minv, size_t maxv) { - size_t v; + size_t val; - if (size != sizeof(v)) { + if (sz != sizeof(val)) { return (NNG_EINVAL); } - memcpy(&v, val, sizeof(v)); - if (v > maxval) { + memcpy(&val, v, sizeof(val)); + if (val > maxv) { return (NNG_EINVAL); } - if (v < minval) { + if (val < minv) { return (NNG_EINVAL); } - *ptr = v; + *sp = val; return (0); } diff --git a/src/core/options.h b/src/core/options.h index 8e6e1edf..4b29c4b6 100644 --- a/src/core/options.h +++ b/src/core/options.h @@ -1,5 +1,6 @@ // -// 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> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -51,4 +52,8 @@ extern int nni_getopt_size(size_t *, void *, size_t *); // nni_getopt_fd obtains a notification file descriptor. extern int nni_getopt_fd(nni_sock *, nni_notifyfd *, int, void *, size_t *); +extern int nni_chkopt_usec(const void *, size_t); +extern int nni_chkopt_int(const void *, size_t, int, int); +extern int nni_chkopt_size(const void *, size_t, size_t, size_t); + #endif // CORE_OPTIONS_H diff --git a/src/core/pipe.c b/src/core/pipe.c index cdbeb6a7..94524da4 100644 --- a/src/core/pipe.c +++ b/src/core/pipe.c @@ -117,7 +117,9 @@ nni_pipe_destroy(nni_pipe *p) if (nni_list_node_active(&p->p_ep_node)) { nni_ep_pipe_remove(p->p_ep, p); } - nni_sock_pipe_remove(p->p_sock, p); + if (nni_list_node_active(&p->p_sock_node)) { + nni_sock_pipe_remove(p->p_sock, p); + } if (p->p_tran_data != NULL) { p->p_tran_ops.p_fini(p->p_tran_data); diff --git a/src/core/socket.c b/src/core/socket.c index 9aa89a2d..79c1602b 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -18,6 +18,13 @@ static nni_list nni_sock_list; static nni_idhash *nni_sock_hash; static nni_mtx nni_sock_lk; +typedef struct nni_sockopt { + nni_list_node node; + int opt; + size_t sz; + void * data; +} nni_sockopt; + struct nni_socket { nni_list_node s_node; nni_mtx s_mx; @@ -44,7 +51,8 @@ struct nni_socket { nni_duration s_rcvtimeo; // receive timeout nni_duration s_reconn; // reconnect time nni_duration s_reconnmax; // max reconnect time - size_t s_rcvmaxsz; // maximum receive size + size_t s_rcvmaxsz; // max receive size + nni_list s_options; // opts not handled by sock/proto nni_list s_eps; // active endpoints nni_list s_pipes; // active pipes @@ -63,6 +71,13 @@ struct nni_socket { nni_notifyfd s_recv_fd; }; +static void +nni_free_opt(nni_sockopt *opt) +{ + nni_free(opt->data, opt->sz); + NNI_FREE_STRUCT(opt); +} + uint32_t nni_sock_id(nni_sock *s) { @@ -268,6 +283,8 @@ nni_sock_unnotify(nni_sock *sock, nni_notify *notify) static void nni_sock_destroy(nni_sock *s) { + nni_sockopt *sopt; + if (s == NULL) { return; } @@ -285,6 +302,11 @@ nni_sock_destroy(nni_sock *s) s->s_sock_ops.sock_fini(s->s_data); } + while ((sopt = nni_list_first(&s->s_options)) != NULL) { + nni_list_remove(&s->s_options, sopt); + nni_free_opt(sopt); + } + nni_ev_fini(&s->s_send_ev); nni_ev_fini(&s->s_recv_ev); nni_msgq_fini(s->s_urq); @@ -305,8 +327,8 @@ nni_sock_create(nni_sock **sp, const nni_proto *proto) return (NNG_ENOMEM); } s->s_linger = 0; - s->s_sndtimeo = -1; - s->s_rcvtimeo = -1; + s->s_sndtimeo = NNI_TIME_NEVER; + s->s_rcvtimeo = NNI_TIME_NEVER; s->s_closing = 0; s->s_reconn = NNI_SECOND; s->s_reconnmax = 0; @@ -328,6 +350,7 @@ nni_sock_create(nni_sock **sp, const nni_proto *proto) NNI_ASSERT(s->s_pipe_ops.pipe_stop != NULL); NNI_LIST_NODE_INIT(&s->s_node); + NNI_LIST_INIT(&s->s_options, nni_sockopt, node); nni_pipe_sock_list_init(&s->s_pipes); nni_ep_list_init(&s->s_eps); nni_mtx_init(&s->s_mx); @@ -338,10 +361,23 @@ nni_sock_create(nni_sock **sp, const nni_proto *proto) if (((rv = nni_msgq_init(&s->s_uwq, 0)) != 0) || ((rv = nni_msgq_init(&s->s_urq, 0)) != 0) || - ((rv = s->s_sock_ops.sock_init(&s->s_data, s)) != 0)) { + ((rv = s->s_sock_ops.sock_init(&s->s_data, s)) != 0) || + ((rv = nni_sock_setopt(s, NNG_OPT_LINGER, &s->s_linger, + sizeof(nni_duration))) != 0) || + ((rv = nni_sock_setopt(s, NNG_OPT_SNDTIMEO, &s->s_sndtimeo, + sizeof(nni_duration))) != 0) || + ((rv = nni_sock_setopt(s, NNG_OPT_RCVTIMEO, &s->s_rcvtimeo, + sizeof(nni_duration))) != 0) || + ((rv = nni_sock_setopt(s, NNG_OPT_RECONN_TIME, &s->s_reconn, + sizeof(nni_duration))) != 0) || + ((rv = nni_sock_setopt(s, NNG_OPT_RECONN_MAXTIME, &s->s_reconnmax, + sizeof(nni_duration))) != 0) || + ((rv = nni_sock_setopt(s, NNG_OPT_RCVMAXSZ, &s->s_rcvmaxsz, + sizeof(size_t))) != 0)) { nni_sock_destroy(s); return (rv); } + *sp = s; return (rv); } @@ -383,7 +419,6 @@ nni_sock_open(nni_sock **sockp, const nni_proto *proto) if (((rv = nni_init()) != 0) || ((rv = nni_sock_create(&s, proto)) != 0)) { - nni_sock_destroy(s); return (rv); } @@ -690,12 +725,6 @@ nni_sock_peer(nni_sock *sock) return (sock->s_peer_id.p_id); } -size_t -nni_sock_rcvmaxsz(nni_sock *sock) -{ - return (sock->s_rcvmaxsz); -} - void nni_sock_reconntimes(nni_sock *sock, nni_duration *rcur, nni_duration *rmax) { @@ -707,15 +736,25 @@ nni_sock_reconntimes(nni_sock *sock, nni_duration *rcur, nni_duration *rmax) } int -nni_sock_ep_add(nni_sock *sock, nni_ep *ep) +nni_sock_ep_add(nni_sock *s, nni_ep *ep) { - nni_mtx_lock(&sock->s_mx); - if (sock->s_closing) { - nni_mtx_unlock(&sock->s_mx); + nni_sockopt *sopt; + + nni_mtx_lock(&s->s_mx); + if (s->s_closing) { + nni_mtx_unlock(&s->s_mx); return (NNG_ECLOSED); } - nni_list_append(&sock->s_eps, ep); - nni_mtx_unlock(&sock->s_mx); + NNI_LIST_FOREACH (&s->s_options, sopt) { + int rv; + rv = nni_ep_setopt(ep, sopt->opt, sopt->data, sopt->sz, 0); + if ((rv != 0) && (rv != NNG_ENOTSUP)) { + nni_mtx_unlock(&s->s_mx); + return (rv); + } + } + nni_list_append(&s->s_eps, ep); + nni_mtx_unlock(&s->s_mx); return (0); } @@ -745,108 +784,222 @@ nni_sock_senderr(nni_sock *sock, int err) } int -nni_sock_setopt(nni_sock *sock, int opt, const void *val, size_t size) +nni_sock_setopt(nni_sock *s, int opt, const void *val, size_t size) { - int rv = NNG_ENOTSUP; + int rv = NNG_ENOTSUP; + nni_ep * ep; + int commits = 0; + nni_sockopt *optv; + nni_sockopt *oldv = NULL; - nni_mtx_lock(&sock->s_mx); - if (sock->s_closing) { - nni_mtx_unlock(&sock->s_mx); + nni_mtx_lock(&s->s_mx); + if (s->s_closing) { + nni_mtx_unlock(&s->s_mx); return (NNG_ECLOSED); } - if (sock->s_sock_ops.sock_setopt != NULL) { - rv = - sock->s_sock_ops.sock_setopt(sock->s_data, opt, val, size); + if (s->s_sock_ops.sock_setopt != NULL) { + rv = s->s_sock_ops.sock_setopt(s->s_data, opt, val, size); if (rv != NNG_ENOTSUP) { - nni_mtx_unlock(&sock->s_mx); + nni_mtx_unlock(&s->s_mx); return (rv); } } + + // Some options do not go down to transports. Handle them + // directly. switch (opt) { - case NNG_OPT_LINGER: - rv = nni_setopt_usec(&sock->s_linger, val, size); - break; - case NNG_OPT_SNDTIMEO: - rv = nni_setopt_usec(&sock->s_sndtimeo, val, size); - break; - case NNG_OPT_RCVTIMEO: - rv = nni_setopt_usec(&sock->s_rcvtimeo, val, size); - break; case NNG_OPT_RECONN_TIME: - rv = nni_setopt_usec(&sock->s_reconn, val, size); + rv = nni_setopt_usec(&s->s_reconn, val, size); break; case NNG_OPT_RECONN_MAXTIME: - rv = nni_setopt_usec(&sock->s_reconnmax, val, size); + rv = nni_setopt_usec(&s->s_reconnmax, val, size); break; case NNG_OPT_SNDBUF: - rv = nni_setopt_buf(sock->s_uwq, val, size); + rv = nni_setopt_buf(s->s_uwq, val, size); break; case NNG_OPT_RCVBUF: - rv = nni_setopt_buf(sock->s_urq, val, size); + rv = nni_setopt_buf(s->s_urq, val, size); break; - case NNG_OPT_RCVMAXSZ: - rv = nni_setopt_size( - &sock->s_rcvmaxsz, val, size, 0, NNI_MAXSZ); + case NNG_OPT_SNDFD: + case NNG_OPT_RCVFD: + case NNG_OPT_LOCALADDR: + case NNG_OPT_REMOTEADDR: + // these options can be read, but cannot be set + rv = NNG_EINVAL; break; } - nni_mtx_unlock(&sock->s_mx); + + nni_mtx_unlock(&s->s_mx); + + // If the option was already handled one way or the other, + if (rv != NNG_ENOTSUP) { + return (rv); + } + + // Validation of transport options. This is stateless, so + // transports should not fail to set an option later if they + // passed it here. + rv = nni_tran_chkopt(opt, val, size); + + // Also check a few generic things. We do this if no transport + // check was found, or even if a transport rejected one of the + // settings. + if ((rv == NNG_ENOTSUP) || (rv == 0)) { + switch (opt) { + case NNG_OPT_LINGER: + rv = nni_chkopt_usec(val, size); + break; + case NNG_OPT_SNDTIMEO: + rv = nni_chkopt_usec(val, size); + break; + case NNG_OPT_RCVTIMEO: + rv = nni_chkopt_usec(val, size); + break; + case NNG_OPT_RCVMAXSZ: + // just a sanity test on the size; it also ensures that + // a size can be set even with no transport configured. + rv = nni_chkopt_size(val, size, 0, NNI_MAXSZ); + break; + } + } + + if (rv != 0) { + return (rv); + } + + // Prepare a copy of the sockoption. + if ((optv = NNI_ALLOC_STRUCT(optv)) == NULL) { + return (NNG_ENOMEM); + } + if ((optv->data = nni_alloc(size)) == NULL) { + NNI_FREE_STRUCT(optv); + return (NNG_ENOMEM); + } + memcpy(optv->data, val, size); + optv->opt = opt; + optv->sz = size; + NNI_LIST_NODE_INIT(&optv->node); + + nni_mtx_lock(&s->s_mx); + NNI_LIST_FOREACH (&s->s_options, oldv) { + if (oldv->opt == opt) { + if ((oldv->sz != size) || + (memcmp(oldv->data, val, size) != 0)) { + break; + } + + // The values are the same. This is a no-op. + nni_mtx_unlock(&s->s_mx); + nni_free_opt(optv); + return (0); + } + } + + // Apply the options. Failure to set any option on any transport + // (other than ENOTSUP) stops the operation altogether. Its + // important that transport wide checks properly pre-validate. + NNI_LIST_FOREACH (&s->s_eps, ep) { + int x; + x = nni_ep_setopt(ep, opt, optv->data, size, 0); + if (x != NNG_ENOTSUP) { + if ((rv = x) != 0) { + nni_mtx_unlock(&s->s_mx); + nni_free_opt(optv); + return (rv); + } + } + } + + // For some options, which also have an impact on the socket + // behavior, we save a local value. Note that the transport + // will already have had a chance to veto this. + switch (opt) { + case NNG_OPT_LINGER: + rv = nni_setopt_usec(&s->s_linger, val, size); + break; + case NNG_OPT_SNDTIMEO: + rv = nni_setopt_usec(&s->s_sndtimeo, val, size); + break; + case NNG_OPT_RCVTIMEO: + rv = nni_setopt_usec(&s->s_rcvtimeo, val, size); + break; + } + + if (rv == 0) { + // Remove and toss the old value, we are using a new one. + if (oldv != NULL) { + nni_list_remove(&s->s_options, oldv); + nni_free_opt(oldv); + } + + // Insert our new value. This permits it to be compared + // against later, and for new endpoints to automatically + // receive these values, + nni_list_append(&s->s_options, optv); + } else { + nni_free_opt(optv); + } + + nni_mtx_unlock(&s->s_mx); return (rv); } int -nni_sock_getopt(nni_sock *sock, int opt, void *val, size_t *sizep) +nni_sock_getopt(nni_sock *s, int opt, void *val, size_t *szp) { - int rv = NNG_ENOTSUP; + int rv = NNG_ENOTSUP; + nni_sockopt *sopt; - nni_mtx_lock(&sock->s_mx); - if (sock->s_closing) { - nni_mtx_unlock(&sock->s_mx); + nni_mtx_lock(&s->s_mx); + if (s->s_closing) { + nni_mtx_unlock(&s->s_mx); return (NNG_ECLOSED); } - if (sock->s_sock_ops.sock_getopt != NULL) { - rv = sock->s_sock_ops.sock_getopt( - sock->s_data, opt, val, sizep); + if (s->s_sock_ops.sock_getopt != NULL) { + rv = s->s_sock_ops.sock_getopt(s->s_data, opt, val, szp); if (rv != NNG_ENOTSUP) { - nni_mtx_unlock(&sock->s_mx); + nni_mtx_unlock(&s->s_mx); return (rv); } } + // Options that are handled by socket core, and never + // passed down. switch (opt) { - case NNG_OPT_LINGER: - rv = nni_getopt_usec(&sock->s_linger, val, sizep); - break; - case NNG_OPT_SNDTIMEO: - rv = nni_getopt_usec(&sock->s_sndtimeo, val, sizep); - break; - case NNG_OPT_RCVTIMEO: - rv = nni_getopt_usec(&sock->s_rcvtimeo, val, sizep); - break; case NNG_OPT_RECONN_TIME: - rv = nni_getopt_usec(&sock->s_reconn, val, sizep); + rv = nni_getopt_usec(&s->s_reconn, val, szp); break; case NNG_OPT_RECONN_MAXTIME: - rv = nni_getopt_usec(&sock->s_reconnmax, val, sizep); + rv = nni_getopt_usec(&s->s_reconnmax, val, szp); break; case NNG_OPT_SNDBUF: - rv = nni_getopt_buf(sock->s_uwq, val, sizep); + rv = nni_getopt_buf(s->s_uwq, val, szp); break; case NNG_OPT_RCVBUF: - rv = nni_getopt_buf(sock->s_urq, val, sizep); - break; - case NNG_OPT_RCVMAXSZ: - rv = nni_getopt_size(&sock->s_rcvmaxsz, val, sizep); + rv = nni_getopt_buf(s->s_urq, val, szp); break; case NNG_OPT_SNDFD: - rv = nni_getopt_fd( - sock, &sock->s_send_fd, NNG_EV_CAN_SND, val, sizep); + rv = nni_getopt_fd(s, &s->s_send_fd, NNG_EV_CAN_SND, val, szp); break; case NNG_OPT_RCVFD: - rv = nni_getopt_fd( - sock, &sock->s_recv_fd, NNG_EV_CAN_RCV, val, sizep); + rv = nni_getopt_fd(s, &s->s_recv_fd, NNG_EV_CAN_RCV, val, szp); + break; + default: + NNI_LIST_FOREACH (&s->s_options, sopt) { + if (sopt->opt == opt) { + size_t sz = sopt->sz; + if (sopt->sz > *szp) { + sz = *szp; + } + *szp = sopt->sz; + memcpy(val, sopt->data, sz); + rv = 0; + break; + } + } break; } - nni_mtx_unlock(&sock->s_mx); + nni_mtx_unlock(&s->s_mx); return (rv); } diff --git a/src/core/socket.h b/src/core/socket.h index 9fa6c0fa..931fefac 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -64,8 +64,7 @@ extern nni_msgq *nni_sock_sendq(nni_sock *); // inject incoming messages from pipes to it. extern nni_msgq *nni_sock_recvq(nni_sock *); -extern size_t nni_sock_rcvmaxsz(nni_sock *); -extern void nni_sock_reconntimes(nni_sock *, nni_duration *, nni_duration *); +extern void nni_sock_reconntimes(nni_sock *, nni_duration *, nni_duration *); // nni_sock_flags returns the socket flags, used to indicate whether read // and or write are appropriate for the protocol. diff --git a/src/core/transport.c b/src/core/transport.c index 2f373f9a..3130ae26 100644 --- a/src/core/transport.c +++ b/src/core/transport.c @@ -89,6 +89,27 @@ nni_tran_find(const char *addr) return (NULL); } +int +nni_tran_chkopt(int o, const void *v, size_t sz) +{ + nni_transport *t; + int rv = NNG_ENOTSUP; + nni_mtx_lock(&nni_tran_lk); + NNI_LIST_FOREACH (&nni_tran_list, t) { + int x; + if (t->t_tran.tran_chkopt == NULL) { + continue; + } + if ((x = t->t_tran.tran_chkopt(o, v, sz)) != NNG_ENOTSUP) { + if ((rv = x) != 0) { + break; + } + } + } + nni_mtx_unlock(&nni_tran_lk); + return (rv); +} + // nni_tran_sys_init initializes the entire transport subsystem, including // each individual transport. int diff --git a/src/core/transport.h b/src/core/transport.h index 78cb2bbf..2891d8a4 100644 --- a/src/core/transport.h +++ b/src/core/transport.h @@ -29,6 +29,11 @@ struct nni_tran { // tran_pipe links our pipe-specific operations. const nni_tran_pipe *tran_pipe; + // tran_chkopt, if not NULL, is used to validate that the + // option data presented is valid. This allows an option to + // be set on a socket, even if no endpoints are configured. + int (*tran_chkopt)(int, const void *, size_t); + // tran_init, if not NULL, is called once during library // initialization. int (*tran_init)(void); @@ -135,6 +140,7 @@ struct nni_tran_pipe { // These APIs are used by the framework internally, and not for use by // transport implementations. extern nni_tran *nni_tran_find(const char *); +extern int nni_tran_chkopt(int, const void *, size_t); extern int nni_tran_sys_init(void); extern void nni_tran_sys_fini(void); extern int nni_tran_register(const nni_tran *); @@ -279,6 +279,20 @@ nng_listener_create(nng_listener *lp, nng_socket sid, const char *addr) } int +nng_listener_start(nng_listener id, int flags) +{ + nni_ep *ep; + int rv; + + if ((rv = nni_ep_find(&ep, id)) != 0) { + return (rv); + } + rv = nni_ep_listen(ep, flags); + nni_ep_rele(ep); + return (rv); +} + +int nng_dialer_create(nng_dialer *dp, nng_socket sid, const char *addr) { nni_sock *s; @@ -298,6 +312,148 @@ nng_dialer_create(nng_dialer *dp, nng_socket sid, const char *addr) return (0); } +int +nng_dialer_start(nng_dialer id, int flags) +{ + nni_ep *ep; + int rv; + + if ((rv = nni_ep_find(&ep, id)) != 0) { + return (rv); + } + rv = nni_ep_dial(ep, flags); + nni_ep_rele(ep); + return (rv); +} + +static int +nng_ep_setopt(uint32_t id, int opt, const void *val, size_t sz) +{ + nni_ep *ep; + int rv; + if ((rv = nni_ep_find(&ep, id)) != 0) { + return (rv); + } + rv = nni_ep_setopt(ep, opt, val, sz, 1); + nni_ep_rele(ep); + return (rv); +} + +static int +nng_ep_getopt(uint32_t id, int opt, void *val, size_t *szp) +{ + nni_ep *ep; + int rv; + if ((rv = nni_ep_find(&ep, id)) != 0) { + return (rv); + } + rv = nni_ep_getopt(ep, opt, val, szp); + nni_ep_rele(ep); + return (rv); +} + +int +nng_dialer_setopt(nng_dialer id, int opt, const void *v, size_t sz) +{ + return (nng_ep_setopt(id, opt, v, sz)); +} + +int +nng_dialer_setopt_int(nng_dialer id, int opt, int val) +{ + return (nng_ep_setopt(id, opt, &val, sizeof(val))); +} + +int +nng_dialer_setopt_size(nng_dialer id, int opt, size_t val) +{ + return (nng_ep_setopt(id, opt, &val, sizeof(val))); +} + +int +nng_dialer_setopt_usec(nng_dialer id, int opt, uint64_t val) +{ + return (nng_ep_setopt(id, opt, &val, sizeof(val))); +} + +int +nng_dialer_getopt(nng_dialer id, int opt, void *val, size_t *szp) +{ + return (nng_ep_getopt(id, opt, val, szp)); +} + +int +nng_dialer_getopt_int(nng_dialer id, int opt, int *valp) +{ + size_t sz = sizeof(*valp); + return (nng_ep_getopt(id, opt, valp, &sz)); +} + +int +nng_dialer_getopt_size(nng_dialer id, int opt, size_t *valp) +{ + size_t sz = sizeof(*valp); + return (nng_ep_getopt(id, opt, valp, &sz)); +} + +int +nng_dialer_getopt_usec(nng_dialer id, int opt, uint64_t *valp) +{ + size_t sz = sizeof(*valp); + return (nng_ep_getopt(id, opt, valp, &sz)); +} + +int +nng_listener_setopt(nng_listener id, int opt, const void *v, size_t sz) +{ + return (nng_ep_setopt(id, opt, v, sz)); +} + +int +nng_listener_setopt_int(nng_listener id, int opt, int val) +{ + return (nng_ep_setopt(id, opt, &val, sizeof(val))); +} + +int +nng_listener_setopt_size(nng_listener id, int opt, size_t val) +{ + return (nng_ep_setopt(id, opt, &val, sizeof(val))); +} + +int +nng_listener_setopt_usec(nng_listener id, int opt, uint64_t val) +{ + return (nng_ep_setopt(id, opt, &val, sizeof(val))); +} + +int +nng_listener_getopt(nng_listener id, int opt, void *val, size_t *szp) +{ + return (nng_ep_getopt(id, opt, val, szp)); +} + +int +nng_listener_getopt_int(nng_listener id, int opt, int *valp) +{ + size_t sz = sizeof(*valp); + return (nng_ep_getopt(id, opt, valp, &sz)); +} + +int +nng_listener_getopt_size(nng_listener id, int opt, size_t *valp) +{ + size_t sz = sizeof(*valp); + return (nng_ep_getopt(id, opt, valp, &sz)); +} + +int +nng_listener_getopt_usec(nng_listener id, int opt, uint64_t *valp) +{ + size_t sz = sizeof(*valp); + return (nng_ep_getopt(id, opt, valp, &sz)); +} + static int nng_ep_close(uint32_t id) { @@ -422,7 +578,8 @@ nng_unsetnotify(nng_socket sid, nng_notify *notify) nng_socket nng_event_socket(nng_event *ev) { - // XXX: FOR NOW.... maybe evnet should contain socket Id instead? + // XXX: FOR NOW.... maybe evnet should contain socket Id + // instead? return (nni_sock_id(ev->e_sock)); } @@ -768,10 +925,10 @@ nng_stat_value(nng_stat *stat) } #endif -// These routines exist as utility functions, exposing some of our "guts" -// to the external world for the purposes of test code and bundled utilities. -// They should not be considered part of our public API, and applications -// should refrain from their use. +// These routines exist as utility functions, exposing some of our +// "guts" to the external world for the purposes of test code and +// bundled utilities. They should not be considered part of our public +// API, and applications should refrain from their use. void nng_usleep(uint64_t usec) @@ -199,17 +199,35 @@ NNG_DECL int nng_listener_close(nng_listener); // nng_dialer_setopt sets an option for a specific dialer. Note // dialer options may not be altered on a running dialer. -NNG_DECL int nng_dialer_setopt(nng_dialer, int, void *, size_t); - -// nng_dialer_getopt obtains the option for a dialer. +NNG_DECL int nng_dialer_setopt(nng_dialer, int, const void *, size_t); +NNG_DECL int nng_dialer_setopt_int(nng_dialer, int, int); +NNG_DECL int nng_dialer_setopt_usec(nng_dialer, int, uint64_t); +NNG_DECL int nng_dialer_setopt_size(nng_dialer, int, size_t); + +// nng_dialer_getopt obtains the option for a dialer. This will +// fail for options that a particular dialer is not interested in, +// even if they were set on the socket. NNG_DECL int nng_dialer_getopt(nng_dialer, int, void *, size_t *); - -// nng_listener_setopt sets an option for a specific listener. Note -// listener options may not be altered on a running listener. -NNG_DECL int nng_listener_setopt(nng_listener, int, void *, size_t); - -// nng_listener_getopt obtains the option for a listener. +NNG_DECL int nng_dialer_getopt_int(nng_dialer, int, int *); +NNG_DECL int nng_dialer_getopt_usec(nng_dialer, int, uint64_t *); +NNG_DECL int nng_dialer_getopt_size(nng_dialer, int, size_t *); + +// nng_listener_setopt sets an option for a dialer. This value is +// not stored in the socket. Subsequent setopts on the socket may +// override these value however. Note listener options may not be altered +// on a running listener. +NNG_DECL int nng_listener_setopt(nng_dialer, int, const void *, size_t); +NNG_DECL int nng_listener_setopt_int(nng_dialer, int, int); +NNG_DECL int nng_listener_setopt_usec(nng_dialer, int, uint64_t); +NNG_DECL int nng_listener_setopt_size(nng_dialer, int, size_t); + +// nng_listener_getopt obtains the option for a listener. This will +// fail for options that a particular listener is not interested in, +// even if they were set on the socket. NNG_DECL int nng_listener_getopt(nng_listener, int, void *, size_t *); +NNG_DECL int nng_listener_getopt_int(nng_listener, int, int *); +NNG_DECL int nng_listener_getopt_usec(nng_listener, int, uint64_t *); +NNG_DECL int nng_listener_getopt_size(nng_listener, int, size_t *); // nng_strerror returns a human readable string associated with the error // code supplied. @@ -302,6 +320,7 @@ NNG_DECL int nng_pipe_close(nng_pipe); enum nng_flag_enum { NNG_FLAG_ALLOC = 1, // Recv to allocate receive buffer. NNG_FLAG_NONBLOCK = 2, // Non-blocking operations. + NNG_FLAG_DRYRUN = 4, // Setopt dry-run (internally used). }; // Protocol numbers. These are to be used with nng_socket_create(). diff --git a/src/transport/ipc/ipc.c b/src/transport/ipc/ipc.c index d421d57a..11bfceb9 100644 --- a/src/transport/ipc/ipc.c +++ b/src/transport/ipc/ipc.c @@ -63,6 +63,16 @@ static void nni_ipc_pipe_nego_cb(void *); static void nni_ipc_ep_cb(void *); static int +nni_ipc_tran_chkopt(int o, const void *data, size_t sz) +{ + switch (o) { + case NNG_OPT_RCVMAXSZ: + return (nni_chkopt_size(data, sz, 0, NNI_MAXSZ)); + } + return (NNG_ENOTSUP); +} + +static int nni_ipc_tran_init(void) { return (0); @@ -496,7 +506,6 @@ nni_ipc_ep_init(void **epp, const char *url, nni_sock *sock, int mode) ep->closed = 0; ep->proto = nni_sock_proto(sock); - ep->rcvmax = nni_sock_rcvmaxsz(sock); (void) snprintf(ep->addr, sizeof(ep->addr), "%s", url); *epp = ep; @@ -649,6 +658,43 @@ nni_ipc_ep_connect(void *arg, nni_aio *aio) nni_mtx_unlock(&ep->mtx); } +static int +nni_ipc_ep_setopt(void *arg, int opt, const void *v, size_t sz) +{ + int rv; + nni_ipc_ep *ep = arg; + nni_mtx_lock(&ep->mtx); + switch (opt) { + case NNG_OPT_RCVMAXSZ: + rv = nni_setopt_size(&ep->rcvmax, v, sz, 0, NNI_MAXSZ); + break; + default: + rv = NNG_ENOTSUP; + break; + } + nni_mtx_unlock(&ep->mtx); + return (rv); +} + +static int +nni_ipc_ep_getopt(void *arg, int opt, void *v, size_t *szp) +{ + int rv; + nni_ipc_ep *ep = arg; + + nni_mtx_lock(&ep->mtx); + switch (opt) { + case NNG_OPT_RCVMAXSZ: + rv = nni_getopt_size(&ep->rcvmax, v, szp); + break; + default: + rv = NNG_ENOTSUP; + break; + } + nni_mtx_unlock(&ep->mtx); + return (rv); +} + static nni_tran_pipe nni_ipc_pipe_ops = { .p_fini = nni_ipc_pipe_fini, .p_start = nni_ipc_pipe_start, @@ -666,8 +712,8 @@ static nni_tran_ep nni_ipc_ep_ops = { .ep_bind = nni_ipc_ep_bind, .ep_accept = nni_ipc_ep_accept, .ep_close = nni_ipc_ep_close, - .ep_setopt = NULL, - .ep_getopt = NULL, + .ep_setopt = nni_ipc_ep_setopt, + .ep_getopt = nni_ipc_ep_getopt, }; // This is the IPC transport linkage, and should be the only global diff --git a/src/transport/tcp/tcp.c b/src/transport/tcp/tcp.c index 035ced62..99e59302 100644 --- a/src/transport/tcp/tcp.c +++ b/src/transport/tcp/tcp.c @@ -51,6 +51,7 @@ struct nni_tcp_ep { int closed; uint16_t proto; size_t rcvmax; + nni_duration linger; int ipv4only; nni_aio aio; nni_aio * user_aio; @@ -63,6 +64,18 @@ static void nni_tcp_pipe_nego_cb(void *); static void nni_tcp_ep_cb(void *arg); static int +nni_tcp_tran_chkopt(int o, const void *data, size_t sz) +{ + switch (o) { + case NNG_OPT_RCVMAXSZ: + return (nni_chkopt_size(data, sz, 0, NNI_MAXSZ)); + case NNG_OPT_LINGER: + return (nni_chkopt_usec(data, sz)); + } + return (NNG_ENOTSUP); +} + +static int nni_tcp_tran_init(void) { return (0); @@ -561,7 +574,6 @@ nni_tcp_ep_init(void **epp, const char *url, nni_sock *sock, int mode) ep->closed = 0; ep->proto = nni_sock_proto(sock); - ep->rcvmax = nni_sock_rcvmaxsz(sock); (void) snprintf(ep->addr, sizeof(ep->addr), "%s", url); *epp = ep; @@ -713,6 +725,51 @@ nni_tcp_ep_connect(void *arg, nni_aio *aio) nni_mtx_unlock(&ep->mtx); } +static int +nni_tcp_ep_setopt(void *arg, int opt, const void *v, size_t sz) +{ + int rv; + nni_tcp_ep *ep = arg; + + nni_mtx_lock(&ep->mtx); + switch (opt) { + case NNG_OPT_RCVMAXSZ: + rv = nni_setopt_size(&ep->rcvmax, v, sz, 0, NNI_MAXSZ); + break; + case NNG_OPT_LINGER: + rv = nni_setopt_usec(&ep->linger, v, sz); + break; + default: + rv = NNG_ENOTSUP; + break; + } + nni_mtx_unlock(&ep->mtx); + return (rv); +} + +static int +nni_tcp_ep_getopt(void *arg, int opt, void *v, size_t *szp) +{ + int rv; + nni_tcp_ep *ep = arg; + + nni_mtx_lock(&ep->mtx); + switch (opt) { + case NNG_OPT_RCVMAXSZ: + rv = nni_getopt_size(&ep->rcvmax, v, szp); + break; + case NNG_OPT_LINGER: + rv = nni_getopt_usec(&ep->linger, v, szp); + break; + default: + // XXX: add address properties + rv = NNG_ENOTSUP; + break; + } + nni_mtx_unlock(&ep->mtx); + return (rv); +} + static nni_tran_pipe nni_tcp_pipe_ops = { .p_fini = nni_tcp_pipe_fini, .p_start = nni_tcp_pipe_start, @@ -730,8 +787,8 @@ static nni_tran_ep nni_tcp_ep_ops = { .ep_bind = nni_tcp_ep_bind, .ep_accept = nni_tcp_ep_accept, .ep_close = nni_tcp_ep_close, - .ep_setopt = NULL, - .ep_getopt = NULL, + .ep_setopt = nni_tcp_ep_setopt, + .ep_getopt = nni_tcp_ep_getopt, }; // This is the TCP transport linkage, and should be the only global @@ -741,6 +798,7 @@ struct nni_tran nni_tcp_tran = { .tran_scheme = "tcp", .tran_ep = &nni_tcp_ep_ops, .tran_pipe = &nni_tcp_pipe_ops, + .tran_chkopt = nni_tcp_tran_chkopt, .tran_init = nni_tcp_tran_init, .tran_fini = nni_tcp_tran_fini, }; diff --git a/tests/sock.c b/tests/sock.c index d33b0c81..2e2345c9 100644 --- a/tests/sock.c +++ b/tests/sock.c @@ -9,12 +9,15 @@ #include "convey.h" #include "nng.h" +#include "trantest.h" #include <string.h> TestMain("Socket Operations", { - Reset({ nng_fini(); }); + atexit(nng_fini); + // Reset({ nng_fini(); }); + Reset({ nng_closeall(); }); Convey("We are able to open a PAIR socket", { int rv; @@ -98,12 +101,63 @@ TestMain("Socket Operations", { So(nng_setopt_usec(s1, NNG_OPT_SNDTIMEO, to) == 0); + Convey("Read only options handled properly", { + So(nng_setopt_int(s1, NNG_OPT_RCVFD, 0) == + NNG_EINVAL); + So(nng_setopt_int(s1, NNG_OPT_SNDFD, 0) == + NNG_EINVAL); + So(nng_setopt(s1, NNG_OPT_LOCALADDR, "a", 1) == + NNG_EINVAL); + So(nng_setopt(s1, NNG_OPT_REMOTEADDR, "a", + 1) == NNG_EINVAL); + }); + + Convey("We can apply options before endpoint", { + nng_listener l; + char addr[NNG_MAXADDRLEN]; + trantest_next_address( + addr, "ipc:///tmp/lopt_%u"); + + So(nng_setopt_size( + s1, NNG_OPT_RCVMAXSZ, 543) == 0); + So(nng_listener_create(&l, s1, addr) == 0); + So(nng_listener_getopt_size( + l, NNG_OPT_RCVMAXSZ, &sz) == 0); + So(sz == 543); + + Convey("Endpoint option can be overridden", { + So(nng_listener_setopt_size( + l, NNG_OPT_RCVMAXSZ, 678) == 0); + So(nng_listener_getopt_size( + l, NNG_OPT_RCVMAXSZ, &sz) == 0); + So(sz == 678); + So(nng_getopt_size(s1, + NNG_OPT_RCVMAXSZ, &sz) == 0); + So(sz == 543); + }); + + Convey("And socket overrides again", { + So(nng_setopt_size(s1, + NNG_OPT_RCVMAXSZ, 911) == 0); + So(nng_listener_getopt_size( + l, NNG_OPT_RCVMAXSZ, &sz) == 0); + So(sz == 911); + }); + }); Convey("Short size is not copied", { sz = 0; So(nng_getopt(s1, NNG_OPT_SNDTIMEO, &v, &sz) == 0); So(sz == sizeof(v)); So(v == 0); + sz = 0; + So(nng_getopt( + s1, NNG_OPT_RECONN_TIME, &v, &sz) == 0); + + So(v == 0); + So(nng_getopt(s1, NNG_OPT_RECONN_MAXTIME, &v, + &sz) == 0); + So(v == 0); }); Convey("Correct size is copied", { @@ -126,6 +180,9 @@ TestMain("Socket Operations", { Convey("Insane buffer size fails", { So(nng_setopt_int(s1, NNG_OPT_RCVBUF, 0x100000) == NNG_EINVAL); + So(nng_setopt_int(s1, NNG_OPT_RCVBUF, -200) == + NNG_EINVAL); + }); Convey("Negative timeout fails", { @@ -138,6 +195,8 @@ TestMain("Socket Operations", { sz = sizeof(to) - 1; So(nng_setopt(s1, NNG_OPT_RCVTIMEO, &to, sz) == NNG_EINVAL); + So(nng_setopt(s1, NNG_OPT_RECONN_TIME, &to, + sz) == NNG_EINVAL); }); Convey("Bogus raw fails", { @@ -146,6 +205,8 @@ TestMain("Socket Operations", { So(nng_setopt_int(s1, NNG_OPT_RAW, -42) == NNG_EINVAL); So(nng_setopt_int(s1, NNG_OPT_RAW, 0) == 0); + So(nng_setopt(s1, NNG_OPT_RAW, "a", 1) == + NNG_EINVAL); }); Convey("Unsupported options fail", { @@ -156,6 +217,7 @@ TestMain("Socket Operations", { Convey("Bogus sizes fail", { size_t v; + int i; So(nng_setopt_size( s1, NNG_OPT_RCVMAXSZ, 6550) == 0); @@ -170,6 +232,10 @@ TestMain("Socket Operations", { 0); So(v == 6550); + i = 42; + So(nng_setopt(s1, NNG_OPT_RCVBUF, &i, 1) == + NNG_EINVAL); + if (sizeof(size_t) == 8) { v = 0x10000; v <<= 30; @@ -220,15 +286,20 @@ TestMain("Socket Operations", { }); Convey("Listening works", { - char *a = "inproc://here"; - rv = nng_listen(s1, a, NULL, 0); + char * a = "inproc://here"; + nng_listener l; + rv = nng_listen(s1, a, &l, 0); So(rv == 0); + So(l != 0); Convey("Second listen fails ADDRINUSE", { rv = nng_listen(s1, a, NULL, 0); So(rv == NNG_EADDRINUSE); }); + Convey("We cannot try to start a listener again", + { So(nng_listener_start(l, 0) == NNG_ESTATE); }); + Convey("We can connect to it", { nng_socket s2; So(nng_pair_open(&s2) == 0); @@ -238,6 +309,121 @@ TestMain("Socket Operations", { }); }); + Convey("Dialer creation ok", { + nng_dialer ep; + char * a = "tcp://127.0.0.1:2929"; + So(nng_dialer_create(&ep, s1, a) == 0); + Convey("Options work", { + size_t sz; + So(nng_dialer_setopt_size( + ep, NNG_OPT_RCVMAXSZ, 4321) == 0); + So(nng_dialer_getopt_size( + ep, NNG_OPT_RCVMAXSZ, &sz) == 0); + So(sz == 4321); + }); + Convey("Socket opts not for dialer", { + // Not appropriate for dialer. + So(nng_dialer_setopt_int(ep, NNG_OPT_RAW, 1) == + NNG_ENOTSUP); + So(nng_dialer_setopt_usec(ep, + NNG_OPT_RECONN_TIME, 1) == NNG_ENOTSUP); + }); + Convey("Bad size checks", { + So(nng_dialer_setopt(ep, NNG_OPT_RCVMAXSZ, "a", + 1) == NNG_EINVAL); + }); + Convey("Cannot listen", + { So(nng_listener_start(ep, 0) == NNG_ENOTSUP); }); + + }); + + Convey("Listener creation ok", { + nng_listener ep; + char * a = "tcp://127.0.0.1:2929"; + So(nng_listener_create(&ep, s1, a) == 0); + Convey("Options work", { + size_t sz; + So(nng_listener_setopt_size( + ep, NNG_OPT_RCVMAXSZ, 4321) == 0); + So(nng_listener_getopt_size( + ep, NNG_OPT_RCVMAXSZ, &sz) == 0); + So(sz == 4321); + }); + Convey("Socket opts not for dialer", { + // Not appropriate for dialer. + So(nng_listener_setopt_int( + ep, NNG_OPT_RAW, 1) == NNG_ENOTSUP); + So(nng_listener_setopt_usec(ep, + NNG_OPT_RECONN_TIME, 1) == NNG_ENOTSUP); + }); + Convey("Bad size checks", { + So(nng_listener_setopt(ep, NNG_OPT_RCVMAXSZ, + "a", 1) == NNG_EINVAL); + }); + Convey("Cannot dial", + { So(nng_dialer_start(ep, 0) == NNG_ENOTSUP); }); + }); + + Convey("Cannot access absent ep options", { + size_t s; + int i; + uint64_t t; + + So(nng_dialer_setopt_size( + 1999, NNG_OPT_RCVMAXSZ, 10) == NNG_ENOENT); + So(nng_listener_setopt_size( + 1999, NNG_OPT_RCVMAXSZ, 10) == NNG_ENOENT); + + s = 1; + So(nng_dialer_getopt(1999, NNG_OPT_RAW, &i, &s) == + NNG_ENOENT); + So(nng_listener_getopt(1999, NNG_OPT_RAW, &i, &s) == + NNG_ENOENT); + + So(nng_dialer_getopt_size( + 1999, NNG_OPT_RCVMAXSZ, &s) == NNG_ENOENT); + So(nng_listener_getopt_size( + 1999, NNG_OPT_RCVMAXSZ, &s) == NNG_ENOENT); + + So(nng_dialer_getopt_int(1999, NNG_OPT_RAW, &i) == + NNG_ENOENT); + So(nng_listener_getopt_int(1999, NNG_OPT_RAW, &i) == + NNG_ENOENT); + + So(nng_dialer_getopt_usec(1999, NNG_OPT_LINGER, &t) == + NNG_ENOENT); + So(nng_listener_getopt_usec( + 1999, NNG_OPT_LINGER, &t) == NNG_ENOENT); + + }); + + Convey("Cannot set dialer opts when running", { + nng_dialer ep; + char addr[NNG_MAXADDRLEN]; + + trantest_next_address(addr, "ipc:///tmp/sock_test_%u"); + So(nng_dialer_create(&ep, s1, addr) == 0); + So(nng_dialer_start(ep, NNG_FLAG_NONBLOCK) == 0); + So(nng_dialer_setopt_size(ep, NNG_OPT_RCVMAXSZ, 10) == + NNG_ESTATE); + So(nng_dialer_close(ep) == 0); + So(nng_dialer_close(ep) == NNG_ENOENT); + }); + + Convey("Cannot set listener opts when running", { + nng_listener ep; + char addr[NNG_MAXADDRLEN]; + + trantest_next_address(addr, "ipc:///tmp/sock_test_%u"); + + So(nng_listener_create(&ep, s1, addr) == 0); + So(nng_listener_start(ep, 0) == 0); + So(nng_listener_setopt_size( + ep, NNG_OPT_RCVMAXSZ, 10) == NNG_ESTATE); + So(nng_listener_close(ep) == 0); + So(nng_listener_close(ep) == NNG_ENOENT); + }); + Convey("We can send and receive messages", { nng_socket s2; int len; |
