diff options
Diffstat (limited to 'src/supplemental/http')
| -rw-r--r-- | src/supplemental/http/client.c | 115 | ||||
| -rw-r--r-- | src/supplemental/http/http.h | 37 | ||||
| -rw-r--r-- | src/supplemental/http/server.c | 146 |
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) |
