diff options
| author | Garrett D'Amore <garrett@damore.org> | 2017-12-27 16:32:33 -0800 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2017-12-27 16:32:33 -0800 |
| commit | 5ff3d3d3ab82be4b2171e06f1def9cdddaef4115 (patch) | |
| tree | 028fff3d0dbcbec741406541931029c106bae465 /src/transport/ws | |
| parent | eb1f8db4ed87867f0f08afba79253e3981db9c88 (diff) | |
| download | nng-5ff3d3d3ab82be4b2171e06f1def9cdddaef4115.tar.gz nng-5ff3d3d3ab82be4b2171e06f1def9cdddaef4115.tar.bz2 nng-5ff3d3d3ab82be4b2171e06f1def9cdddaef4115.zip | |
fixes #180 add websocket header properties
Diffstat (limited to 'src/transport/ws')
| -rw-r--r-- | src/transport/ws/websocket.c | 206 | ||||
| -rw-r--r-- | src/transport/ws/websocket.h | 50 |
2 files changed, 210 insertions, 46 deletions
diff --git a/src/transport/ws/websocket.c b/src/transport/ws/websocket.c index 178338f9..c6ce9b25 100644 --- a/src/transport/ws/websocket.c +++ b/src/transport/ws/websocket.c @@ -14,11 +14,20 @@ #include <string.h> #include "core/nng_impl.h" +#include "supplemental/http/http.h" #include "supplemental/websocket/websocket.h" +#include "websocket.h" + typedef struct ws_ep ws_ep; typedef struct ws_pipe ws_pipe; +typedef struct ws_hdr { + nni_list_node node; + char * name; + char * value; +} ws_hdr; + struct ws_ep { int mode; // NNI_EP_MODE_DIAL or NNI_EP_MODE_LISTEN char * addr; @@ -32,6 +41,7 @@ struct ws_ep { nni_aio * accaio; nni_ws_listener *listener; nni_ws_dialer * dialer; + nni_list headers; // to send, res or req }; struct ws_pipe { @@ -242,9 +252,32 @@ ws_pipe_start(void *arg, nni_aio *aio) // Servers use the HTTP server framework, and a request methodology. static int +ws_hook(void *arg, nni_http_req *req, nni_http_res *res) +{ + ws_ep * ep = arg; + ws_hdr *h; + // Eventually we'll want user customizable hooks. + // For now we just set the headers we want. + + nni_mtx_lock(&ep->mtx); + NNI_LIST_FOREACH (&ep->headers, h) { + int rv; + rv = nni_http_req_set_header(req, h->name, h->value); + if (rv != 0) { + nni_mtx_unlock(&ep->mtx); + return (rv); + } + } + nni_mtx_unlock(&ep->mtx); + return (0); +} + +static int ws_ep_bind(void *arg) { ws_ep *ep = arg; + + nni_ws_listener_hook(ep->listener, ws_hook, ep); return (nni_ws_listener_listen(ep->listener)); } @@ -284,8 +317,9 @@ ws_ep_accept(void *arg, nni_aio *aio) static void ws_ep_connect(void *arg, nni_aio *aio) { - ws_ep *ep = arg; - int rv; + ws_ep * ep = arg; + int rv; + ws_hdr *h; nni_mtx_lock(&ep->mtx); NNI_ASSERT(nni_list_empty(&ep->aios)); @@ -297,6 +331,15 @@ ws_ep_connect(void *arg, nni_aio *aio) return; } + NNI_LIST_FOREACH (&ep->headers, h) { + rv = nni_ws_dialer_header(ep->dialer, h->name, h->value); + if (rv != 0) { + nni_aio_finish_error(aio, rv); + nni_mtx_unlock(&ep->mtx); + return; + } + } + nni_list_append(&ep->aios, aio); nni_ws_dialer_dial(ep->dialer, ep->connaio); nni_mtx_unlock(&ep->mtx); @@ -313,6 +356,117 @@ ws_ep_setopt_recvmaxsz(void *arg, const void *v, size_t sz) } static int +ws_ep_setopt_headers(ws_ep *ep, const void *v, size_t sz) +{ + // XXX: check that the string is well formed. + char * dupstr; + size_t duplen; + char * name; + char * value; + char * nl; + nni_list l; + ws_hdr * h; + int rv; + + if (nni_strnlen(v, sz) >= sz) { + return (NNG_EINVAL); + } + if (ep == NULL) { + return (0); + } + + NNI_LIST_INIT(&l, ws_hdr, node); + if ((dupstr = nni_strdup(v)) == NULL) { + return (NNG_ENOMEM); + } + duplen = strlen(dupstr) + 1; // so we can free it later + name = dupstr; + for (;;) { + if ((value = strchr(name, ':')) == NULL) { + // Note that this also means that if + // a bare word is present, we ignore it. + break; + } + *value = '\0'; + value++; + while (*value == ' ') { + // Skip leading whitespace. Not strictly + // necessary, but still a good idea. + value++; + } + nl = value; + // Find the end of the line -- should be CRLF, but can + // also be unterminated or just LF if user + while ((*nl != '\0') && (*nl != '\r') && (*nl != '\n')) { + nl++; + } + while ((*nl == '\r') || (*nl == '\n')) { + *nl = '\0'; + nl++; + } + + if ((h = NNI_ALLOC_STRUCT(h)) == NULL) { + rv = NNG_ENOMEM; + goto done; + } + nni_list_append(&l, h); + if (((h->name = nni_strdup(name)) == NULL) || + ((h->value = nni_strdup(value)) == NULL)) { + rv = NNG_ENOMEM; + goto done; + } + + name = nl; + } + + nni_mtx_lock(&ep->mtx); + while ((h = nni_list_first(&ep->headers)) != NULL) { + nni_list_remove(&ep->headers, h); + nni_strfree(h->name); + nni_strfree(h->value); + NNI_FREE_STRUCT(h); + } + while ((h = nni_list_first(&l)) != NULL) { + nni_list_remove(&l, h); + nni_list_append(&ep->headers, h); + } + nni_mtx_unlock(&ep->mtx); + rv = 0; + +done: + while ((h = nni_list_first(&l)) != NULL) { + nni_list_remove(&l, h); + nni_strfree(h->name); + nni_strfree(h->value); + NNI_FREE_STRUCT(h); + } + nni_free(dupstr, duplen); + return (rv); +} + +static int +ws_ep_setopt_reqhdrs(void *arg, const void *v, size_t sz) +{ + ws_ep *ep = arg; + + if (ep->mode == NNI_EP_MODE_LISTEN) { + return (NNG_EREADONLY); + } + return (ws_ep_setopt_headers(ep, v, sz)); +} + +static int +ws_ep_setopt_reshdrs(void *arg, const void *v, size_t sz) +{ + ws_ep *ep = arg; + + if (ep->mode == NNI_EP_MODE_DIAL) { + return (NNG_EREADONLY); + } + return (ws_ep_setopt_headers(ep, v, sz)); +} + +static int ws_ep_getopt_recvmaxsz(void *arg, void *v, size_t *szp) { ws_ep *ep = arg; @@ -347,11 +501,38 @@ ws_pipe_getopt_remaddr(void *arg, void *v, size_t *szp) return (rv); } +static int +ws_pipe_getopt_reshdrs(void *arg, void *v, size_t *szp) +{ + ws_pipe * p = arg; + const char *s; + + if ((s = nni_ws_response_headers(p->ws)) == NULL) { + return (NNG_ENOMEM); + } + return (nni_getopt_str(s, v, szp)); +} + +static int +ws_pipe_getopt_reqhdrs(void *arg, void *v, size_t *szp) +{ + ws_pipe * p = arg; + const char *s; + + if ((s = nni_ws_request_headers(p->ws)) == NULL) { + return (NNG_ENOMEM); + } + return (nni_getopt_str(s, v, szp)); +} + static nni_tran_pipe_option ws_pipe_options[] = { // clang-format off { NNG_OPT_LOCADDR, ws_pipe_getopt_locaddr }, { NNG_OPT_REMADDR, ws_pipe_getopt_remaddr }, + { NNG_OPT_WS_REQUEST_HEADERS, ws_pipe_getopt_reqhdrs }, + { NNG_OPT_WS_RESPONSE_HEADERS, ws_pipe_getopt_reshdrs }, + // clang-format on // terminate list @@ -374,6 +555,17 @@ static nni_tran_ep_option ws_ep_options[] = { .eo_getopt = ws_ep_getopt_recvmaxsz, .eo_setopt = ws_ep_setopt_recvmaxsz, }, + { + .eo_name = NNG_OPT_WS_REQUEST_HEADERS, + .eo_getopt = NULL, + .eo_setopt = ws_ep_setopt_reqhdrs, + }, + { + .eo_name = NNG_OPT_WS_RESPONSE_HEADERS, + .eo_getopt = NULL, + .eo_setopt = ws_ep_setopt_reshdrs, + }, + // terminate list { NULL, NULL, NULL }, }; @@ -381,7 +573,8 @@ static nni_tran_ep_option ws_ep_options[] = { static void ws_ep_fini(void *arg) { - ws_ep *ep = arg; + ws_ep * ep = arg; + ws_hdr *hdr; nni_aio_stop(ep->accaio); nni_aio_stop(ep->connaio); @@ -393,6 +586,12 @@ ws_ep_fini(void *arg) if (ep->dialer != NULL) { nni_ws_dialer_fini(ep->dialer); } + while ((hdr = nni_list_first(&ep->headers)) != NULL) { + nni_list_remove(&ep->headers, hdr); + nni_strfree(hdr->name); + nni_strfree(hdr->value); + NNI_FREE_STRUCT(hdr); + } nni_strfree(ep->addr); nni_strfree(ep->protoname); nni_mtx_fini(&ep->mtx); @@ -493,6 +692,7 @@ ws_ep_init(void **epp, const char *url, nni_sock *sock, int mode) } nni_mtx_init(&ep->mtx); + NNI_LIST_INIT(&ep->headers, ws_hdr, node); // List of pipes (server only). nni_aio_list_init(&ep->aios); diff --git a/src/transport/ws/websocket.h b/src/transport/ws/websocket.h index 1beb6156..2e86aaf0 100644 --- a/src/transport/ws/websocket.h +++ b/src/transport/ws/websocket.h @@ -11,52 +11,16 @@ #ifndef NNG_TRANSPORT_WS_WEBSOCKET_H #define NNG_TRANSPORT_WS_WEBSOCKET_H -// TLS transport. This is used for communication via TLS v1.2 over TCP/IP. +// WebSocket transport. This is used for communication via WebSocket. NNG_DECL int nng_ws_register(void); -// TLS options. Note that these can only be set *before* the endpoint is -// started. Once started, it is no longer possible to alter the TLS -// configuration. +// NNG_OPT_TLS_REQUEST_HEADERS is a string containing the +// request headers, formatted as CRLF terminated lines. +#define NNG_OPT_WS_REQUEST_HEADERS "ws:request-headers" -// NNG_OPT_TLS_CA_CERT is a string with one or more X.509 certificates, -// representing the entire CA chain. The content may be either PEM or DER -// encoded. -#define NNG_OPT_TLS_CA_CERT "tls:ca-cert" - -// NNG_OPT_TLS_CRL is a PEM encoded CRL (revocation list). Multiple lists -// may be loaded by using this option multiple times. -#define NNG_OPT_TLS_CRL "tls:crl" - -// NNG_OPT_TLS_CERT is used to specify our own certificate. At present -// only one certificate may be supplied. (In the future it may be -// possible to call this multiple times, for servers that select different -// certificates depending upon client capabilities.) -#define NNG_OPT_TLS_CERT "tls:cert" - -// NNG_OPT_TLS_PRIVATE_KEY is used to specify the private key used -// with the given certificate. This should be called after setting -// the certificate. The private key may be in PEM or DER format. -// If in PEM encoded, a terminating ZERO byte should be included. -#define NNG_OPT_TLS_PRIVATE_KEY "tls:private-key" - -// NNG_OPT_TLS_PRIVATE_KEY_PASSWORD is used to specify a password -// used for the private key. The value is an ASCIIZ string. -#define NNG_OPT_TLS_PRIVATE_KEY_PASSWORD "tls:private-key-password" - -// NNG_OPT_TLS_AUTH_MODE is an integer indicating whether our -// peer should be verified or not. It is required on clients/dialers, -// and off on servers/listeners, by default. -#define NNG_OPT_TLS_AUTH_MODE "tls:auth-mode" - -extern int nng_tls_auth_mode_required; -extern int nng_tls_auth_mode_none; -extern int nng_tls_auth_mode_optional; - -// NNG_OPT_TLS_AUTH_VERIFIED is a boolean that can be read on pipes, -// indicating whether the peer certificate is verified. -#define NNG_OPT_TLS_AUTH_VERIFIED "tls:auth-verified" - -// XXX: TBD: Ciphersuite selection and reporting. Session reuse? +// NNG_OPT_TLS_RESPONSE_HEADERS is a string containing the +// response headers, formatted as CRLF terminated lines. +#define NNG_OPT_WS_RESPONSE_HEADERS "ws:response-headers" #endif // NNG_TRANSPORT_WS_WEBSOCKET_H |
