aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/supplemental/http/http.h16
-rw-r--r--src/supplemental/http/http_msg.c26
-rw-r--r--src/supplemental/websocket/websocket.c105
-rw-r--r--src/supplemental/websocket/websocket.h2
-rw-r--r--src/transport/ws/websocket.c206
-rw-r--r--src/transport/ws/websocket.h50
-rw-r--r--tests/ws.c36
7 files changed, 384 insertions, 57 deletions
diff --git a/src/supplemental/http/http.h b/src/supplemental/http/http.h
index b28845ec..3ecce4c8 100644
--- a/src/supplemental/http/http.h
+++ b/src/supplemental/http/http.h
@@ -45,7 +45,8 @@ extern const char *nni_http_req_get_header(nni_http_req *, const char *);
extern const char *nni_http_req_get_version(nni_http_req *);
extern const char *nni_http_req_get_uri(nni_http_req *);
extern const char *nni_http_req_get_method(nni_http_req *);
-extern int nni_http_req_parse(nni_http_req *, void *, size_t, size_t *);
+extern int nni_http_req_parse(nni_http_req *, void *, size_t, size_t *);
+extern char *nni_http_req_headers(nni_http_req *);
extern int nni_http_res_init(nni_http_res **);
extern void nni_http_res_fini(nni_http_res *);
@@ -60,12 +61,13 @@ extern const char *nni_http_res_get_header(nni_http_res *, const char *);
extern const char *nni_http_res_get_version(nni_http_res *);
extern const char *nni_http_res_get_reason(nni_http_res *);
extern int nni_http_res_get_status(nni_http_res *);
-extern int nni_http_res_parse(nni_http_res *, void *, size_t, size_t *);
-extern int nni_http_res_set_data(nni_http_res *, const void *, size_t);
-extern int nni_http_res_copy_data(nni_http_res *, const void *, size_t);
-extern int nni_http_res_alloc_data(nni_http_res *, size_t);
-extern void nni_http_res_get_data(nni_http_res *, void **, size_t *);
-extern int nni_http_res_init_error(nni_http_res **, uint16_t);
+extern int nni_http_res_parse(nni_http_res *, void *, size_t, size_t *);
+extern int nni_http_res_set_data(nni_http_res *, const void *, size_t);
+extern int nni_http_res_copy_data(nni_http_res *, const void *, size_t);
+extern int nni_http_res_alloc_data(nni_http_res *, size_t);
+extern void nni_http_res_get_data(nni_http_res *, void **, size_t *);
+extern int nni_http_res_init_error(nni_http_res **, uint16_t);
+extern char *nni_http_res_headers(nni_http_res *);
// HTTP status codes. This list is not exhaustive.
enum { NNI_HTTP_STATUS_CONTINUE = 100,
diff --git a/src/supplemental/http/http_msg.c b/src/supplemental/http/http_msg.c
index 2e245868..8b3c6a7c 100644
--- a/src/supplemental/http/http_msg.c
+++ b/src/supplemental/http/http_msg.c
@@ -520,6 +520,32 @@ http_res_prepare(nni_http_res *res)
return (rv);
}
+char *
+nni_http_req_headers(nni_http_req *req)
+{
+ char * s;
+ size_t len;
+
+ len = http_sprintf_headers(NULL, 0, &req->hdrs) + 1;
+ if ((s = nni_alloc(len)) != NULL) {
+ http_sprintf_headers(s, len, &req->hdrs);
+ }
+ return (s);
+}
+
+char *
+nni_http_res_headers(nni_http_res *res)
+{
+ char * s;
+ size_t len;
+
+ len = http_sprintf_headers(NULL, 0, &res->hdrs) + 1;
+ if ((s = nni_alloc(len)) != NULL) {
+ http_sprintf_headers(s, len, &res->hdrs);
+ }
+ return (s);
+}
+
int
nni_http_req_get_buf(nni_http_req *req, void **data, size_t *szp)
{
diff --git a/src/supplemental/websocket/websocket.c b/src/supplemental/websocket/websocket.c
index dfcac3e7..53e4af8c 100644
--- a/src/supplemental/websocket/websocket.c
+++ b/src/supplemental/websocket/websocket.c
@@ -23,10 +23,16 @@
typedef struct ws_frame ws_frame;
typedef struct ws_msg ws_msg;
+typedef struct ws_header {
+ nni_list_node node;
+ char * name;
+ char * value;
+} ws_header;
+
struct nni_ws {
- int mode; // NNI_EP_MODE_DIAL or NNI_EP_MODE_LISTEN
nni_list_node node;
nni_reap_item reap;
+ int mode; // NNI_EP_MODE_DIAL or NNI_EP_MODE_LISTEN
bool closed;
bool ready;
bool wclose;
@@ -42,6 +48,8 @@ struct nni_ws {
nni_http * http;
nni_http_req *req;
nni_http_res *res;
+ char * reqhdrs;
+ char * reshdrs;
size_t maxframe;
size_t fragsize;
};
@@ -64,6 +72,7 @@ struct nni_ws_listener {
nni_http_handler handler;
nni_ws_listen_hook hookfn;
void * hookarg;
+ nni_list headers; // response headers
};
// The dialer tracks user aios in two lists. The first list is for aios
@@ -91,6 +100,7 @@ struct nni_ws_dialer {
bool started;
bool closed;
nng_sockaddr sa;
+ nni_list headers; // request headers
};
typedef enum ws_type {
@@ -724,7 +734,6 @@ ws_start_read(nni_ws *ws)
nni_aio_finish_error(wm->aio, NNG_ENOMEM);
}
ws_msg_fini(wm);
- // XXX: NOW WHAT?
return;
}
@@ -1028,6 +1037,28 @@ nni_ws_request(nni_ws *ws)
return (ws->req);
}
+const char *
+nni_ws_request_headers(nni_ws *ws)
+{
+ nni_mtx_lock(&ws->mtx);
+ if (ws->reqhdrs == NULL) {
+ ws->reqhdrs = nni_http_req_headers(ws->req);
+ }
+ nni_mtx_unlock(&ws->mtx);
+ return (ws->reqhdrs);
+}
+
+const char *
+nni_ws_response_headers(nni_ws *ws)
+{
+ nni_mtx_lock(&ws->mtx);
+ if (ws->reshdrs == NULL) {
+ ws->reshdrs = nni_http_res_headers(ws->res);
+ }
+ nni_mtx_unlock(&ws->mtx);
+ return (ws->reshdrs);
+}
+
static void
ws_fini(void *arg)
{
@@ -1075,6 +1106,8 @@ ws_fini(void *arg)
nni_http_res_fini(ws->res);
}
+ nni_strfree(ws->reqhdrs);
+ nni_strfree(ws->reshdrs);
nni_http_fini(ws->http);
nni_aio_fini(ws->rxaio);
nni_aio_fini(ws->txaio);
@@ -1261,12 +1294,20 @@ ws_init(nni_ws **wsp, nni_http *http, nni_http_req *req, nni_http_res *res)
void
nni_ws_listener_fini(nni_ws_listener *l)
{
+ ws_header *hdr;
+
nni_mtx_fini(&l->mtx);
nni_strfree(l->url);
nni_strfree(l->proto);
nni_strfree(l->host);
nni_strfree(l->serv);
nni_strfree(l->path);
+ while ((hdr = nni_list_first(&l->headers)) != NULL) {
+ nni_list_remove(&l->headers, hdr);
+ nni_strfree(hdr->name);
+ nni_strfree(hdr->value);
+ NNI_FREE_STRUCT(hdr);
+ }
NNI_FREE_STRUCT(l);
}
@@ -1718,6 +1759,7 @@ ws_conn_cb(void *arg)
uint8_t raw[16];
char wskey[25];
nni_ws * ws;
+ ws_header * hdr;
nni_mtx_lock(&d->mtx);
uaio = nni_list_first(&d->conaios);
@@ -1776,6 +1818,13 @@ ws_conn_cb(void *arg)
((rv = SETH("Sec-WebSocket-Protocol", d->proto)) != 0)) {
goto err;
}
+
+ NNI_LIST_FOREACH (&d->headers, hdr) {
+ if ((rv = SETH(hdr->name, hdr->value)) != 0) {
+ goto err;
+ }
+ }
+
#undef SETH
if ((rv = ws_init(&ws, http, req, NULL)) != 0) {
@@ -1807,6 +1856,8 @@ err:
void
nni_ws_dialer_fini(nni_ws_dialer *d)
{
+ ws_header *hdr;
+
nni_aio_fini(d->conaio);
nni_strfree(d->proto);
nni_strfree(d->addr);
@@ -1815,6 +1866,12 @@ nni_ws_dialer_fini(nni_ws_dialer *d)
nni_strfree(d->serv);
nni_strfree(d->path);
nni_strfree(d->qinfo);
+ while ((hdr = nni_list_first(&d->headers)) != NULL) {
+ nni_list_remove(&d->headers, hdr);
+ nni_strfree(hdr->name);
+ nni_strfree(hdr->value);
+ NNI_FREE_STRUCT(hdr);
+ }
if (d->client) {
nni_http_client_fini(d->client);
}
@@ -1832,6 +1889,7 @@ nni_ws_dialer_init(nni_ws_dialer **dp, const char *url)
if ((d = NNI_ALLOC_STRUCT(d)) == NULL) {
return (NNG_ENOMEM);
}
+ NNI_LIST_INIT(&d->headers, ws_header, node);
nni_mtx_init(&d->mtx);
nni_aio_list_init(&d->conaios);
nni_aio_list_init(&d->httpaios);
@@ -1877,7 +1935,6 @@ nni_ws_dialer_init(nni_ws_dialer **dp, const char *url)
void
nni_ws_dialer_close(nni_ws_dialer *d)
{
- // XXX: what to do here?
nni_mtx_lock(&d->mtx);
if (d->closed) {
nni_mtx_unlock(&d->mtx);
@@ -1944,7 +2001,47 @@ nni_ws_dialer_dial(nni_ws_dialer *d, nni_aio *aio)
nni_mtx_unlock(&d->mtx);
}
-extern int nni_ws_dialer_header(nni_ws_dialer *, const char *, const char *);
+static int
+ws_set_header(nni_list *l, const char *n, const char *v)
+{
+ ws_header *hdr;
+ char * nv;
+
+ if ((nv = nni_strdup(v)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+
+ NNI_LIST_FOREACH (l, hdr) {
+ if (strcasecmp(hdr->name, n) == 0) {
+ nni_strfree(hdr->value);
+ hdr->value = nv;
+ return (0);
+ }
+ }
+
+ if ((hdr = NNI_ALLOC_STRUCT(hdr)) == NULL) {
+ nni_strfree(nv);
+ return (NNG_ENOMEM);
+ }
+ if ((hdr->name = strdup(n)) == NULL) {
+ nni_strfree(nv);
+ NNI_FREE_STRUCT(hdr);
+ return (NNG_ENOMEM);
+ }
+ hdr->value = nv;
+ nni_list_append(l, hdr);
+ return (0);
+}
+
+int
+nni_ws_dialer_header(nni_ws_dialer *d, const char *n, const char *v)
+{
+ int rv;
+ nni_mtx_lock(&d->mtx);
+ rv = ws_set_header(&d->headers, n, v);
+ nni_mtx_unlock(&d->mtx);
+ return (rv);
+}
// Dialer does not get a hook chance, as it can examine the request
// and reply after dial is done; this is not a 3-way handshake, so
diff --git a/src/supplemental/websocket/websocket.h b/src/supplemental/websocket/websocket.h
index 3c3ce085..95147a9f 100644
--- a/src/supplemental/websocket/websocket.h
+++ b/src/supplemental/websocket/websocket.h
@@ -59,6 +59,8 @@ extern int nni_ws_peer_addr(nni_ws *, nni_sockaddr *);
extern void nni_ws_close(nni_ws *);
extern void nni_ws_close_error(nni_ws *, uint16_t);
extern void nni_ws_fini(nni_ws *);
+extern const char * nni_ws_response_headers(nni_ws *);
+extern const char * nni_ws_request_headers(nni_ws *);
// The implementation will send periodic PINGs, and respond with PONGs.
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
diff --git a/tests/ws.c b/tests/ws.c
index eff95ab3..71836390 100644
--- a/tests/ws.c
+++ b/tests/ws.c
@@ -11,6 +11,7 @@
#include "convey.h"
#include "nng.h"
#include "protocol/pair1/pair.h"
+#include "transport/ws/websocket.h"
#include "trantest.h"
#include "stubs.h"
@@ -48,6 +49,41 @@ check_props_v4(nng_msg *msg, nng_listener l, nng_dialer d)
So(ra.s_un.s_in.sa_port != 0);
So(ra.s_un.s_in.sa_addr == htonl(0x7f000001));
});
+
+ Convey("Request header property works", {
+ char * buf;
+ size_t len;
+ z = 0;
+ buf = NULL;
+ So(nng_pipe_getopt(p, NNG_OPT_WS_REQUEST_HEADERS, buf, &z) ==
+ 0);
+ So(z > 0);
+ len = z;
+ So((buf = nni_alloc(len)) != NULL);
+ So(nng_pipe_getopt(p, NNG_OPT_WS_REQUEST_HEADERS, buf, &z) ==
+ 0);
+ So(strstr(buf, "Sec-WebSocket-Key") != NULL);
+ So(z == len);
+ nni_free(buf, len);
+ });
+
+ Convey("Response header property works", {
+ char * buf;
+ size_t len;
+ z = 0;
+ buf = NULL;
+ So(nng_pipe_getopt(p, NNG_OPT_WS_RESPONSE_HEADERS, buf, &z) ==
+ 0);
+ So(z > 0);
+ len = z;
+ So((buf = nni_alloc(len)) != NULL);
+ So(nng_pipe_getopt(p, NNG_OPT_WS_RESPONSE_HEADERS, buf, &z) ==
+ 0);
+ So(strstr(buf, "Sec-WebSocket-Accept") != NULL);
+ So(z == len);
+ nni_free(buf, len);
+ });
+
return (0);
}