aboutsummaryrefslogtreecommitdiff
path: root/src/supplemental/http/http_client.c
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2018-01-24 17:38:16 -0800
committerGarrett D'Amore <garrett@damore.org>2018-02-01 16:11:38 -0800
commit3dae30ed5e543dc73fc993334ef56b9b157b9b3c (patch)
treed7e294b5d544aa18e8fc8749abfe605a05fa4bd7 /src/supplemental/http/http_client.c
parent5914e40c2ff7fcf346c90705785f3fb7650a9fdc (diff)
downloadnng-3dae30ed5e543dc73fc993334ef56b9b157b9b3c.tar.gz
nng-3dae30ed5e543dc73fc993334ef56b9b157b9b3c.tar.bz2
nng-3dae30ed5e543dc73fc993334ef56b9b157b9b3c.zip
fixes #173 Define public HTTP server API
This introduces enough of the HTTP API to support fully server applications, including creation of websocket style protocols, pluggable handlers, and so forth. We have also introduced scatter/gather I/O (rudimentary) for aios, and made other enhancements to the AIO framework. The internals of the AIOs themselves are now fully private, and we have eliminated the aio->a_addr member, with plans to remove the pipe and possibly message members as well. A few other minor issues were found and fixed as well. The HTTP API includes request, response, and connection objects, which can be used with both servers and clients. It also defines the HTTP server and handler objects, which support server applications. Support for client applications will require a client object to be exposed, and that should be happening shortly. None of this is "documented" yet, bug again, we will follow up shortly.
Diffstat (limited to 'src/supplemental/http/http_client.c')
-rw-r--r--src/supplemental/http/http_client.c248
1 files changed, 248 insertions, 0 deletions
diff --git a/src/supplemental/http/http_client.c b/src/supplemental/http/http_client.c
new file mode 100644
index 00000000..345e5947
--- /dev/null
+++ b/src/supplemental/http/http_client.c
@@ -0,0 +1,248 @@
+//
+// 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
+// file was obtained (LICENSE.txt). A copy of the license may also be
+// found online at https://opensource.org/licenses/MIT.
+//
+
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "core/nng_impl.h"
+#include "supplemental/tls/tls.h"
+
+#include "http.h"
+
+struct nni_http_client {
+ nni_list aios;
+ nni_mtx mtx;
+ bool closed;
+ nng_tls_config * tls;
+ nni_aio * connaio;
+ nni_plat_tcp_ep *tep;
+};
+
+static void
+http_conn_start(nni_http_client *c)
+{
+ nni_plat_tcp_ep_connect(c->tep, c->connaio);
+}
+
+static void
+http_conn_done(void *arg)
+{
+ nni_http_client * c = arg;
+ nni_aio * aio;
+ int rv;
+ nni_plat_tcp_pipe *p;
+ nni_http_conn * conn;
+
+ nni_mtx_lock(&c->mtx);
+ rv = nni_aio_result(c->connaio);
+ p = rv == 0 ? nni_aio_get_pipe(c->connaio) : NULL;
+ if ((aio = nni_list_first(&c->aios)) == NULL) {
+ if (p != NULL) {
+ nni_plat_tcp_pipe_fini(p);
+ }
+ nni_mtx_unlock(&c->mtx);
+ return;
+ }
+ nni_aio_list_remove(aio);
+
+ if (rv != 0) {
+ nni_aio_finish_error(aio, rv);
+ nni_mtx_unlock(&c->mtx);
+ return;
+ }
+
+ if (c->tls != NULL) {
+ rv = nni_http_conn_init_tls(&conn, c->tls, p);
+ } else {
+ rv = nni_http_conn_init_tcp(&conn, p);
+ }
+ if (rv != 0) {
+ nni_aio_finish_error(aio, rv);
+ nni_mtx_unlock(&c->mtx);
+ return;
+ }
+
+ nni_aio_set_output(aio, 0, conn);
+ nni_aio_finish(aio, 0, 0);
+
+ if (!nni_list_empty(&c->aios)) {
+ http_conn_start(c);
+ }
+ nni_mtx_unlock(&c->mtx);
+}
+
+void
+nni_http_client_fini(nni_http_client *c)
+{
+ nni_aio_fini(c->connaio);
+ nni_plat_tcp_ep_fini(c->tep);
+ nni_mtx_fini(&c->mtx);
+#ifdef NNG_SUPP_TLS
+ if (c->tls != NULL) {
+ nni_tls_config_fini(c->tls);
+ }
+#endif
+ NNI_FREE_STRUCT(c);
+}
+
+int
+nni_http_client_init(nni_http_client **cp, nni_url *url)
+{
+ int rv;
+ nni_http_client *c;
+ nni_aio * aio;
+ nni_sockaddr sa;
+ char * host;
+ char * port;
+
+ if (strlen(url->u_hostname) == 0) {
+ // We require a valid hostname.
+ 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);
+ }
+
+ // 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) {
+ return (rv);
+ }
+ nni_aio_set_input(aio, 0, &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) {
+ return (rv);
+ }
+
+ if ((c = NNI_ALLOC_STRUCT(c)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ nni_mtx_init(&c->mtx);
+ nni_aio_list_init(&c->aios);
+
+#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);
+ return (rv);
+ }
+ *cp = c;
+ return (0);
+}
+
+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;
+ c->tls = tls;
+ if (tls != NULL) {
+ nni_tls_config_hold(tls);
+ }
+ nni_mtx_unlock(&c->mtx);
+ if (old != NULL) {
+ 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)
+{
+ nni_http_client *c = nni_aio_get_prov_data(aio);
+ nni_mtx_lock(&c->mtx);
+ if (nni_aio_list_active(aio)) {
+ nni_aio_list_remove(aio);
+ nni_aio_finish_error(aio, rv);
+ }
+ if (nni_list_empty(&c->aios)) {
+ nni_aio_abort(c->connaio, rv);
+ }
+ nni_mtx_unlock(&c->mtx);
+}
+
+void
+nni_http_client_connect(nni_http_client *c, nni_aio *aio)
+{
+ if (nni_aio_start(aio, http_connect_cancel, aio) != 0) {
+ return;
+ }
+ nni_mtx_lock(&c->mtx);
+ nni_list_append(&c->aios, aio);
+ if (nni_list_first(&c->aios) == aio) {
+ http_conn_start(c);
+ }
+ nni_mtx_unlock(&c->mtx);
+}