aboutsummaryrefslogtreecommitdiff
path: root/src/supplemental/http
diff options
context:
space:
mode:
Diffstat (limited to 'src/supplemental/http')
-rw-r--r--src/supplemental/http/client.c115
-rw-r--r--src/supplemental/http/http.h37
-rw-r--r--src/supplemental/http/server.c146
3 files changed, 239 insertions, 59 deletions
diff --git a/src/supplemental/http/client.c b/src/supplemental/http/client.c
index b1794f93..7542043d 100644
--- a/src/supplemental/http/client.c
+++ b/src/supplemental/http/client.c
@@ -1,6 +1,6 @@
//
-// Copyright 2017 Staysail Systems, Inc. <info@staysail.tech>
-// Copyright 2017 Capitar IT Group BV <info@capitar.com>
+// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2018 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
@@ -19,12 +19,12 @@
#include "http.h"
struct nni_http_client {
- nng_sockaddr addr;
nni_list aios;
nni_mtx mtx;
bool closed;
nng_tls_config * tls;
nni_aio * connaio;
+ nni_url * url;
nni_plat_tcp_ep *tep;
};
@@ -92,26 +92,98 @@ nni_http_client_fini(nni_http_client *c)
nni_tls_config_fini(c->tls);
}
#endif
+ if (c->url != NULL) {
+ nni_url_free(c->url);
+ }
NNI_FREE_STRUCT(c);
}
int
-nni_http_client_init(nni_http_client **cp, nng_sockaddr *sa)
+nni_http_client_init(nni_http_client **cp, const char *urlstr)
{
- int rv;
-
+ int rv;
+ nni_url * url;
nni_http_client *c;
- if ((c = NNI_ALLOC_STRUCT(c)) == NULL) {
- return (NNG_ENOMEM);
+ nni_aio * aio;
+ nni_sockaddr sa;
+ char * host;
+ char * port;
+
+ if ((rv = nni_url_parse(&url, urlstr)) != 0) {
+ return (rv);
+ }
+
+ if (strlen(url->u_hostname) == 0) {
+ // We require a valid hostname.
+ nni_url_free(url);
+ return (NNG_EADDRINVAL);
+ }
+ if ((strcmp(url->u_scheme, "http") != 0) &&
+#ifdef NNG_SUPP_TLS
+ (strcmp(url->u_scheme, "https") != 0) &&
+ (strcmp(url->u_scheme, "wss") != 0) &&
+#endif
+ (strcmp(url->u_scheme, "ws") != 0)) {
+ return (NNG_EADDRINVAL);
}
- c->addr = *sa;
- rv = nni_plat_tcp_ep_init(&c->tep, NULL, &c->addr, NNI_EP_MODE_DIAL);
+
+ // For now we are looking up the address. We would really like
+ // to do this later, but we need TcP support for this. One
+ // imagines the ability to create a tcp dialer that does the
+ // necessary DNS lookups, etc. all asynchronously.
+ if ((rv = nni_aio_init(&aio, NULL, NULL)) != 0) {
+ nni_url_free(url);
+ return (rv);
+ }
+ aio->a_addr = &sa;
+ host = (strlen(url->u_hostname) != 0) ? url->u_hostname : NULL;
+ port = (strlen(url->u_port) != 0) ? url->u_port : NULL;
+ nni_plat_tcp_resolv(host, port, NNG_AF_UNSPEC, false, aio);
+ nni_aio_wait(aio);
+ rv = nni_aio_result(aio);
+ nni_aio_fini(aio);
if (rv != 0) {
- NNI_FREE_STRUCT(c);
+ nni_url_free(url);
return (rv);
}
+
+ if ((c = NNI_ALLOC_STRUCT(c)) == NULL) {
+ return (NNG_ENOMEM);
+ }
nni_mtx_init(&c->mtx);
nni_aio_list_init(&c->aios);
+ c->url = url;
+
+#ifdef NNG_SUPP_TLS
+ if ((strcmp(url->u_scheme, "https") == 0) ||
+ (strcmp(url->u_scheme, "wss") == 0)) {
+ rv = nni_tls_config_init(&c->tls, NNG_TLS_MODE_CLIENT);
+ if (rv != 0) {
+ nni_http_client_fini(c);
+ return (rv);
+ }
+ // Take the server name right from the client URL. We only
+ // consider the name, as the port is never part of the
+ // certificate.
+ rv = nng_tls_config_server_name(c->tls, url->u_hostname);
+ if (rv != 0) {
+ nni_http_client_fini(c);
+ return (rv);
+ }
+
+ // Note that the application has to supply the location of
+ // certificates. We could probably use a default based
+ // on environment or common locations used by OpenSSL, but
+ // as there is no way to *unload* the cert file, lets not
+ // do that. (We might want to consider a mode to reset.)
+ }
+#endif
+
+ rv = nni_plat_tcp_ep_init(&c->tep, NULL, &sa, NNI_EP_MODE_DIAL);
+ if (rv != 0) {
+ nni_http_client_fini(c);
+ return (rv);
+ }
if ((rv = nni_aio_init(&c->connaio, http_conn_done, c)) != 0) {
nni_http_client_fini(c);
@@ -121,10 +193,10 @@ nni_http_client_init(nni_http_client **cp, nng_sockaddr *sa)
return (0);
}
-#ifdef NNG_SUPP_TLS
int
nni_http_client_set_tls(nni_http_client *c, nng_tls_config *tls)
{
+#ifdef NNG_SUPP_TLS
nng_tls_config *old;
nni_mtx_lock(&c->mtx);
old = c->tls;
@@ -137,8 +209,27 @@ nni_http_client_set_tls(nni_http_client *c, nng_tls_config *tls)
nni_tls_config_fini(old);
}
return (0);
+#else
+ return (NNG_EINVAL);
+#endif
}
+
+int
+nni_http_client_get_tls(nni_http_client *c, nng_tls_config **tlsp)
+{
+#ifdef NNG_SUPP_TLS
+ nni_mtx_lock(&c->mtx);
+ if (c->tls == NULL) {
+ nni_mtx_unlock(&c->mtx);
+ return (NNG_EINVAL);
+ }
+ *tlsp = c->tls;
+ nni_mtx_unlock(&c->mtx);
+ return (0);
+#else
+ return (NNG_ENOTSUP);
#endif
+}
static void
http_connect_cancel(nni_aio *aio, int rv)
diff --git a/src/supplemental/http/http.h b/src/supplemental/http/http.h
index 0f3affc0..06394fdd 100644
--- a/src/supplemental/http/http.h
+++ b/src/supplemental/http/http.h
@@ -222,11 +222,14 @@ typedef struct {
} nni_http_handler;
// nni_http_server will look for an existing server with the same
-// socket address, or create one if one does not exist. The servers
+// name and port, or create one if one does not exist. The servers
// are reference counted to permit sharing the server object across
-// multiple subsystems. The sockaddr matching is very limited though,
-// and the addresses must match *exactly*.
-extern int nni_http_server_init(nni_http_server **, nng_sockaddr *);
+// multiple subsystems. The URL hostname matching is very limited,
+// and the names must match *exactly* (without DNS resolution). Unless
+// a restricted binding is required, we recommend using a URL consisting
+// of an empty host name, such as http:// or https:// -- this would
+// convert to binding to the default port on all interfaces on the host.
+extern int nni_http_server_init(nni_http_server **, const char *);
// nni_http_server_fini drops the reference count on the server, and
// if this was the last reference, closes down the server and frees
@@ -245,9 +248,17 @@ extern void nni_http_server_del_handler(nni_http_server *, void *);
// nni_http_server_set_tls adds a TLS configuration to the server,
// and enables the use of it. This returns NNG_EBUSY if the server is
-// already started.
+// already started. This wipes out the entire TLS configuration on the
+// server client, so the caller must have configured it reasonably.
+// This API is not recommended unless the caller needs complete control
+// over the TLS configuration.
extern int nni_http_server_set_tls(nni_http_server *, nng_tls_config *);
+// nni_http_server_get_tls obtains the TLS configuration if one is present,
+// or returns NNG_EINVAL. The TLS configuration is invalidated if the
+// nni_http_server_set_tls function is called, so be careful.
+extern int nni_http_server_get_tls(nni_http_server *, nng_tls_config **);
+
// nni_http_server_start starts listening on the supplied port.
extern int nni_http_server_start(nni_http_server *);
@@ -279,9 +290,21 @@ extern int nni_http_server_add_file(nni_http_server *, const char *host,
typedef struct nni_http_client nni_http_client;
-extern int nni_http_client_init(nni_http_client **, nng_sockaddr *);
+// https vs. http; would also allow us to defer DNS lookups til later.
+extern int nni_http_client_init(nni_http_client **, const char *);
extern void nni_http_client_fini(nni_http_client *);
-extern int nni_http_client_set_tls(nni_http_client *, nng_tls_config *);
+
+// nni_http_client_set_tls sets the TLS configuration. This wipes out
+// the entire TLS configuration on the client, so the caller must have
+// configured it reasonably. This API is not recommended unless the
+// caller needs complete control over the TLS configuration.
+extern int nni_http_client_set_tls(nni_http_client *, nng_tls_config *);
+
+// nni_http_client_get_tls obtains the TLS configuration if one is present,
+// or returns NNG_EINVAL. The supplied TLS configuration object may
+// be invalidated by any future calls to nni_http_client_set_tls.
+extern int nni_http_client_get_tls(nni_http_client *, nng_tls_config **);
+
extern void nni_http_client_connect(nni_http_client *, nni_aio *);
#endif // NNG_SUPPLEMENTAL_HTTP_HTTP_H
diff --git a/src/supplemental/http/server.c b/src/supplemental/http/server.c
index 01f1230d..700ec536 100644
--- a/src/supplemental/http/server.c
+++ b/src/supplemental/http/server.c
@@ -69,6 +69,7 @@ struct nni_http_server {
nng_tls_config * tls;
nni_aio * accaio;
nni_plat_tcp_ep *tep;
+ nni_url * url;
};
static nni_list http_servers;
@@ -78,13 +79,15 @@ static void http_handler_fini(http_handler *);
static void
http_sconn_reap(void *arg)
{
- http_sconn *sc = arg;
+ http_sconn * sc = arg;
+ nni_http_server *s = sc->server;
NNI_ASSERT(!sc->finished);
sc->finished = true;
nni_aio_stop(sc->rxaio);
nni_aio_stop(sc->txaio);
nni_aio_stop(sc->txdataio);
nni_aio_stop(sc->cbaio);
+
if (sc->http != NULL) {
nni_http_fini(sc->http);
}
@@ -98,6 +101,17 @@ http_sconn_reap(void *arg)
nni_aio_fini(sc->txaio);
nni_aio_fini(sc->txdataio);
nni_aio_fini(sc->cbaio);
+
+ // Now it is safe to release our reference on the server.
+ nni_mtx_lock(&s->mtx);
+ if (nni_list_node_active(&sc->node)) {
+ nni_list_remove(&s->conns, sc);
+ if (nni_list_empty(&s->conns)) {
+ nni_cv_wake(&s->cv);
+ }
+ }
+ nni_mtx_unlock(&s->mtx);
+
NNI_FREE_STRUCT(sc);
}
@@ -120,13 +134,6 @@ http_sconn_close_locked(http_sconn *sc)
NNI_ASSERT(!sc->finished);
sc->closed = true;
- // Close the underlying transport.
- if (nni_list_node_active(&sc->node)) {
- nni_list_remove(&s->conns, sc);
- if (nni_list_empty(&s->conns)) {
- nni_cv_wake(&s->cv);
- }
- }
nni_aio_cancel(sc->rxaio, NNG_ECLOSED);
nni_aio_cancel(sc->txaio, NNG_ECLOSED);
nni_aio_cancel(sc->txdataio, NNG_ECLOSED);
@@ -144,10 +151,6 @@ http_sconn_close(http_sconn *sc)
nni_http_server *s;
s = sc->server;
- if (sc->closed) {
- return;
- }
-
nni_mtx_lock(&s->mtx);
http_sconn_close_locked(sc);
nni_mtx_unlock(&s->mtx);
@@ -631,6 +634,9 @@ http_server_fini(nni_http_server *s)
http_handler_fini(h);
}
nni_mtx_unlock(&s->mtx);
+ if (s->url != NULL) {
+ nni_url_free(s->url);
+ }
#ifdef NNG_SUPP_TLS
if (s->tls != NULL) {
nni_tls_config_fini(s->tls);
@@ -655,14 +661,34 @@ nni_http_server_fini(nni_http_server *s)
}
static int
-http_server_init(nni_http_server **serverp, nng_sockaddr *sa)
+http_server_init(nni_http_server **serverp, nni_url *url)
{
nni_http_server *s;
int rv;
+ const char * host;
+ const char * port;
+ nni_aio * aio;
+
+ host = url->u_hostname;
+ if (strlen(host) == 0) {
+ host = NULL;
+ }
+ port = url->u_port;
+ if ((strcmp(url->u_scheme, "http") != 0) &&
+#ifdef NNG_SUPP_TLS
+ (strcmp(url->u_scheme, "https") != 0) &&
+ (strcmp(url->u_scheme, "wss") != 0) &&
+#endif
+ (strcmp(url->u_scheme, "ws") != 0)) {
+ nni_url_free(url);
+ return (NNG_EADDRINVAL);
+ }
if ((s = NNI_ALLOC_STRUCT(s)) == NULL) {
+ nni_url_free(url);
return (NNG_ENOMEM);
}
+ s->url = url;
nni_mtx_init(&s->mtx);
nni_cv_init(&s->cv, &s->mtx);
NNI_LIST_INIT(&s->handlers, http_handler, node);
@@ -671,49 +697,71 @@ http_server_init(nni_http_server **serverp, nng_sockaddr *sa)
http_server_fini(s);
return (rv);
}
- s->addr = *sa;
- *serverp = s;
+#ifdef NNG_SUPP_TLS
+ if ((strcmp(url->u_scheme, "https") == 0) ||
+ (strcmp(url->u_scheme, "wss") == 0)) {
+ rv = nni_tls_config_init(&s->tls, NNG_TLS_MODE_SERVER);
+ if (rv != 0) {
+ http_server_fini(s);
+ return (rv);
+ }
+ }
+#endif
+
+ // Do the DNS lookup *now*. This means that this is synchronous,
+ // but it should be fast, since it should either resolve as a number,
+ // or resolve locally, without having to hit up DNS.
+ if ((rv = nni_aio_init(&aio, NULL, NULL)) != 0) {
+ http_server_fini(s);
+ return (rv);
+ }
+ aio->a_addr = &s->addr;
+ host = (strlen(url->u_hostname) != 0) ? url->u_hostname : NULL;
+ port = (strlen(url->u_port) != 0) ? url->u_port : NULL;
+ nni_plat_tcp_resolv(host, port, NNG_AF_UNSPEC, true, aio);
+ nni_aio_wait(aio);
+ rv = nni_aio_result(aio);
+ nni_aio_fini(aio);
+ if (rv != 0) {
+ http_server_fini(s);
+ return (rv);
+ }
+ s->refcnt = 1;
+ *serverp = s;
return (0);
}
int
-nni_http_server_init(nni_http_server **serverp, nng_sockaddr *sa)
+nni_http_server_init(nni_http_server **serverp, const char *urlstr)
{
int rv;
nni_http_server *s;
+ nni_url * url;
+
+ if ((rv = nni_url_parse(&url, urlstr)) != 0) {
+ return (rv);
+ }
nni_initialize(&http_server_initializer);
nni_mtx_lock(&http_servers_lk);
NNI_LIST_FOREACH (&http_servers, s) {
- switch (sa->s_un.s_family) {
- case NNG_AF_INET:
- if (memcmp(&s->addr.s_un.s_in, &sa->s_un.s_in,
- sizeof(sa->s_un.s_in)) == 0) {
- *serverp = s;
- s->refcnt++;
- nni_mtx_unlock(&http_servers_lk);
- return (0);
- }
- break;
- case NNG_AF_INET6:
- if (memcmp(&s->addr.s_un.s_in6, &sa->s_un.s_in6,
- sizeof(sa->s_un.s_in6)) == 0) {
- *serverp = s;
- s->refcnt++;
- nni_mtx_unlock(&http_servers_lk);
- return (0);
- }
- break;
+ if ((strcmp(url->u_port, s->url->u_port) == 0) &&
+ (strcmp(url->u_hostname, s->url->u_hostname) == 0)) {
+ nni_url_free(url);
+ *serverp = s;
+ s->refcnt++;
+ nni_mtx_unlock(&http_servers_lk);
+ return (0);
}
}
// We didn't find a server, try to make a new one.
- if ((rv = http_server_init(&s, sa)) == 0) {
- s->addr = *sa;
- s->refcnt = 1;
+ if ((rv = http_server_init(&s, url)) == 0) {
nni_list_append(&http_servers, s);
*serverp = s;
+ } else {
+ nni_url_free(url);
}
nni_mtx_unlock(&http_servers_lk);
@@ -724,7 +772,6 @@ static int
http_server_start(nni_http_server *s)
{
int rv;
-
rv = nni_plat_tcp_ep_init(&s->tep, &s->addr, NULL, NNI_EP_MODE_LISTEN);
if (rv != 0) {
return (rv);
@@ -1116,10 +1163,10 @@ nni_http_server_add_static(nni_http_server *s, const char *host,
return (0);
}
-#ifdef NNG_SUPP_TLS
int
nni_http_server_set_tls(nni_http_server *s, nng_tls_config *tcfg)
{
+#ifdef NNG_SUPP_TLS
nng_tls_config *old;
nni_mtx_lock(&s->mtx);
if (s->starts) {
@@ -1136,8 +1183,27 @@ nni_http_server_set_tls(nni_http_server *s, nng_tls_config *tcfg)
nni_tls_config_fini(old);
}
return (0);
+#else
+ return (NNG_ENOTSUP);
+#endif
}
+
+int
+nni_http_server_get_tls(nni_http_server *s, nng_tls_config **tp)
+{
+#ifdef NNG_SUPP_TLS
+ nni_mtx_lock(&s->mtx);
+ if (s->tls == NULL) {
+ nni_mtx_unlock(&s->mtx);
+ return (NNG_EINVAL);
+ }
+ *tp = s->tls;
+ nni_mtx_unlock(&s->mtx);
+ return (0);
+#else
+ return (NNG_ENOTSUP);
#endif
+}
static int
http_server_sys_init(void)