aboutsummaryrefslogtreecommitdiff
path: root/src/supplemental/http
diff options
context:
space:
mode:
Diffstat (limited to 'src/supplemental/http')
-rw-r--r--src/supplemental/http/CMakeLists.txt19
-rw-r--r--src/supplemental/http/http.c740
-rw-r--r--src/supplemental/http/http.h226
-rw-r--r--src/supplemental/http/http_client.c (renamed from src/supplemental/http/client.c)20
-rw-r--r--src/supplemental/http/http_conn.c765
-rw-r--r--src/supplemental/http/http_msg.c318
-rw-r--r--src/supplemental/http/http_public.c668
-rw-r--r--src/supplemental/http/http_server.c (renamed from src/supplemental/http/server.c)406
8 files changed, 1913 insertions, 1249 deletions
diff --git a/src/supplemental/http/CMakeLists.txt b/src/supplemental/http/CMakeLists.txt
index 2c8d6a68..9cfbd14a 100644
--- a/src/supplemental/http/CMakeLists.txt
+++ b/src/supplemental/http/CMakeLists.txt
@@ -9,11 +9,18 @@
#
if (NNG_SUPP_HTTP)
-set(HTTP_SOURCES
- supplemental/http/http.c
- supplemental/http/http_msg.c
- supplemental/http/server.c
- supplemental/http/client.c
- supplemental/http/http.h)
+ set(HTTP_DEFINES -DNNG_SUPP_HTTP)
+ set(HTTP_SOURCES
+ supplemental/http/http.h
+ supplemental/http/http_client.c
+ supplemental/http/http_conn.c
+ supplemental/http/http_msg.c
+ supplemental/http/http_public.c
+ supplemental/http/http_server.c)
+else()
+ set(HTTP_SOURCES
+ supplemental/http/http.h
+ supplemental/http/http_public.c)
endif()
+set(NNG_DEFINES ${NNG_DEFINES} ${HTTP_DEFINES} PARENT_SCOPE)
set(NNG_SOURCES ${NNG_SOURCES} ${HTTP_SOURCES} PARENT_SCOPE)
diff --git a/src/supplemental/http/http.c b/src/supplemental/http/http.c
deleted file mode 100644
index 2ba5b274..00000000
--- a/src/supplemental/http/http.c
+++ /dev/null
@@ -1,740 +0,0 @@
-//
-// 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 <stdbool.h>
-#include <string.h>
-
-#include "core/nng_impl.h"
-#include "supplemental/tls/tls.h"
-
-#include "http.h"
-
-// We insist that individual headers fit in 8K.
-// If you need more than that, you need something we can't do.
-#define HTTP_BUFSIZE 8192
-
-// types of reads
-enum read_flavor {
- HTTP_RD_RAW,
- HTTP_RD_FULL,
- HTTP_RD_REQ,
- HTTP_RD_RES,
-};
-
-enum write_flavor {
- HTTP_WR_RAW,
- HTTP_WR_FULL,
- HTTP_WR_REQ,
- HTTP_WR_RES,
-};
-
-typedef struct nni_http_tran {
- void (*h_read)(void *, nni_aio *);
- void (*h_write)(void *, nni_aio *);
- int (*h_sock_addr)(void *, nni_sockaddr *);
- int (*h_peer_addr)(void *, nni_sockaddr *);
- bool (*h_verified)(void *);
- void (*h_close)(void *);
- void (*h_fini)(void *);
-} nni_http_tran;
-
-#define SET_RD_FLAVOR(aio, f) (aio)->a_prov_extra[0] = ((void *) (intptr_t)(f))
-#define GET_RD_FLAVOR(aio) (int) ((intptr_t) aio->a_prov_extra[0])
-#define SET_WR_FLAVOR(aio, f) (aio)->a_prov_extra[0] = ((void *) (intptr_t)(f))
-#define GET_WR_FLAVOR(aio) (int) ((intptr_t) aio->a_prov_extra[0])
-
-struct nni_http {
- void *sock;
- void (*rd)(void *, nni_aio *);
- void (*wr)(void *, nni_aio *);
- int (*sock_addr)(void *, nni_sockaddr *);
- int (*peer_addr)(void *, nni_sockaddr *);
- bool (*verified)(void *);
- void (*close)(void *);
- void (*fini)(void *);
-
- bool closed;
-
- nni_list rdq; // high level http read requests
- nni_list wrq; // high level http write requests
-
- nni_aio *rd_uaio; // user aio for read
- nni_aio *wr_uaio; // user aio for write
- nni_aio *rd_aio; // bottom half read operations
- nni_aio *wr_aio; // bottom half write operations
-
- nni_mtx mtx;
-
- uint8_t *rd_buf;
- size_t rd_get;
- size_t rd_put;
- size_t rd_bufsz;
-};
-
-static void
-http_close(nni_http *http)
-{
- // Call with lock held.
- nni_aio *aio;
-
- if (http->closed) {
- return;
- }
-
- http->closed = true;
- if (nni_list_first(&http->wrq)) {
- nni_aio_cancel(http->wr_aio, NNG_ECLOSED);
- // Abort all operations except the one in flight.
- while ((aio = nni_list_last(&http->wrq)) !=
- nni_list_first(&http->wrq)) {
- nni_aio_list_remove(aio);
- nni_aio_finish_error(aio, NNG_ECLOSED);
- }
- }
- if (nni_list_first(&http->rdq)) {
- nni_aio_cancel(http->rd_aio, NNG_ECLOSED);
- while ((aio = nni_list_last(&http->rdq)) !=
- nni_list_first(&http->rdq)) {
- nni_aio_list_remove(aio);
- nni_aio_finish_error(aio, NNG_ECLOSED);
- }
- }
-
- if (http->sock != NULL) {
- http->close(http->sock);
- }
-}
-
-void
-nni_http_close(nni_http *http)
-{
- nni_mtx_lock(&http->mtx);
- http_close(http);
- nni_mtx_unlock(&http->mtx);
-}
-
-// http_rd_buf attempts to satisfy the read from data in the buffer.
-static int
-http_rd_buf(nni_http *http, nni_aio *aio)
-{
- size_t cnt = http->rd_put - http->rd_get;
- size_t n;
- uint8_t *rbuf = http->rd_buf;
- int i;
- int rv;
- bool raw = false;
-
- rbuf += http->rd_get;
-
- switch (GET_RD_FLAVOR(aio)) {
- case HTTP_RD_RAW:
- raw = true; // FALLTHROUGH
- case HTTP_RD_FULL:
- for (i = 0; (aio->a_niov != 0) && (cnt != 0); i++) {
- // Pull up data from the buffer if possible.
- n = aio->a_iov[0].iov_len;
- if (n > cnt) {
- n = cnt;
- }
- memcpy(aio->a_iov[0].iov_buf, rbuf, n);
- aio->a_iov[0].iov_len -= n;
- NNI_INCPTR(aio->a_iov[0].iov_buf, n);
- http->rd_get += n;
- rbuf += n;
- aio->a_count += n;
- cnt -= n;
-
- if (aio->a_iov[0].iov_len == 0) {
- aio->a_niov--;
- for (i = 0; i < aio->a_niov; i++) {
- aio->a_iov[i] = aio->a_iov[i + 1];
- }
- }
- }
-
- if ((aio->a_niov == 0) || (raw && (aio->a_count != 0))) {
- // Finished the read. (We are finished if we either
- // got *all* the data, or we got *some* data for
- // a raw read.)
- return (0);
- }
-
- // No more data left in the buffer, so use a physio.
- // (Note that we get here if we either have not completed
- // a full transaction on a FULL read, or were not even able
- // to get *any* data for a partial RAW read.)
- for (i = 0; i < aio->a_niov; i++) {
- http->rd_aio->a_iov[i] = aio->a_iov[i];
- }
- nni_aio_set_data(http->rd_aio, 1, NULL);
- http->rd_aio->a_niov = aio->a_niov;
- http->rd(http->sock, http->rd_aio);
- return (NNG_EAGAIN);
-
- case HTTP_RD_REQ:
- rv = nni_http_req_parse(aio->a_prov_extra[1], rbuf, cnt, &n);
- http->rd_get += n;
- if (http->rd_get == http->rd_put) {
- http->rd_get = http->rd_put = 0;
- }
- if (rv == NNG_EAGAIN) {
- http->rd_aio->a_niov = 1;
- http->rd_aio->a_iov[0].iov_buf =
- http->rd_buf + http->rd_put;
- http->rd_aio->a_iov[0].iov_len =
- http->rd_bufsz - http->rd_put;
- nni_aio_set_data(http->rd_aio, 1, aio);
- http->rd(http->sock, http->rd_aio);
- }
- return (rv);
-
- case HTTP_RD_RES:
- rv = nni_http_res_parse(aio->a_prov_extra[1], rbuf, cnt, &n);
- http->rd_get += n;
- if (http->rd_get == http->rd_put) {
- http->rd_get = http->rd_put = 0;
- }
- if (rv == NNG_EAGAIN) {
- http->rd_aio->a_niov = 1;
- http->rd_aio->a_iov[0].iov_buf =
- http->rd_buf + http->rd_put;
- http->rd_aio->a_iov[0].iov_len =
- http->rd_bufsz - http->rd_put;
- nni_aio_set_data(http->rd_aio, 1, aio);
- http->rd(http->sock, http->rd_aio);
- }
- return (rv);
- }
- return (NNG_EINVAL);
-}
-
-static void
-http_rd_start(nni_http *http)
-{
- for (;;) {
- nni_aio *aio;
- int rv;
-
- if ((aio = http->rd_uaio) == NULL) {
- if ((aio = nni_list_first(&http->rdq)) == NULL) {
- // No more stuff waiting for read.
- return;
- }
- nni_list_remove(&http->rdq, aio);
- http->rd_uaio = aio;
- }
-
- if (http->closed) {
- rv = NNG_ECLOSED;
- } else {
- rv = http_rd_buf(http, aio);
- }
- switch (rv) {
- case NNG_EAGAIN:
- return;
- case 0:
- http->rd_uaio = NULL;
- nni_aio_finish(aio, 0, aio->a_count);
- break;
- default:
- http->rd_uaio = NULL;
- nni_aio_finish_error(aio, rv);
- http_close(http);
- break;
- }
- }
-}
-
-static void
-http_rd_cb(void *arg)
-{
- nni_http *http = arg;
- nni_aio * aio = http->rd_aio;
- nni_aio * uaio;
- size_t cnt;
- int rv;
-
- nni_mtx_lock(&http->mtx);
-
- if ((rv = nni_aio_result(aio)) != 0) {
- if ((uaio = http->rd_uaio) != NULL) {
- http->rd_uaio = NULL;
- nni_aio_finish_error(uaio, rv);
- }
- http_close(http);
- nni_mtx_unlock(&http->mtx);
- return;
- }
-
- cnt = nni_aio_count(aio);
-
- // If we were reading into the buffer, then advance location(s).
- if ((uaio = nni_aio_get_data(aio, 1)) != NULL) {
- http->rd_put += cnt;
- NNI_ASSERT(http->rd_put <= http->rd_bufsz);
- http_rd_start(http);
- nni_mtx_unlock(&http->mtx);
- return;
- }
-
- // Otherwise we are completing a USER request, and there should
- // be no data left in the user buffer.
- NNI_ASSERT(http->rd_get == http->rd_put);
-
- if ((uaio = http->rd_uaio) == NULL) {
- // This indicates that a read request was canceled. This
- // can occur only when shutting down, really.
- nni_mtx_unlock(&http->mtx);
- return;
- }
-
- for (int i = 0; (uaio->a_niov != 0) && (cnt != 0); i++) {
- // Pull up data from the buffer if possible.
- size_t n = uaio->a_iov[0].iov_len;
- if (n > cnt) {
- n = cnt;
- }
- uaio->a_iov[0].iov_len -= n;
- NNI_INCPTR(uaio->a_iov[0].iov_buf, n);
- uaio->a_count += n;
- cnt -= n;
-
- if (uaio->a_iov[0].iov_len == 0) {
- uaio->a_niov--;
- for (i = 0; i < uaio->a_niov; i++) {
- uaio->a_iov[i] = uaio->a_iov[i + 1];
- }
- }
- }
-
- // Resubmit the start. This will attempt to consume data
- // from the read buffer (there won't be any), and then either
- // complete the I/O (for HTTP_RD_RAW, or if there is nothing left),
- // or submit another physio.
- http_rd_start(http);
- nni_mtx_unlock(&http->mtx);
-}
-
-static void
-http_rd_cancel(nni_aio *aio, int rv)
-{
- nni_http *http = aio->a_prov_data;
-
- nni_mtx_lock(&http->mtx);
- if (aio == http->rd_uaio) {
- http->rd_uaio = NULL;
- nni_aio_cancel(http->rd_aio, rv);
- nni_aio_finish_error(aio, rv);
- } else if (nni_aio_list_active(aio)) {
- nni_aio_list_remove(aio);
- nni_aio_finish_error(aio, rv);
- }
- nni_mtx_unlock(&http->mtx);
-}
-
-static void
-http_rd_submit(nni_http *http, nni_aio *aio)
-{
- if (nni_aio_start(aio, http_rd_cancel, http) != 0) {
- return;
- }
- if (http->closed) {
- nni_aio_finish_error(aio, NNG_ECLOSED);
- return;
- }
- nni_list_append(&http->rdq, aio);
- if (http->rd_uaio == NULL) {
- http_rd_start(http);
- }
-}
-
-static void
-http_wr_start(nni_http *http)
-{
- nni_aio *aio;
-
- if ((aio = http->wr_uaio) == NULL) {
- if ((aio = nni_list_first(&http->wrq)) == NULL) {
- // No more stuff waiting for read.
- return;
- }
- nni_list_remove(&http->wrq, aio);
- http->wr_uaio = aio;
- }
-
- for (int i = 0; i < aio->a_niov; i++) {
- http->wr_aio->a_iov[i] = aio->a_iov[i];
- }
- http->wr_aio->a_niov = aio->a_niov;
- http->wr(http->sock, http->wr_aio);
-}
-
-static void
-http_wr_cb(void *arg)
-{
- nni_http *http = arg;
- nni_aio * aio = http->wr_aio;
- nni_aio * uaio;
- int rv;
- size_t n;
-
- nni_mtx_lock(&http->mtx);
-
- uaio = http->wr_uaio;
-
- if ((rv = nni_aio_result(aio)) != 0) {
- // We failed to complete the aio.
- if (uaio != NULL) {
- http->wr_uaio = NULL;
- nni_aio_finish_error(uaio, rv);
- }
- http_close(http);
- nni_mtx_unlock(&http->mtx);
- return;
- }
-
- if (uaio == NULL) {
- // Write canceled? This happens pretty much only during
- // shutdown/close, so we don't want to resume writing.
- // The stream is probably corrupted at this point anyway.
- nni_mtx_unlock(&http->mtx);
- return;
- }
-
- n = nni_aio_count(aio);
- uaio->a_count += n;
- if (GET_WR_FLAVOR(uaio) == HTTP_WR_RAW) {
- // For raw data, we just send partial completion
- // notices to the consumer.
- goto done;
- }
- while (n) {
- NNI_ASSERT(aio->a_niov != 0);
-
- if (aio->a_iov[0].iov_len > n) {
- aio->a_iov[0].iov_len -= n;
- NNI_INCPTR(aio->a_iov[0].iov_buf, n);
- break;
- }
- n -= aio->a_iov[0].iov_len;
- for (int i = 0; i < aio->a_niov; i++) {
- aio->a_iov[i] = aio->a_iov[i + 1];
- }
- aio->a_niov--;
- }
- if ((aio->a_niov != 0) && (aio->a_iov[0].iov_len != 0)) {
- // We have more to transmit - start another and leave
- // (we will get called again when it is done).
- http->wr(http->sock, aio);
- nni_mtx_unlock(&http->mtx);
- return;
- }
-
-done:
- http->wr_uaio = NULL;
- nni_aio_finish(uaio, 0, uaio->a_count);
-
- // Start next write if another is ready.
- http_wr_start(http);
-
- nni_mtx_unlock(&http->mtx);
-}
-
-static void
-http_wr_cancel(nni_aio *aio, int rv)
-{
- nni_http *http = aio->a_prov_data;
-
- nni_mtx_lock(&http->mtx);
- if (aio == http->wr_uaio) {
- http->wr_uaio = NULL;
- nni_aio_cancel(http->wr_aio, rv);
- nni_aio_finish_error(aio, rv);
- } else if (nni_aio_list_active(aio)) {
- nni_aio_list_remove(aio);
- nni_aio_finish_error(aio, rv);
- }
- nni_mtx_unlock(&http->mtx);
-}
-
-static void
-http_wr_submit(nni_http *http, nni_aio *aio)
-{
- if (nni_aio_start(aio, http_wr_cancel, http) != 0) {
- return;
- }
- if (http->closed) {
- nni_aio_finish_error(aio, NNG_ECLOSED);
- return;
- }
- nni_list_append(&http->wrq, aio);
- if (http->wr_uaio == NULL) {
- http_wr_start(http);
- }
-}
-
-void
-nni_http_read_req(nni_http *http, nni_http_req *req, nni_aio *aio)
-{
- SET_RD_FLAVOR(aio, HTTP_RD_REQ);
- aio->a_prov_extra[1] = req;
-
- nni_mtx_lock(&http->mtx);
- http_rd_submit(http, aio);
- nni_mtx_unlock(&http->mtx);
-}
-
-void
-nni_http_read_res(nni_http *http, nni_http_res *res, nni_aio *aio)
-{
- SET_RD_FLAVOR(aio, HTTP_RD_RES);
- aio->a_prov_extra[1] = res;
-
- nni_mtx_lock(&http->mtx);
- http_rd_submit(http, aio);
- nni_mtx_unlock(&http->mtx);
-}
-
-void
-nni_http_read_full(nni_http *http, nni_aio *aio)
-{
- aio->a_count = 0;
- SET_RD_FLAVOR(aio, HTTP_RD_FULL);
- aio->a_prov_extra[1] = NULL;
-
- nni_mtx_lock(&http->mtx);
- http_rd_submit(http, aio);
- nni_mtx_unlock(&http->mtx);
-}
-
-void
-nni_http_read(nni_http *http, nni_aio *aio)
-{
- SET_RD_FLAVOR(aio, HTTP_RD_RAW);
- aio->a_prov_extra[1] = NULL;
-
- nni_mtx_lock(&http->mtx);
- http_rd_submit(http, aio);
- nni_mtx_unlock(&http->mtx);
-}
-
-void
-nni_http_write_req(nni_http *http, nni_http_req *req, nni_aio *aio)
-{
- int rv;
- void * buf;
- size_t bufsz;
-
- if ((rv = nni_http_req_get_buf(req, &buf, &bufsz)) != 0) {
- nni_aio_finish_error(aio, rv);
- return;
- }
- aio->a_niov = 1;
- aio->a_iov[0].iov_len = bufsz;
- aio->a_iov[0].iov_buf = buf;
- SET_WR_FLAVOR(aio, HTTP_WR_REQ);
-
- nni_mtx_lock(&http->mtx);
- http_wr_submit(http, aio);
- nni_mtx_unlock(&http->mtx);
-}
-
-void
-nni_http_write_res(nni_http *http, nni_http_res *res, nni_aio *aio)
-{
- int rv;
- void * buf;
- size_t bufsz;
-
- if ((rv = nni_http_res_get_buf(res, &buf, &bufsz)) != 0) {
- nni_aio_finish_error(aio, rv);
- return;
- }
- aio->a_niov = 1;
- aio->a_iov[0].iov_len = bufsz;
- aio->a_iov[0].iov_buf = buf;
- SET_WR_FLAVOR(aio, HTTP_WR_RES);
-
- nni_mtx_lock(&http->mtx);
- http_wr_submit(http, aio);
- nni_mtx_unlock(&http->mtx);
-}
-
-// Writer. As with nni_http_conn_write, this is used to write data on
-// a connection that has been "upgraded" (e.g. transformed to
-// websocket). It is an error to perform other HTTP exchanges on an
-// connection after this method is called. (This mostly exists to
-// support websocket.)
-void
-nni_http_write(nni_http *http, nni_aio *aio)
-{
- SET_WR_FLAVOR(aio, HTTP_WR_RAW);
-
- nni_mtx_lock(&http->mtx);
- http_wr_submit(http, aio);
- nni_mtx_unlock(&http->mtx);
-}
-
-void
-nni_http_write_full(nni_http *http, nni_aio *aio)
-{
- SET_WR_FLAVOR(aio, HTTP_WR_FULL);
-
- nni_mtx_lock(&http->mtx);
- http_wr_submit(http, aio);
- nni_mtx_unlock(&http->mtx);
-}
-
-int
-nni_http_sock_addr(nni_http *http, nni_sockaddr *sa)
-{
- int rv;
- nni_mtx_lock(&http->mtx);
- rv = http->closed ? NNG_ECLOSED : http->sock_addr(http->sock, sa);
- nni_mtx_unlock(&http->mtx);
- return (rv);
-}
-
-int
-nni_http_peer_addr(nni_http *http, nni_sockaddr *sa)
-{
- int rv;
- nni_mtx_lock(&http->mtx);
- rv = http->closed ? NNG_ECLOSED : http->peer_addr(http->sock, sa);
- nni_mtx_unlock(&http->mtx);
- return (rv);
-}
-
-bool
-nni_http_tls_verified(nni_http *http)
-{
- bool rv;
-
- nni_mtx_lock(&http->mtx);
- rv = http->closed ? false : http->verified(http->sock);
- nni_mtx_unlock(&http->mtx);
- return (rv);
-}
-
-void
-nni_http_fini(nni_http *http)
-{
- nni_mtx_lock(&http->mtx);
- http_close(http);
- if ((http->sock != NULL) && (http->fini != NULL)) {
- http->fini(http->sock);
- http->sock = NULL;
- }
- nni_mtx_unlock(&http->mtx);
- nni_aio_stop(http->wr_aio);
- nni_aio_stop(http->rd_aio);
- nni_aio_fini(http->wr_aio);
- nni_aio_fini(http->rd_aio);
- nni_free(http->rd_buf, http->rd_bufsz);
- nni_mtx_fini(&http->mtx);
- NNI_FREE_STRUCT(http);
-}
-
-static int
-http_init(nni_http **httpp, nni_http_tran *tran, void *data)
-{
- nni_http *http;
- int rv;
-
- if ((http = NNI_ALLOC_STRUCT(http)) == NULL) {
- return (NNG_ENOMEM);
- }
- http->rd_bufsz = HTTP_BUFSIZE;
- if ((http->rd_buf = nni_alloc(http->rd_bufsz)) == NULL) {
- NNI_FREE_STRUCT(http);
- return (NNG_ENOMEM);
- }
- nni_mtx_init(&http->mtx);
- nni_aio_list_init(&http->rdq);
- nni_aio_list_init(&http->wrq);
-
- http->sock = data;
- http->rd_bufsz = HTTP_BUFSIZE;
- http->rd = tran->h_read;
- http->wr = tran->h_write;
- http->close = tran->h_close;
- http->fini = tran->h_fini;
- http->sock_addr = tran->h_sock_addr;
- http->peer_addr = tran->h_peer_addr;
- http->verified = tran->h_verified;
-
- if (((rv = nni_aio_init(&http->wr_aio, http_wr_cb, http)) != 0) ||
- ((rv = nni_aio_init(&http->rd_aio, http_rd_cb, http)) != 0)) {
- nni_http_fini(http);
- return (rv);
- }
-
- *httpp = http;
-
- return (0);
-}
-
-static bool
-nni_http_verified_tcp(void *arg)
-{
- NNI_ARG_UNUSED(arg);
- return (false);
-}
-
-static nni_http_tran http_tcp_ops = {
- .h_read = (void *) nni_plat_tcp_pipe_recv,
- .h_write = (void *) nni_plat_tcp_pipe_send,
- .h_close = (void *) nni_plat_tcp_pipe_close,
- .h_fini = (void *) nni_plat_tcp_pipe_fini,
- .h_sock_addr = (void *) nni_plat_tcp_pipe_sockname,
- .h_peer_addr = (void *) nni_plat_tcp_pipe_peername,
- .h_verified = nni_http_verified_tcp,
-};
-
-int
-nni_http_init_tcp(nni_http **hpp, void *tcp)
-{
- return (http_init(hpp, &http_tcp_ops, tcp));
-}
-
-#ifdef NNG_SUPP_TLS
-static nni_http_tran http_tls_ops = {
- .h_read = (void *) nni_tls_recv,
- .h_write = (void *) nni_tls_send,
- .h_close = (void *) nni_tls_close,
- .h_fini = (void *) nni_tls_fini,
- .h_sock_addr = (void *) nni_tls_sockname,
- .h_peer_addr = (void *) nni_tls_peername,
- .h_verified = (void *) nni_tls_verified,
-};
-
-int
-nni_http_init_tls(nni_http **hpp, nng_tls_config *cfg, void *tcp)
-{
- nni_tls *tls;
- int rv;
-
- if ((rv = nni_tls_init(&tls, cfg, tcp)) != 0) {
- nni_plat_tcp_pipe_fini(tcp);
- return (rv);
- }
-
- return (http_init(hpp, &http_tls_ops, tls));
-}
-#else
-int
-nni_http_init_tls(nni_http **hpp, nng_tls_config *cfg, void *tcp)
-{
- NNI_ARG_UNUSED(hpp);
- NNI_ARG_UNUSED(cfg);
- nni_plat_tcp_pipe_fini(tcp);
- return (NNG_ENOTSUP);
-}
-#endif // NNG_SUPP_TLS \ No newline at end of file
diff --git a/src/supplemental/http/http.h b/src/supplemental/http/http.h
index 93a94049..e44ddc76 100644
--- a/src/supplemental/http/http.h
+++ b/src/supplemental/http/http.h
@@ -11,113 +11,34 @@
#ifndef NNG_SUPPLEMENTAL_HTTP_HTTP_H
#define NNG_SUPPLEMENTAL_HTTP_HTTP_H
-#include <stdbool.h>
-
-typedef struct nni_http_res nni_http_res;
-typedef struct nni_http_entity nni_http_entity;
+#include "core/nng_impl.h"
-typedef struct nni_http_req nni_http_req;
+#include <stdbool.h>
-extern int nni_http_req_init(nni_http_req **);
-extern void nni_http_req_fini(nni_http_req *);
-extern void nni_http_req_reset(nni_http_req *);
-extern int nni_http_req_set_header(nni_http_req *, const char *, const char *);
-extern int nni_http_req_add_header(nni_http_req *, const char *, const char *);
-extern int nni_http_req_del_header(nni_http_req *, const char *);
-extern int nni_http_req_get_buf(nni_http_req *, void **, size_t *);
-extern int nni_http_req_set_method(nni_http_req *, const char *);
-extern int nni_http_req_set_version(nni_http_req *, const char *);
-extern int nni_http_req_set_uri(nni_http_req *, const char *);
-extern const char *nni_http_req_get_header(nni_http_req *, const char *);
-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 *);
+typedef struct nng_http_req nni_http_req;
+typedef struct nng_http_res nni_http_res;
+typedef struct nng_http_conn nni_http_conn;
+typedef struct nng_http_handler nni_http_handler;
+typedef struct nng_http_server nni_http_server;
+
+// These functions are private to the internal framework, and really should
+// not be used elsewhere.
+extern int nni_http_req_init(nni_http_req **);
+extern void nni_http_req_reset(nni_http_req *);
+extern int nni_http_req_get_buf(nni_http_req *, void **, 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 void nni_http_req_get_data(nni_http_req *, void **, size_t *);
-extern int nni_http_res_init(nni_http_res **);
-extern void nni_http_res_fini(nni_http_res *);
-extern void nni_http_res_reset(nni_http_res *);
-extern int nni_http_res_get_buf(nni_http_res *, void **, size_t *);
-extern int nni_http_res_set_header(nni_http_res *, const char *, const char *);
-extern int nni_http_res_add_header(nni_http_res *, const char *, const char *);
-extern int nni_http_res_del_header(nni_http_res *, const char *);
-extern int nni_http_res_set_version(nni_http_res *, const char *);
-extern int nni_http_res_set_status(nni_http_res *, int, const char *);
-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 void nni_http_res_reset(nni_http_res *);
+extern int nni_http_res_get_buf(nni_http_res *, void **, size_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,
- NNI_HTTP_STATUS_SWITCHING = 101,
- NNI_HTTP_STATUS_PROCESSING = 102,
- NNI_HTTP_STATUS_OK = 200,
- NNI_HTTP_STATUS_CREATED = 201,
- NNI_HTTP_STATUS_ACCEPTED = 202,
- NNI_HTTP_STATUS_NOT_AUTHORITATIVE = 203,
- NNI_HTTP_STATUS_NO_CONTENT = 204,
- NNI_HTTP_STATUS_RESET_CONTENT = 205,
- NNI_HTTP_STATUS_PARTIAL_CONTENT = 206,
- NNI_HTTP_STATUS_MULTI_STATUS = 207,
- NNI_HTTP_STATUS_ALREADY_REPORTED = 208,
- NNI_HTTP_STATUS_IM_USED = 226,
- NNI_HTTP_STATUS_MULTIPLE_CHOICES = 300,
- NNI_HTTP_STATUS_STATUS_MOVED_PERMANENTLY = 301,
- NNI_HTTP_STATUS_FOUND = 302,
- NNI_HTTP_STATUS_SEE_OTHER = 303,
- NNI_HTTP_STATUS_NOT_MODIFIED = 304,
- NNI_HTTP_STATUS_USE_PROXY = 305,
- NNI_HTTP_STATUS_TEMPORARY_REDIRECT = 307,
- NNI_HTTP_STATUS_PERMANENT_REDIRECT = 308,
- NNI_HTTP_STATUS_BAD_REQUEST = 400,
- NNI_HTTP_STATUS_UNAUTHORIZED = 401,
- NNI_HTTP_STATUS_PAYMENT_REQUIRED = 402,
- NNI_HTTP_STATUS_FORBIDDEN = 403,
- NNI_HTTP_STATUS_NOT_FOUND = 404,
- NNI_HTTP_STATUS_METHOD_NOT_ALLOWED = 405,
- NNI_HTTP_STATUS_NOT_ACCEPTABLE = 406,
- NNI_HTTP_STATUS_PROXY_AUTH_REQUIRED = 407,
- NNI_HTTP_STATUS_REQUEST_TIMEOUT = 408,
- NNI_HTTP_STATUS_CONFLICT = 409,
- NNI_HTTP_STATUS_GONE = 410,
- NNI_HTTP_STATUS_LENGTH_REQUIRED = 411,
- NNI_HTTP_STATUS_PRECONDITION_FAILED = 412,
- NNI_HTTP_STATUS_PAYLOAD_TOO_LARGE = 413,
- NNI_HTTP_STATUS_URI_TOO_LONG = 414,
- NNI_HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415,
- NNI_HTTP_STATUS_RANGE_NOT_SATISFIABLE = 416,
- NNI_HTTP_STATUS_EXPECTATION_FAILED = 417,
- NNI_HTTP_STATUS_TEAPOT = 418,
- NNI_HTTP_STATUS_UNPROCESSABLE_ENTITY = 422,
- NNI_HTTP_STATUS_LOCKED = 423,
- NNI_HTTP_STATUS_FAILED_DEPENDENCY = 424,
- NNI_HTTP_STATUS_UPGRADE_REQUIRED = 426,
- NNI_HTTP_STATUS_PRECONDITION_REQUIRED = 428,
- NNI_HTTP_STATUS_TOO_MANY_REQUESTS = 429,
- NNI_HTTP_STATUS_HEADERS_TOO_LARGE = 431,
- NNI_HTTP_STATUS_UNAVAIL_LEGAL_REASONS = 451,
- NNI_HTTP_STATUS_INTERNAL_SERVER_ERROR = 500,
- NNI_HTTP_STATUS_NOT_IMPLEMENTED = 501,
- NNI_HTTP_STATUS_BAD_GATEWAY = 502,
- NNI_HTTP_STATUS_SERVICE_UNAVAILABLE = 503,
- NNI_HTTP_STATUS_GATEWAY_TIMEOUT = 504,
- NNI_HTTP_STATUS_HTTP_VERSION_NOT_SUPP = 505,
- NNI_HTTP_STATUS_VARIANT_ALSO_NEGOTIATES = 506,
- NNI_HTTP_STATUS_INSUFFICIENT_STORAGE = 507,
- NNI_HTTP_STATUS_LOOP_DETECTED = 508,
- NNI_HTTP_STATUS_NOT_EXTENDED = 510,
- NNI_HTTP_STATUS_NETWORK_AUTH_REQUIRED = 511,
-};
+// Private to the server. (Used to support session hijacking.)
+extern void nni_http_conn_set_ctx(nni_http_conn *, void *);
+extern void *nni_http_conn_get_ctx(nni_http_conn *);
// An HTTP connection is a connection over which messages are exchanged.
// Generally, clients send request messages, and then read responses.
@@ -132,39 +53,65 @@ enum { NNI_HTTP_STATUS_CONTINUE = 100,
//
// Any error on the connection, including cancellation of a request, is fatal
// the connection.
-typedef struct nni_http nni_http;
// These initialization functions create stream for HTTP transactions.
// They should only be used by the server or client HTTP implementations,
// and are not for use by other code.
-extern int nni_http_init_tcp(nni_http **, void *);
-extern int nni_http_init_tls(nni_http **, nng_tls_config *, void *);
+extern int nni_http_conn_init_tcp(nni_http_conn **, void *);
+extern int nni_http_conn_init_tls(nni_http_conn **, nng_tls_config *, void *);
-extern void nni_http_close(nni_http *);
-extern void nni_http_fini(nni_http *);
+extern void nni_http_conn_close(nni_http_conn *);
+extern void nni_http_conn_fini(nni_http_conn *);
// Reading messages -- the caller must supply a preinitialized (but otherwise
// idle) message. We recommend the caller store this in the aio's user data.
// Note that the iovs of the aio's are clobbered by these methods -- callers
// must not use them for any other purpose.
-extern void nni_http_write_req(nni_http *, nni_http_req *, nni_aio *);
-extern void nni_http_write_res(nni_http *, nni_http_res *, nni_aio *);
-extern void nni_http_read_req(nni_http *, nni_http_req *, nni_aio *);
-extern void nni_http_read_res(nni_http *, nni_http_res *, nni_aio *);
+extern int nni_http_req_alloc(nni_http_req **, const nni_url *);
+extern int nni_http_res_alloc(nni_http_res **);
+extern int nni_http_res_alloc_error(nni_http_res **, uint16_t);
+extern void nni_http_req_free(nni_http_req *);
+extern void nni_http_res_free(nni_http_res *);
+extern void nni_http_write_req(nni_http_conn *, nni_http_req *, nni_aio *);
+extern void nni_http_write_res(nni_http_conn *, nni_http_res *, nni_aio *);
+extern void nni_http_read_req(nni_http_conn *, nni_http_req *, nni_aio *);
+extern void nni_http_read_res(nni_http_conn *, nni_http_res *, nni_aio *);
+
+extern const char *nni_http_req_get_header(nni_http_req *, const char *);
+extern const char *nni_http_res_get_header(nni_http_res *, const char *);
+extern int nni_http_req_add_header(nni_http_req *, const char *, const char *);
+extern int nni_http_res_add_header(nni_http_res *, const char *, const char *);
+extern int nni_http_req_set_header(nni_http_req *, const char *, const char *);
+extern int nni_http_res_set_header(nni_http_res *, const char *, const char *);
+extern int nni_http_req_del_header(nni_http_req *, const char *);
+extern int nni_http_res_del_header(nni_http_res *, const char *);
+extern int nni_http_req_copy_data(nni_http_req *, const void *, size_t);
+extern int nni_http_res_copy_data(nni_http_res *, const void *, size_t);
+extern int nni_http_req_set_data(nni_http_req *, const void *, size_t);
+extern int nni_http_res_set_data(nni_http_res *, const void *, size_t);
+extern const char *nni_http_req_get_method(nni_http_req *);
+extern const char *nni_http_req_get_version(nni_http_req *);
+extern const char *nni_http_req_get_uri(nni_http_req *);
+extern int nni_http_req_set_method(nni_http_req *, const char *);
+extern int nni_http_req_set_version(nni_http_req *, const char *);
+extern int nni_http_req_set_uri(nni_http_req *, const char *);
+extern uint16_t nni_http_res_get_status(nni_http_res *);
+extern int nni_http_res_set_status(nni_http_res *, uint16_t);
+extern const char *nni_http_res_get_version(nni_http_res *);
+extern int nni_http_res_set_version(nni_http_res *, const char *);
+extern const char *nni_http_res_get_reason(nni_http_res *);
+extern int nni_http_res_set_reason(nni_http_res *, const char *);
-extern void nni_http_read(nni_http *, nni_aio *);
-extern void nni_http_read_full(nni_http *, nni_aio *);
-extern void nni_http_write(nni_http *, nni_aio *);
-extern void nni_http_write_full(nni_http *, nni_aio *);
-extern int nni_http_sock_addr(nni_http *, nni_sockaddr *);
-extern int nni_http_peer_addr(nni_http *, nni_sockaddr *);
+extern void nni_http_read(nni_http_conn *, nni_aio *);
+extern void nni_http_read_full(nni_http_conn *, nni_aio *);
+extern void nni_http_write(nni_http_conn *, nni_aio *);
+extern void nni_http_write_full(nni_http_conn *, nni_aio *);
+extern int nni_http_sock_addr(nni_http_conn *, nni_sockaddr *);
+extern int nni_http_peer_addr(nni_http_conn *, nni_sockaddr *);
// nni_tls_http_verified returns true if the peer has been verified using TLS.
-extern bool nni_http_tls_verified(nni_http *);
-
-typedef struct nni_http_server nni_http_server;
-typedef struct nni_http_handler nni_http_handler;
+extern bool nni_http_tls_verified(nni_http_conn *);
// nni_http_server will look for an existing server with the same
// name and port, or create one if one does not exist. The servers
@@ -174,7 +121,7 @@ typedef struct nni_http_handler nni_http_handler;
// 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 **, nni_url *);
+extern int nni_http_server_init(nni_http_server **, const nni_url *);
// nni_http_server_fini drops the reference count on the server, and
// if this was the last reference, closes down the server and frees
@@ -188,8 +135,10 @@ extern void nni_http_server_fini(nni_http_server *);
extern int nni_http_server_add_handler(nni_http_server *, nni_http_handler *);
// nni_http_del_handler removes the given handler. The caller is
-// responsible for finalizing it afterwards.
-extern void nni_http_server_del_handler(nni_http_server *, nni_http_handler *);
+// responsible for finalizing it afterwards. If the handler was not found
+// (not registered), NNG_ENOENT is returned. In this case it is unsafe
+// to make assumptions about the validity of the handler.
+extern int nni_http_server_del_handler(nni_http_server *, nni_http_handler *);
// 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
@@ -212,10 +161,6 @@ extern int nni_http_server_start(nni_http_server *);
// associated with a callback will complete their callback, and then close.
extern void nni_http_server_stop(nni_http_server *);
-// nni_http_ctx is the context associated with a particular request
-// arriving at the server, and is tied to an underlying nni_http channel.
-typedef struct nni_http_ctx nni_http_ctx;
-
// nni_http_hijack is intended to be called by a handler that wishes to
// take over the processing of the HTTP session -- usually to change protocols
// (such as in the case of websocket). The caller is responsible for obtaining
@@ -226,12 +171,7 @@ typedef struct nni_http_ctx nni_http_ctx;
// when a session is hijacked, the caller is also responsible for disposing
// of the request structure. (Some hijackers may keep the request for
// further processing.)
-extern int nni_http_hijack(nni_http_ctx *);
-
-// nni_http_ctx_stream obtains the underlying nni_http channel for the
-// context. This is used by hijackers, as well as anything that needs
-// to handle sending its own replies on the channel.
-extern int nni_http_ctx_stream(nni_http_ctx *, nni_http **);
+extern int nni_http_hijack(nni_http_conn *);
// nni_http_handler_init creates a server handler object, for the supplied
// URI (path only) with the callback.
@@ -241,7 +181,7 @@ extern int nni_http_ctx_stream(nni_http_ctx *, nni_http **);
// once per server.
//
// The callback function will receive the following arguments (via
-// nni_aio_get_input(): nni_http_request *, nni_http_handler *, and
+// nng_aio_get_input(): nni_http_request *, nni_http_handler *, and
// nni_http_context_t *. The first is a request object, for convenience.
// The second is the handler, from which the callback can obtain any other
// data it has set. The final is the http context, from which its possible
@@ -255,12 +195,12 @@ extern int nni_http_handler_init_file(
nni_http_handler **, const char *, const char *);
// nni_http_handler_init_file_ctype is like nni_http_handler_init_file, but
-// provides for settign the Content-Type explicitly (last argument).
+// provides for setting the Content-Type explicitly (last argument).
extern int nni_http_handler_init_file_ctype(
nni_http_handler **, const char *, const char *, const char *);
// nni_http_handler_init_directory arranges to serve up an entire
-// directory tree. The content types are determined from the builtin
+// directory tree. The content types are determined from the built-in
// content type list. Actual directories are required to contain a
// file called index.html or index.htm. We do not generate directory
// listings for security reasons.
@@ -277,17 +217,10 @@ extern int nni_http_handler_init_static(
// calls this for any handlers still registered with it if it is destroyed.
extern void nni_http_handler_fini(nni_http_handler *);
-// nni_http_handler_set_dtor sets a callback that is executed when
-// the handler is torn down. The argument to the destructor is the
-// handler itself. This function is called by the nni_http_handler_fini
-// function.
-extern int nni_http_handler_set_dtor(
- nni_http_handler *, void (*)(nni_http_handler *));
-
// nni_http_handler_set_tree marks the handler as servicing the entire
// tree (e.g. a directory), rather than just a leaf node. The handler
// will probably need to inspect the URL of the request.
-extern int nni_http_handler_set_tree(nni_http_handler *, bool);
+extern int nni_http_handler_set_tree(nni_http_handler *);
// nni_http_handler_set_host limits the handler to only being called for
// the given Host: field. This can be used to set up multiple virtual
@@ -312,14 +245,17 @@ extern int nni_http_handler_set_method(nni_http_handler *, const char *);
// nni_http_handler_set_data sets an opaque data element on the handler,
// which will be available to the callback via nni_http_handler_get_data.
-// Note that indices used should be small, to minimize array allocations.
-// This can fail with NNG_ENOMEM if storage cannot be allocated.
-extern int nni_http_handler_set_data(nni_http_handler *, void *, unsigned);
+// The callback is an optional destructor, and will be called with the
+// data as its argument, when the handler is being destroyed.
+extern int nni_http_handler_set_data(nni_http_handler *, void *, nni_cb);
// nni_http_handler_get_data returns the data that was previously stored
// at that index. It returns NULL if no data was set, or an invalid index
// is supplied.
-extern void *nni_http_handler_get_data(nni_http_handler *, unsigned);
+extern void *nni_http_handler_get_data(nni_http_handler *);
+
+// nni_http_handler_get_uri returns the URI set on the handler.
+extern const char *nni_http_handler_get_uri(nni_http_handler *);
// Client stuff.
diff --git a/src/supplemental/http/client.c b/src/supplemental/http/http_client.c
index 4c54a708..345e5947 100644
--- a/src/supplemental/http/client.c
+++ b/src/supplemental/http/http_client.c
@@ -40,7 +40,7 @@ http_conn_done(void *arg)
nni_aio * aio;
int rv;
nni_plat_tcp_pipe *p;
- nni_http * http;
+ nni_http_conn * conn;
nni_mtx_lock(&c->mtx);
rv = nni_aio_result(c->connaio);
@@ -61,9 +61,9 @@ http_conn_done(void *arg)
}
if (c->tls != NULL) {
- rv = nni_http_init_tls(&http, c->tls, p);
+ rv = nni_http_conn_init_tls(&conn, c->tls, p);
} else {
- rv = nni_http_init_tcp(&http, p);
+ rv = nni_http_conn_init_tcp(&conn, p);
}
if (rv != 0) {
nni_aio_finish_error(aio, rv);
@@ -71,7 +71,7 @@ http_conn_done(void *arg)
return;
}
- nni_aio_set_output(aio, 0, http);
+ nni_aio_set_output(aio, 0, conn);
nni_aio_finish(aio, 0, 0);
if (!nni_list_empty(&c->aios)) {
@@ -124,9 +124,9 @@ nni_http_client_init(nni_http_client **cp, nni_url *url)
if ((rv = nni_aio_init(&aio, NULL, NULL)) != 0) {
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_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);
@@ -221,14 +221,14 @@ nni_http_client_get_tls(nni_http_client *c, nng_tls_config **tlsp)
static void
http_connect_cancel(nni_aio *aio, int rv)
{
- nni_http_client *c = aio->a_prov_data;
+ 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_cancel(c->connaio, rv);
+ nni_aio_abort(c->connaio, rv);
}
nni_mtx_unlock(&c->mtx);
}
@@ -245,4 +245,4 @@ nni_http_client_connect(nni_http_client *c, nni_aio *aio)
http_conn_start(c);
}
nni_mtx_unlock(&c->mtx);
-} \ No newline at end of file
+}
diff --git a/src/supplemental/http/http_conn.c b/src/supplemental/http/http_conn.c
new file mode 100644
index 00000000..484d2242
--- /dev/null
+++ b/src/supplemental/http/http_conn.c
@@ -0,0 +1,765 @@
+//
+// 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 <stdbool.h>
+#include <string.h>
+
+#include "core/nng_impl.h"
+#include "supplemental/tls/tls.h"
+
+#include "http.h"
+
+// We insist that individual headers fit in 8K.
+// If you need more than that, you need something we can't do.
+#define HTTP_BUFSIZE 8192
+
+// types of reads
+enum read_flavor {
+ HTTP_RD_RAW,
+ HTTP_RD_FULL,
+ HTTP_RD_REQ,
+ HTTP_RD_RES,
+};
+
+enum write_flavor {
+ HTTP_WR_RAW,
+ HTTP_WR_FULL,
+ HTTP_WR_REQ,
+ HTTP_WR_RES,
+};
+
+typedef struct nni_http_tran {
+ void (*h_read)(void *, nni_aio *);
+ void (*h_write)(void *, nni_aio *);
+ int (*h_sock_addr)(void *, nni_sockaddr *);
+ int (*h_peer_addr)(void *, nni_sockaddr *);
+ bool (*h_verified)(void *);
+ void (*h_close)(void *);
+ void (*h_fini)(void *);
+} nni_http_tran;
+
+#define SET_RD_FLAVOR(aio, f) \
+ nni_aio_set_prov_extra(aio, 0, ((void *) (intptr_t)(f)))
+#define GET_RD_FLAVOR(aio) (int) ((intptr_t) nni_aio_get_prov_extra(aio, 0))
+#define SET_WR_FLAVOR(aio, f) \
+ nni_aio_set_prov_extra(aio, 0, ((void *) (intptr_t)(f)))
+#define GET_WR_FLAVOR(aio) (int) ((intptr_t) nni_aio_get_prov_extra(aio, 0))
+
+struct nng_http_conn {
+ void *sock;
+ void (*rd)(void *, nni_aio *);
+ void (*wr)(void *, nni_aio *);
+ int (*sock_addr)(void *, nni_sockaddr *);
+ int (*peer_addr)(void *, nni_sockaddr *);
+ bool (*verified)(void *);
+ void (*close)(void *);
+ void (*fini)(void *);
+
+ void *ctx;
+ bool closed;
+
+ nni_list rdq; // high level http read requests
+ nni_list wrq; // high level http write requests
+
+ nni_aio *rd_uaio; // user aio for read
+ nni_aio *wr_uaio; // user aio for write
+ nni_aio *rd_aio; // bottom half read operations
+ nni_aio *wr_aio; // bottom half write operations
+
+ nni_mtx mtx;
+
+ uint8_t *rd_buf;
+ size_t rd_get;
+ size_t rd_put;
+ size_t rd_bufsz;
+};
+
+void
+nni_http_conn_set_ctx(nni_http_conn *conn, void *ctx)
+{
+ conn->ctx = ctx;
+}
+
+void *
+nni_http_conn_get_ctx(nni_http_conn *conn)
+{
+ return (conn->ctx);
+}
+
+static void
+http_close(nni_http_conn *conn)
+{
+ // Call with lock held.
+ nni_aio *aio;
+
+ if (conn->closed) {
+ return;
+ }
+
+ conn->closed = true;
+ if (nni_list_first(&conn->wrq)) {
+ nni_aio_abort(conn->wr_aio, NNG_ECLOSED);
+ // Abort all operations except the one in flight.
+ while ((aio = nni_list_last(&conn->wrq)) !=
+ nni_list_first(&conn->wrq)) {
+ nni_aio_list_remove(aio);
+ nni_aio_finish_error(aio, NNG_ECLOSED);
+ }
+ }
+ if (nni_list_first(&conn->rdq)) {
+ nni_aio_abort(conn->rd_aio, NNG_ECLOSED);
+ while ((aio = nni_list_last(&conn->rdq)) !=
+ nni_list_first(&conn->rdq)) {
+ nni_aio_list_remove(aio);
+ nni_aio_finish_error(aio, NNG_ECLOSED);
+ }
+ }
+
+ if (conn->sock != NULL) {
+ conn->close(conn->sock);
+ }
+}
+
+void
+nni_http_conn_close(nni_http_conn *conn)
+{
+ nni_mtx_lock(&conn->mtx);
+ http_close(conn);
+ nni_mtx_unlock(&conn->mtx);
+}
+
+// http_rd_buf attempts to satisfy the read from data in the buffer.
+static int
+http_rd_buf(nni_http_conn *conn, nni_aio *aio)
+{
+ size_t cnt = conn->rd_put - conn->rd_get;
+ size_t n;
+ uint8_t *rbuf = conn->rd_buf;
+ int rv;
+ bool raw = false;
+ nni_iov *iov;
+ int niov;
+
+ rbuf += conn->rd_get;
+
+ switch (GET_RD_FLAVOR(aio)) {
+ case HTTP_RD_RAW:
+ raw = true; // FALLTHROUGH
+ case HTTP_RD_FULL:
+ nni_aio_get_iov(aio, &niov, &iov);
+ while ((niov != 0) && (cnt != 0)) {
+ // Pull up data from the buffer if possible.
+ n = iov[0].iov_len;
+ if (n > cnt) {
+ n = cnt;
+ }
+ memcpy(iov[0].iov_buf, rbuf, n);
+ iov[0].iov_len -= n;
+ NNI_INCPTR(iov[0].iov_buf, n);
+ conn->rd_get += n;
+ rbuf += n;
+ nni_aio_bump_count(aio, n);
+ cnt -= n;
+
+ if (iov[0].iov_len == 0) {
+ niov--;
+ iov = &iov[1];
+ }
+ }
+
+ nni_aio_set_iov(aio, niov, iov);
+
+ if ((niov == 0) || (raw && (nni_aio_count(aio) != 0))) {
+ // Finished the read. (We are finished if we either
+ // got *all* the data, or we got *some* data for
+ // a raw read.)
+ return (0);
+ }
+
+ // No more data left in the buffer, so use a physio.
+ // (Note that we get here if we either have not completed
+ // a full transaction on a FULL read, or were not even able
+ // to get *any* data for a partial RAW read.)
+ nni_aio_set_data(conn->rd_aio, 1, NULL);
+ nni_aio_set_iov(conn->rd_aio, niov, iov);
+ conn->rd(conn->sock, conn->rd_aio);
+ return (NNG_EAGAIN);
+
+ case HTTP_RD_REQ:
+ rv = nni_http_req_parse(
+ nni_aio_get_prov_extra(aio, 1), rbuf, cnt, &n);
+ conn->rd_get += n;
+ if (conn->rd_get == conn->rd_put) {
+ conn->rd_get = conn->rd_put = 0;
+ }
+ if (rv == NNG_EAGAIN) {
+ nni_iov iov;
+ iov.iov_buf = conn->rd_buf + conn->rd_put;
+ iov.iov_len = conn->rd_bufsz - conn->rd_put;
+ nni_aio_set_iov(conn->rd_aio, 1, &iov);
+ nni_aio_set_data(conn->rd_aio, 1, aio);
+ conn->rd(conn->sock, conn->rd_aio);
+ }
+ return (rv);
+
+ case HTTP_RD_RES:
+ rv = nni_http_res_parse(
+ nni_aio_get_prov_extra(aio, 1), rbuf, cnt, &n);
+ conn->rd_get += n;
+ if (conn->rd_get == conn->rd_put) {
+ conn->rd_get = conn->rd_put = 0;
+ }
+ if (rv == NNG_EAGAIN) {
+ nni_iov iov;
+ iov.iov_buf = conn->rd_buf + conn->rd_put;
+ iov.iov_len = conn->rd_bufsz - conn->rd_put;
+ nni_aio_set_iov(conn->rd_aio, 1, &iov);
+ nni_aio_set_data(conn->rd_aio, 1, aio);
+ conn->rd(conn->sock, conn->rd_aio);
+ }
+ return (rv);
+ }
+ return (NNG_EINVAL);
+}
+
+static void
+http_rd_start(nni_http_conn *conn)
+{
+ for (;;) {
+ nni_aio *aio;
+ int rv;
+
+ if ((aio = conn->rd_uaio) == NULL) {
+ if ((aio = nni_list_first(&conn->rdq)) == NULL) {
+ // No more stuff waiting for read.
+ return;
+ }
+ nni_list_remove(&conn->rdq, aio);
+ conn->rd_uaio = aio;
+ }
+
+ if (conn->closed) {
+ rv = NNG_ECLOSED;
+ } else {
+ rv = http_rd_buf(conn, aio);
+ }
+ switch (rv) {
+ case NNG_EAGAIN:
+ return;
+ case 0:
+ conn->rd_uaio = NULL;
+ nni_aio_finish(aio, 0, nni_aio_count(aio));
+ break;
+ default:
+ conn->rd_uaio = NULL;
+ nni_aio_finish_error(aio, rv);
+ http_close(conn);
+ break;
+ }
+ }
+}
+
+static void
+http_rd_cb(void *arg)
+{
+ nni_http_conn *conn = arg;
+ nni_aio * aio = conn->rd_aio;
+ nni_aio * uaio;
+ size_t cnt;
+ int rv;
+ int niov;
+ nni_iov * iov;
+
+ nni_mtx_lock(&conn->mtx);
+
+ if ((rv = nni_aio_result(aio)) != 0) {
+ if ((uaio = conn->rd_uaio) != NULL) {
+ conn->rd_uaio = NULL;
+ nni_aio_finish_error(uaio, rv);
+ }
+ http_close(conn);
+ nni_mtx_unlock(&conn->mtx);
+ return;
+ }
+
+ cnt = nni_aio_count(aio);
+
+ // If we were reading into the buffer, then advance location(s).
+ if ((uaio = nni_aio_get_data(aio, 1)) != NULL) {
+ conn->rd_put += cnt;
+ NNI_ASSERT(conn->rd_put <= conn->rd_bufsz);
+ http_rd_start(conn);
+ nni_mtx_unlock(&conn->mtx);
+ return;
+ }
+
+ // Otherwise we are completing a USER request, and there should
+ // be no data left in the user buffer.
+ NNI_ASSERT(conn->rd_get == conn->rd_put);
+
+ if ((uaio = conn->rd_uaio) == NULL) {
+ // This indicates that a read request was canceled. This
+ // can occur only when shutting down, really.
+ nni_mtx_unlock(&conn->mtx);
+ return;
+ }
+
+ nni_aio_get_iov(uaio, &niov, &iov);
+
+ while ((niov != 0) && (cnt != 0)) {
+ // Pull up data from the buffer if possible.
+ size_t n = iov[0].iov_len;
+ if (n > cnt) {
+ n = cnt;
+ }
+ iov[0].iov_len -= n;
+ NNI_INCPTR(iov[0].iov_buf, n);
+ nni_aio_bump_count(uaio, n);
+ cnt -= n;
+
+ if (iov[0].iov_len == 0) {
+ niov--;
+ iov = &iov[1];
+ }
+ }
+ nni_aio_set_iov(uaio, niov, iov);
+
+ // Resubmit the start. This will attempt to consume data
+ // from the read buffer (there won't be any), and then either
+ // complete the I/O (for HTTP_RD_RAW, or if there is nothing left),
+ // or submit another physio.
+ http_rd_start(conn);
+ nni_mtx_unlock(&conn->mtx);
+}
+
+static void
+http_rd_cancel(nni_aio *aio, int rv)
+{
+ nni_http_conn *conn = nni_aio_get_prov_data(aio);
+
+ nni_mtx_lock(&conn->mtx);
+ if (aio == conn->rd_uaio) {
+ conn->rd_uaio = NULL;
+ nni_aio_abort(conn->rd_aio, rv);
+ nni_aio_finish_error(aio, rv);
+ } else if (nni_aio_list_active(aio)) {
+ nni_aio_list_remove(aio);
+ nni_aio_finish_error(aio, rv);
+ }
+ nni_mtx_unlock(&conn->mtx);
+}
+
+static void
+http_rd_submit(nni_http_conn *conn, nni_aio *aio)
+{
+ if (nni_aio_start(aio, http_rd_cancel, conn) != 0) {
+ return;
+ }
+ if (conn->closed) {
+ nni_aio_finish_error(aio, NNG_ECLOSED);
+ return;
+ }
+ nni_list_append(&conn->rdq, aio);
+ if (conn->rd_uaio == NULL) {
+ http_rd_start(conn);
+ }
+}
+
+static void
+http_wr_start(nni_http_conn *conn)
+{
+ nni_aio *aio;
+ nni_iov *iov;
+ int niov;
+
+ if ((aio = conn->wr_uaio) == NULL) {
+ if ((aio = nni_list_first(&conn->wrq)) == NULL) {
+ // No more stuff waiting for read.
+ return;
+ }
+ nni_list_remove(&conn->wrq, aio);
+ conn->wr_uaio = aio;
+ }
+
+ nni_aio_get_iov(aio, &niov, &iov);
+ nni_aio_set_iov(conn->wr_aio, niov, iov);
+ conn->wr(conn->sock, conn->wr_aio);
+}
+
+static void
+http_wr_cb(void *arg)
+{
+ nni_http_conn *conn = arg;
+ nni_aio * aio = conn->wr_aio;
+ nni_aio * uaio;
+ int rv;
+ size_t n;
+ int niov;
+ nni_iov * iov;
+
+ nni_mtx_lock(&conn->mtx);
+
+ uaio = conn->wr_uaio;
+
+ if ((rv = nni_aio_result(aio)) != 0) {
+ // We failed to complete the aio.
+ if (uaio != NULL) {
+ conn->wr_uaio = NULL;
+ nni_aio_finish_error(uaio, rv);
+ }
+ http_close(conn);
+ nni_mtx_unlock(&conn->mtx);
+ return;
+ }
+
+ if (uaio == NULL) {
+ // Write canceled? This happens pretty much only during
+ // shutdown/close, so we don't want to resume writing.
+ // The stream is probably corrupted at this point anyway.
+ nni_mtx_unlock(&conn->mtx);
+ return;
+ }
+
+ n = nni_aio_count(aio);
+ nni_aio_bump_count(uaio, n);
+
+ if (GET_WR_FLAVOR(uaio) == HTTP_WR_RAW) {
+ // For raw data, we just send partial completion
+ // notices to the consumer.
+ goto done;
+ }
+ nni_aio_iov_advance(aio, n);
+ if (nni_aio_iov_count(aio) > 0) {
+ // We have more to transmit - start another and leave
+ // (we will get called again when it is done).
+ conn->wr(conn->sock, aio);
+ nni_mtx_unlock(&conn->mtx);
+ return;
+ }
+
+done:
+ conn->wr_uaio = NULL;
+ nni_aio_finish(uaio, 0, nni_aio_count(uaio));
+
+ // Start next write if another is ready.
+ http_wr_start(conn);
+
+ nni_mtx_unlock(&conn->mtx);
+}
+
+static void
+http_wr_cancel(nni_aio *aio, int rv)
+{
+ nni_http_conn *conn = nni_aio_get_prov_data(aio);
+
+ nni_mtx_lock(&conn->mtx);
+ if (aio == conn->wr_uaio) {
+ conn->wr_uaio = NULL;
+ nni_aio_abort(conn->wr_aio, rv);
+ nni_aio_finish_error(aio, rv);
+ } else if (nni_aio_list_active(aio)) {
+ nni_aio_list_remove(aio);
+ nni_aio_finish_error(aio, rv);
+ }
+ nni_mtx_unlock(&conn->mtx);
+}
+
+static void
+http_wr_submit(nni_http_conn *conn, nni_aio *aio)
+{
+ if (nni_aio_start(aio, http_wr_cancel, conn) != 0) {
+ return;
+ }
+ if (conn->closed) {
+ nni_aio_finish_error(aio, NNG_ECLOSED);
+ return;
+ }
+ nni_list_append(&conn->wrq, aio);
+ if (conn->wr_uaio == NULL) {
+ http_wr_start(conn);
+ }
+}
+
+void
+nni_http_read_req(nni_http_conn *conn, nni_http_req *req, nni_aio *aio)
+{
+ SET_RD_FLAVOR(aio, HTTP_RD_REQ);
+ nni_aio_set_prov_extra(aio, 1, req);
+
+ nni_mtx_lock(&conn->mtx);
+ http_rd_submit(conn, aio);
+ nni_mtx_unlock(&conn->mtx);
+}
+
+void
+nni_http_read_res(nni_http_conn *conn, nni_http_res *res, nni_aio *aio)
+{
+ SET_RD_FLAVOR(aio, HTTP_RD_RES);
+ nni_aio_set_prov_extra(aio, 1, res);
+
+ nni_mtx_lock(&conn->mtx);
+ http_rd_submit(conn, aio);
+ nni_mtx_unlock(&conn->mtx);
+}
+
+void
+nni_http_read_full(nni_http_conn *conn, nni_aio *aio)
+{
+ SET_RD_FLAVOR(aio, HTTP_RD_FULL);
+ nni_aio_set_prov_extra(aio, 1, NULL);
+
+ nni_mtx_lock(&conn->mtx);
+ http_rd_submit(conn, aio);
+ nni_mtx_unlock(&conn->mtx);
+}
+
+void
+nni_http_read(nni_http_conn *conn, nni_aio *aio)
+{
+ SET_RD_FLAVOR(aio, HTTP_RD_RAW);
+ nni_aio_set_prov_extra(aio, 1, NULL);
+
+ nni_mtx_lock(&conn->mtx);
+ http_rd_submit(conn, aio);
+ nni_mtx_unlock(&conn->mtx);
+}
+
+void
+nni_http_write_req(nni_http_conn *conn, nni_http_req *req, nni_aio *aio)
+{
+ int rv;
+ void * buf;
+ size_t bufsz;
+ void * data;
+ size_t size;
+ nni_iov iov[2];
+ int niov;
+
+ if ((rv = nni_http_req_get_buf(req, &buf, &bufsz)) != 0) {
+ nni_aio_finish_error(aio, rv);
+ return;
+ }
+ nni_http_req_get_data(req, &data, &size);
+ niov = 1;
+ iov[0].iov_len = bufsz;
+ iov[0].iov_buf = buf;
+ if ((size > 0) && (data != NULL)) {
+ niov++;
+ iov[1].iov_len = size;
+ iov[1].iov_buf = data;
+ }
+ nni_aio_set_iov(aio, niov, iov);
+
+ SET_WR_FLAVOR(aio, HTTP_WR_REQ);
+
+ nni_mtx_lock(&conn->mtx);
+ http_wr_submit(conn, aio);
+ nni_mtx_unlock(&conn->mtx);
+}
+
+void
+nni_http_write_res(nni_http_conn *conn, nni_http_res *res, nni_aio *aio)
+{
+ int rv;
+ void * buf;
+ size_t bufsz;
+ void * data;
+ size_t size;
+ nni_iov iov[2];
+ int niov;
+
+ if ((rv = nni_http_res_get_buf(res, &buf, &bufsz)) != 0) {
+ nni_aio_finish_error(aio, rv);
+ return;
+ }
+ nni_http_res_get_data(res, &data, &size);
+ niov = 1;
+ iov[0].iov_len = bufsz;
+ iov[0].iov_buf = buf;
+ if ((size > 0) && (data != NULL)) {
+ niov++;
+ iov[1].iov_len = size;
+ iov[1].iov_buf = data;
+ }
+ nni_aio_set_iov(aio, niov, iov);
+
+ SET_WR_FLAVOR(aio, HTTP_WR_RES);
+
+ nni_mtx_lock(&conn->mtx);
+ http_wr_submit(conn, aio);
+ nni_mtx_unlock(&conn->mtx);
+}
+
+void
+nni_http_write(nni_http_conn *conn, nni_aio *aio)
+{
+ SET_WR_FLAVOR(aio, HTTP_WR_RAW);
+
+ nni_mtx_lock(&conn->mtx);
+ http_wr_submit(conn, aio);
+ nni_mtx_unlock(&conn->mtx);
+}
+
+void
+nni_http_write_full(nni_http_conn *conn, nni_aio *aio)
+{
+ SET_WR_FLAVOR(aio, HTTP_WR_FULL);
+
+ nni_mtx_lock(&conn->mtx);
+ http_wr_submit(conn, aio);
+ nni_mtx_unlock(&conn->mtx);
+}
+
+int
+nni_http_sock_addr(nni_http_conn *conn, nni_sockaddr *sa)
+{
+ int rv;
+ nni_mtx_lock(&conn->mtx);
+ rv = conn->closed ? NNG_ECLOSED : conn->sock_addr(conn->sock, sa);
+ nni_mtx_unlock(&conn->mtx);
+ return (rv);
+}
+
+int
+nni_http_peer_addr(nni_http_conn *conn, nni_sockaddr *sa)
+{
+ int rv;
+ nni_mtx_lock(&conn->mtx);
+ rv = conn->closed ? NNG_ECLOSED : conn->peer_addr(conn->sock, sa);
+ nni_mtx_unlock(&conn->mtx);
+ return (rv);
+}
+
+bool
+nni_http_tls_verified(nni_http_conn *conn)
+{
+ bool rv;
+
+ nni_mtx_lock(&conn->mtx);
+ rv = conn->closed ? false : conn->verified(conn->sock);
+ nni_mtx_unlock(&conn->mtx);
+ return (rv);
+}
+
+void
+nni_http_conn_fini(nni_http_conn *conn)
+{
+ nni_mtx_lock(&conn->mtx);
+ http_close(conn);
+ if ((conn->sock != NULL) && (conn->fini != NULL)) {
+ conn->fini(conn->sock);
+ conn->sock = NULL;
+ }
+ nni_mtx_unlock(&conn->mtx);
+ nni_aio_stop(conn->wr_aio);
+ nni_aio_stop(conn->rd_aio);
+ nni_aio_fini(conn->wr_aio);
+ nni_aio_fini(conn->rd_aio);
+ nni_free(conn->rd_buf, conn->rd_bufsz);
+ nni_mtx_fini(&conn->mtx);
+ NNI_FREE_STRUCT(conn);
+}
+
+static int
+http_init(nni_http_conn **connp, nni_http_tran *tran, void *data)
+{
+ nni_http_conn *conn;
+ int rv;
+
+ if ((conn = NNI_ALLOC_STRUCT(conn)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ conn->rd_bufsz = HTTP_BUFSIZE;
+ if ((conn->rd_buf = nni_alloc(conn->rd_bufsz)) == NULL) {
+ NNI_FREE_STRUCT(conn);
+ return (NNG_ENOMEM);
+ }
+ nni_mtx_init(&conn->mtx);
+ nni_aio_list_init(&conn->rdq);
+ nni_aio_list_init(&conn->wrq);
+
+ conn->sock = data;
+ conn->rd_bufsz = HTTP_BUFSIZE;
+ conn->rd = tran->h_read;
+ conn->wr = tran->h_write;
+ conn->close = tran->h_close;
+ conn->fini = tran->h_fini;
+ conn->sock_addr = tran->h_sock_addr;
+ conn->peer_addr = tran->h_peer_addr;
+ conn->verified = tran->h_verified;
+
+ if (((rv = nni_aio_init(&conn->wr_aio, http_wr_cb, conn)) != 0) ||
+ ((rv = nni_aio_init(&conn->rd_aio, http_rd_cb, conn)) != 0)) {
+ nni_http_conn_fini(conn);
+ return (rv);
+ }
+
+ *connp = conn;
+
+ return (0);
+}
+
+static bool
+nni_http_verified_tcp(void *arg)
+{
+ NNI_ARG_UNUSED(arg);
+ return (false);
+}
+
+static nni_http_tran http_tcp_ops = {
+ .h_read = (void *) nni_plat_tcp_pipe_recv,
+ .h_write = (void *) nni_plat_tcp_pipe_send,
+ .h_close = (void *) nni_plat_tcp_pipe_close,
+ .h_fini = (void *) nni_plat_tcp_pipe_fini,
+ .h_sock_addr = (void *) nni_plat_tcp_pipe_sockname,
+ .h_peer_addr = (void *) nni_plat_tcp_pipe_peername,
+ .h_verified = nni_http_verified_tcp,
+};
+
+int
+nni_http_conn_init_tcp(nni_http_conn **connp, void *tcp)
+{
+ return (http_init(connp, &http_tcp_ops, tcp));
+}
+
+#ifdef NNG_SUPP_TLS
+static nni_http_tran http_tls_ops = {
+ .h_read = (void *) nni_tls_recv,
+ .h_write = (void *) nni_tls_send,
+ .h_close = (void *) nni_tls_close,
+ .h_fini = (void *) nni_tls_fini,
+ .h_sock_addr = (void *) nni_tls_sockname,
+ .h_peer_addr = (void *) nni_tls_peername,
+ .h_verified = (void *) nni_tls_verified,
+};
+
+int
+nni_http_conn_init_tls(nni_http_conn **connp, nng_tls_config *cfg, void *tcp)
+{
+ nni_tls *tls;
+ int rv;
+
+ if ((rv = nni_tls_init(&tls, cfg, tcp)) != 0) {
+ nni_plat_tcp_pipe_fini(tcp);
+ return (rv);
+ }
+
+ return (http_init(connp, &http_tls_ops, tls));
+}
+#else
+int
+nni_http_conn_init_tls(nni_http_conn **connp, nng_tls_config *cfg, void *tcp)
+{
+ NNI_ARG_UNUSED(connp);
+ NNI_ARG_UNUSED(cfg);
+ nni_plat_tcp_pipe_fini(tcp);
+ return (NNG_ENOTSUP);
+}
+#endif // NNG_SUPP_TLS \ No newline at end of file
diff --git a/src/supplemental/http/http_msg.c b/src/supplemental/http/http_msg.c
index da8c70f3..81c1b453 100644
--- a/src/supplemental/http/http_msg.c
+++ b/src/supplemental/http/http_msg.c
@@ -27,14 +27,14 @@ typedef struct http_header {
nni_list_node node;
} http_header;
-struct nni_http_entity {
+typedef struct nni_http_entity {
char * data;
size_t size; // allocated/expected size
size_t len; // current length
bool own; // if true, data is "ours", and should be freed
-};
+} nni_http_entity;
-struct nni_http_req {
+struct nng_http_req {
nni_list hdrs;
nni_http_entity data;
char * meth;
@@ -42,23 +42,27 @@ struct nni_http_req {
char * vers;
char * buf;
size_t bufsz;
+ bool parsed;
};
-struct nni_http_res {
+struct nng_http_res {
nni_list hdrs;
nni_http_entity data;
- int code;
+ uint16_t code;
char * rsn;
char * vers;
char * buf;
size_t bufsz;
+ bool parsed;
};
static int
http_set_string(char **strp, const char *val)
{
char *news;
- if ((news = nni_strdup(val)) == NULL) {
+ if (val == NULL) {
+ news = NULL;
+ } else if ((news = nni_strdup(val)) == NULL) {
return (NNG_ENOMEM);
}
nni_strfree(*strp);
@@ -114,6 +118,8 @@ nni_http_res_reset(nni_http_res *res)
http_entity_reset(&res->data);
nni_strfree(res->rsn);
nni_strfree(res->vers);
+ res->vers = NULL;
+ res->rsn = NULL;
res->code = 0;
if (res->bufsz) {
res->buf[0] = '\0';
@@ -121,7 +127,7 @@ nni_http_res_reset(nni_http_res *res)
}
void
-nni_http_req_fini(nni_http_req *req)
+nni_http_req_free(nni_http_req *req)
{
nni_http_req_reset(req);
if (req->bufsz) {
@@ -131,7 +137,7 @@ nni_http_req_fini(nni_http_req *req)
}
void
-nni_http_res_fini(nni_http_res *res)
+nni_http_res_free(nni_http_res *res)
{
nni_http_res_reset(res);
if (res->bufsz) {
@@ -157,13 +163,13 @@ http_del_header(nni_list *hdrs, const char *key)
}
int
-nni_req_del_header(nni_http_req *req, const char *key)
+nni_http_req_del_header(nni_http_req *req, const char *key)
{
return (http_del_header(&req->hdrs, key));
}
int
-nni_res_del_header(nni_http_res *res, const char *key)
+nni_http_res_del_header(nni_http_res *res, const char *key)
{
return (http_del_header(&res->hdrs, key));
}
@@ -398,12 +404,6 @@ nni_http_res_copy_data(nni_http_res *res, const void *data, size_t size)
return (0);
}
-int
-nni_http_res_alloc_data(nni_http_res *res, size_t size)
-{
- return (http_entity_alloc_data(&res->data, size));
-}
-
static int
http_parse_header(nni_list *hdrs, void *line)
{
@@ -501,11 +501,12 @@ static int
http_req_prepare(nni_http_req *req)
{
int rv;
- if ((req->uri == NULL) || (req->meth == NULL)) {
+ if (req->uri == NULL) {
return (NNG_EINVAL);
}
rv = http_asprintf(&req->buf, &req->bufsz, &req->hdrs, "%s %s %s\r\n",
- req->meth, req->uri, req->vers != NULL ? req->vers : "HTTP/1.1");
+ req->meth != NULL ? req->meth : "GET", req->uri,
+ req->vers != NULL ? req->vers : "HTTP/1.1");
return (rv);
}
@@ -514,8 +515,8 @@ http_res_prepare(nni_http_res *res)
{
int rv;
rv = http_asprintf(&res->buf, &res->bufsz, &res->hdrs, "%s %d %s\r\n",
- res->vers != NULL ? res->vers : "HTTP/1.1", res->code,
- res->rsn != NULL ? res->rsn : "Unknown Error");
+ nni_http_res_get_version(res), nni_http_res_get_status(res),
+ nni_http_res_get_reason(res));
return (rv);
}
@@ -572,7 +573,7 @@ nni_http_res_get_buf(nni_http_res *res, void **data, size_t *szp)
}
int
-nni_http_req_init(nni_http_req **reqp)
+nni_http_req_alloc(nni_http_req **reqp, const nni_url *url)
{
nni_http_req *req;
if ((req = NNI_ALLOC_STRUCT(req)) == NULL) {
@@ -587,12 +588,33 @@ nni_http_req_init(nni_http_req **reqp)
req->vers = NULL;
req->meth = NULL;
req->uri = NULL;
- *reqp = req;
+ if (url != NULL) {
+ const char *host;
+ int rv;
+ if ((req->uri = nni_strdup(url->u_rawpath)) == NULL) {
+ NNI_FREE_STRUCT(req);
+ return (NNG_ENOMEM);
+ }
+
+ // Add a Host: header since we know that from the URL. Also,
+ // only include the :port portion if it isn't the default port.
+ if (strcmp(nni_url_default_port(url->u_scheme), url->u_port) ==
+ 0) {
+ host = url->u_hostname;
+ } else {
+ host = url->u_host;
+ }
+ if ((rv = nni_http_req_add_header(req, "Host", host)) != 0) {
+ nni_http_req_free(req);
+ return (rv);
+ }
+ }
+ *reqp = req;
return (0);
}
int
-nni_http_res_init(nni_http_res **resp)
+nni_http_res_alloc(nni_http_res **resp)
{
nni_http_res *res;
if ((res = NNI_ALLOC_STRUCT(res)) == NULL) {
@@ -614,7 +636,7 @@ nni_http_res_init(nni_http_res **resp)
const char *
nni_http_req_get_method(nni_http_req *req)
{
- return (req->meth != NULL ? req->meth : "");
+ return (req->meth != NULL ? req->meth : "GET");
}
const char *
@@ -626,24 +648,30 @@ nni_http_req_get_uri(nni_http_req *req)
const char *
nni_http_req_get_version(nni_http_req *req)
{
- return (req->vers != NULL ? req->vers : "");
+ return (req->vers != NULL ? req->vers : "HTTP/1.1");
}
const char *
nni_http_res_get_version(nni_http_res *res)
{
- return (res->vers != NULL ? res->vers : "");
+ return (res->vers != NULL ? res->vers : "HTTP/1.1");
}
int
nni_http_req_set_version(nni_http_req *req, const char *vers)
{
+ if (strcmp(vers, "HTTP/1.1") == 0) {
+ vers = NULL;
+ }
return (http_set_string(&req->vers, vers));
}
int
nni_http_res_set_version(nni_http_res *res, const char *vers)
{
+ if (strcmp(vers, "HTTP/1.1") == 0) {
+ vers = NULL;
+ }
return (http_set_string(&res->vers, vers));
}
@@ -656,31 +684,25 @@ nni_http_req_set_uri(nni_http_req *req, const char *uri)
int
nni_http_req_set_method(nni_http_req *req, const char *meth)
{
+ if (strcmp(meth, "GET") == 0) {
+ meth = NULL;
+ }
return (http_set_string(&req->meth, meth));
}
int
-nni_http_res_set_status(nni_http_res *res, int status, const char *reason)
+nni_http_res_set_status(nni_http_res *res, uint16_t status)
{
- int rv;
- if ((rv = http_set_string(&res->rsn, reason)) == 0) {
- res->code = status;
- }
- return (rv);
+ res->code = status;
+ return (0);
}
-int
+uint16_t
nni_http_res_get_status(nni_http_res *res)
{
return (res->code);
}
-const char *
-nni_http_res_get_reason(nni_http_res *res)
-{
- return (res->rsn != NULL ? res->rsn : "");
-}
-
static int
http_scan_line(void *vbuf, size_t n, size_t *lenp)
{
@@ -740,6 +762,7 @@ http_req_parse_line(nni_http_req *req, void *line)
((rv = nni_http_req_set_version(req, version)) != 0)) {
return (rv);
}
+ req->parsed = true;
return (0);
}
@@ -770,10 +793,12 @@ http_res_parse_line(nni_http_res *res, uint8_t *line)
return (NNG_EPROTO);
}
- if (((rv = nni_http_res_set_status(res, status, reason)) != 0) ||
- ((rv = nni_http_res_set_version(res, version)) != 0)) {
+ if (((rv = nni_http_res_set_status(res, (uint16_t) status)) != 0) ||
+ ((rv = nni_http_res_set_version(res, version)) != 0) ||
+ ((rv = nni_http_res_set_reason(res, reason)) != 0)) {
return (rv);
}
+ res->parsed = true;
return (0);
}
@@ -806,7 +831,7 @@ nni_http_req_parse(nni_http_req *req, void *buf, size_t n, size_t *lenp)
break;
}
- if (req->vers != NULL) {
+ if (req->parsed) {
rv = http_parse_header(&req->hdrs, line);
} else {
rv = http_req_parse_line(req, line);
@@ -843,7 +868,7 @@ nni_http_res_parse(nni_http_res *res, void *buf, size_t n, size_t *lenp)
break;
}
- if (res->vers != NULL) {
+ if (res->parsed) {
rv = http_parse_header(&res->hdrs, line);
} else {
rv = http_res_parse_line(res, line);
@@ -858,105 +883,121 @@ nni_http_res_parse(nni_http_res *res, void *buf, size_t n, size_t *lenp)
return (rv);
}
+static struct {
+ uint16_t code;
+ const char *mesg;
+} http_status[] = {
+ // 200, listed first because most likely
+ { NNG_HTTP_STATUS_OK, "OK" },
+
+ // 100 series -- informational
+ { NNG_HTTP_STATUS_CONTINUE, "Continue" },
+ { NNG_HTTP_STATUS_SWITCHING, "Swithching Protocols" },
+ { NNG_HTTP_STATUS_PROCESSING, "Processing" },
+
+ // 200 series -- successful
+ { NNG_HTTP_STATUS_CREATED, "Created" },
+ { NNG_HTTP_STATUS_ACCEPTED, "Accepted" },
+ { NNG_HTTP_STATUS_NOT_AUTHORITATIVE, "Not Authoritative" },
+ { NNG_HTTP_STATUS_NO_CONTENT, "No Content" },
+ { NNG_HTTP_STATUS_RESET_CONTENT, "Reset Content" },
+ { NNG_HTTP_STATUS_PARTIAL_CONTENT, "Partial Content" },
+
+ // 300 series -- redirection
+ { NNG_HTTP_STATUS_MULTIPLE_CHOICES, "Multiple Choices" },
+ { NNG_HTTP_STATUS_STATUS_MOVED_PERMANENTLY, "Moved Permanently" },
+ { NNG_HTTP_STATUS_FOUND, "Found" },
+ { NNG_HTTP_STATUS_SEE_OTHER, "See Other" },
+ { NNG_HTTP_STATUS_NOT_MODIFIED, "Not Modified" },
+ { NNG_HTTP_STATUS_USE_PROXY, "Use Proxy" },
+ { NNG_HTTP_STATUS_TEMPORARY_REDIRECT, "Temporary Redirect" },
+
+ // 400 series -- client errors
+ { NNG_HTTP_STATUS_BAD_REQUEST, "Bad Request" },
+ { NNG_HTTP_STATUS_UNAUTHORIZED, "Unauthorized" },
+ { NNG_HTTP_STATUS_PAYMENT_REQUIRED, "Payment Required" },
+ { NNG_HTTP_STATUS_FORBIDDEN, "Forbidden" },
+ { NNG_HTTP_STATUS_NOT_FOUND, "Not Found" },
+ { NNG_HTTP_STATUS_METHOD_NOT_ALLOWED, "Method Not Allowed" },
+ { NNG_HTTP_STATUS_NOT_ACCEPTABLE, "Not Acceptable" },
+ { NNG_HTTP_STATUS_PROXY_AUTH_REQUIRED,
+ "Proxy Authentication Required" },
+ { NNG_HTTP_STATUS_REQUEST_TIMEOUT, "Request Timeout" },
+ { NNG_HTTP_STATUS_CONFLICT, "Conflict" },
+ { NNG_HTTP_STATUS_GONE, "Gone" },
+ { NNG_HTTP_STATUS_LENGTH_REQUIRED, "Length Required" },
+ { NNG_HTTP_STATUS_PRECONDITION_FAILED, "Precondition Failed" },
+ { NNG_HTTP_STATUS_ENTITY_TOO_LONG, "Request Entity Too Long" },
+ { NNG_HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type" },
+ { NNG_HTTP_STATUS_RANGE_NOT_SATISFIABLE,
+ "Requested Range Not Satisfiable" },
+ { NNG_HTTP_STATUS_EXPECTATION_FAILED, "Expectation Failed" },
+ { NNG_HTTP_STATUS_TEAPOT, "I Am A Teapot" },
+ { NNG_HTTP_STATUS_LOCKED, "Locked" },
+ { NNG_HTTP_STATUS_FAILED_DEPENDENCY, "Failed Dependency" },
+ { NNG_HTTP_STATUS_UPGRADE_REQUIRED, "Upgrade Required" },
+ { NNG_HTTP_STATUS_PRECONDITION_REQUIRED, "Precondition Required" },
+ { NNG_HTTP_STATUS_TOO_MANY_REQUESTS, "Too Many Requests" },
+ { NNG_HTTP_STATUS_HEADERS_TOO_LARGE, "Headers Too Large" },
+ { NNG_HTTP_STATUS_UNAVAIL_LEGAL_REASONS,
+ "Unavailable For Legal Reasons" },
+
+ // 500 series -- server errors
+ { NNG_HTTP_STATUS_INTERNAL_SERVER_ERROR, "Internal Server Error" },
+ { NNG_HTTP_STATUS_NOT_IMPLEMENTED, "Not Implemented" },
+ { NNG_HTTP_STATUS_BAD_REQUEST, "Bad Gateway" },
+ { NNG_HTTP_STATUS_SERVICE_UNAVAILABLE, "Service Unavailable" },
+ { NNG_HTTP_STATUS_GATEWAY_TIMEOUT, "Gateway Timeout" },
+ { NNG_HTTP_STATUS_HTTP_VERSION_NOT_SUPP,
+ "HTTP Version Not Supported" },
+ { NNG_HTTP_STATUS_VARIANT_ALSO_NEGOTIATES, "Variant Also Negotiates" },
+ { NNG_HTTP_STATUS_INSUFFICIENT_STORAGE, "Insufficient Storage" },
+ { NNG_HTTP_STATUS_LOOP_DETECTED, "Loop Detected" },
+ { NNG_HTTP_STATUS_NOT_EXTENDED, "Not Extended" },
+ { NNG_HTTP_STATUS_NETWORK_AUTH_REQUIRED,
+ "Network Authentication Required" },
+
+ // Terminator
+ { 0, NULL },
+};
+
+const char *
+http_reason(uint16_t code)
+{
+ for (int i = 0; http_status[i].code != 0; i++) {
+ if (http_status[i].code == code) {
+ return (http_status[i].mesg);
+ }
+ }
+ return ("Unknown HTTP Status");
+}
+
+const char *
+nni_http_res_get_reason(nni_http_res *res)
+{
+ return (res->rsn ? res->rsn : http_reason(res->code));
+}
+
int
-nni_http_res_init_error(nni_http_res **resp, uint16_t err)
+nni_http_res_set_reason(nni_http_res *res, const char *reason)
{
- char * rsn;
- char rsnbuf[80];
- char html[1024];
+ if (strcmp(reason, http_reason(res->code)) == 0) {
+ reason = NULL;
+ }
+ return (http_set_string(&res->rsn, reason));
+}
+
+int
+nni_http_res_alloc_error(nni_http_res **resp, uint16_t err)
+{
+ char html[512];
int rv;
nni_http_res *res;
- if ((rv = nni_http_res_init(&res)) != 0) {
+ if ((rv = nni_http_res_alloc(&res)) != 0) {
return (rv);
}
- // Note that it is expected that redirect URIs will update the
- // payload to reflect the target location.
- switch (err) {
- case NNI_HTTP_STATUS_STATUS_MOVED_PERMANENTLY:
- rsn = "Moved Permanently";
- break;
- case NNI_HTTP_STATUS_MULTIPLE_CHOICES:
- rsn = "Multiple Choices";
- break;
- case NNI_HTTP_STATUS_FOUND:
- rsn = "Found";
- break;
- case NNI_HTTP_STATUS_SEE_OTHER:
- rsn = "See Other";
- break;
- case NNI_HTTP_STATUS_TEMPORARY_REDIRECT:
- rsn = "Temporary Redirect";
- break;
- case NNI_HTTP_STATUS_BAD_REQUEST:
- rsn = "Bad Request";
- break;
- case NNI_HTTP_STATUS_UNAUTHORIZED:
- rsn = "Unauthorized";
- break;
- case NNI_HTTP_STATUS_PAYMENT_REQUIRED:
- rsn = "Payment Required";
- break;
- case NNI_HTTP_STATUS_NOT_FOUND:
- rsn = "Not Found";
- break;
- case NNI_HTTP_STATUS_METHOD_NOT_ALLOWED:
- // Caller must also supply an Allow: header
- rsn = "Method Not Allowed";
- break;
- case NNI_HTTP_STATUS_NOT_ACCEPTABLE:
- rsn = "Not Acceptable";
- break;
- case NNI_HTTP_STATUS_REQUEST_TIMEOUT:
- rsn = "Request Timeout";
- break;
- case NNI_HTTP_STATUS_CONFLICT:
- rsn = "Conflict";
- break;
- case NNI_HTTP_STATUS_GONE:
- rsn = "Gone";
- break;
- case NNI_HTTP_STATUS_LENGTH_REQUIRED:
- rsn = "Length Required";
- break;
- case NNI_HTTP_STATUS_PAYLOAD_TOO_LARGE:
- rsn = "Payload Too Large";
- break;
- case NNI_HTTP_STATUS_FORBIDDEN:
- rsn = "Forbidden";
- break;
- case NNI_HTTP_STATUS_URI_TOO_LONG:
- rsn = "URI Too Long";
- break;
- case NNI_HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE:
- rsn = "Unsupported Media Type";
- break;
- case NNI_HTTP_STATUS_EXPECTATION_FAILED:
- rsn = "Expectation Failed";
- break;
- case NNI_HTTP_STATUS_UPGRADE_REQUIRED:
- // Caller must add "Upgrade:" header.
- rsn = "Upgrade Required";
- break;
- case NNI_HTTP_STATUS_INTERNAL_SERVER_ERROR:
- rsn = "Internal Server Error";
- break;
- case NNI_HTTP_STATUS_HTTP_VERSION_NOT_SUPP:
- rsn = "HTTP version not supported";
- break;
- case NNI_HTTP_STATUS_NOT_IMPLEMENTED:
- rsn = "Not Implemented";
- break;
- case NNI_HTTP_STATUS_SERVICE_UNAVAILABLE:
- rsn = "Service Unavailable";
- break;
- default:
- snprintf(rsnbuf, sizeof(rsnbuf), "HTTP error code %d", err);
- rsn = rsnbuf;
- break;
- }
-
// very simple builtin error page
(void) snprintf(html, sizeof(html),
"<head><title>%d %s</title></head>"
@@ -967,14 +1008,13 @@ nni_http_res_init_error(nni_http_res **resp, uint16_t err)
"<p align=\"center\">"
"<span style=\"font-size: 24px; font-family: Arial, sans serif;\">"
"%s</span></p></body>",
- err, rsn, err, rsn);
+ err, http_reason(err), err, http_reason(err));
- if (((rv = nni_http_res_set_status(res, err, rsn)) != 0) ||
- ((rv = nni_http_res_set_version(res, "HTTP/1.1")) != 0) ||
- ((rv = nni_http_res_set_header(
+ res->code = err;
+ if (((rv = nni_http_res_set_header(
res, "Content-Type", "text/html; charset=UTF-8")) != 0) ||
((rv = nni_http_res_copy_data(res, html, strlen(html))) != 0)) {
- nni_http_res_fini(res);
+ nni_http_res_free(res);
} else {
*resp = res;
}
diff --git a/src/supplemental/http/http_public.c b/src/supplemental/http/http_public.c
new file mode 100644
index 00000000..f19b83b1
--- /dev/null
+++ b/src/supplemental/http/http_public.c
@@ -0,0 +1,668 @@
+//
+// 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 "core/nng_impl.h"
+#include "http.h"
+
+// Symbols in this file are "public" versions of the HTTP API.
+// These are suitable for exposure to applications.
+
+int
+nng_http_req_alloc(nng_http_req **reqp, const nng_url *url)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_req_alloc(reqp, url));
+#else
+ NNI_ARG_UNUSED(reqp);
+ NNI_ARG_UNUSED(url);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+void
+nng_http_req_free(nng_http_req *req)
+{
+#ifdef NNG_SUPP_HTTP
+ nni_http_req_free(req);
+#else
+ NNI_ARG_UNUSED(req);
+#endif
+}
+
+void
+nng_http_res_free(nng_http_res *res)
+{
+#ifdef NNG_SUPP_HTTP
+ nni_http_res_free(res);
+#else
+ NNI_ARG_UNUSED(res);
+#endif
+}
+
+int
+nng_http_res_alloc(nng_http_res **resp)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_res_alloc(resp));
+#else
+ NNI_ARG_UNUSED(resp);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
+nng_http_res_alloc_error(nng_http_res **resp, uint16_t code)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_res_alloc_error(resp, code));
+#else
+ NNI_ARG_UNUSED(resp);
+ NNI_ARG_UNUSED(code);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+const char *
+nng_http_req_get_header(nng_http_req *req, const char *key)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_req_get_header(req, key));
+#else
+ NNI_ARG_UNUSED(req);
+ NNI_ARG_UNUSED(key);
+ return (NULL);
+#endif
+}
+
+const char *
+nng_http_res_get_header(nng_http_res *res, const char *key)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_res_get_header(res, key));
+#else
+ NNI_ARG_UNUSED(res);
+ NNI_ARG_UNUSED(key);
+ return (NULL);
+#endif
+}
+
+int
+nng_http_req_add_header(nng_http_req *req, const char *key, const char *val)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_req_add_header(req, key, val));
+#else
+ NNI_ARG_UNUSED(req);
+ NNI_ARG_UNUSED(key);
+ NNI_ARG_UNUSED(val);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
+nng_http_res_add_header(nng_http_res *res, const char *key, const char *val)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_res_add_header(res, key, val));
+#else
+ NNI_ARG_UNUSED(res);
+ NNI_ARG_UNUSED(key);
+ NNI_ARG_UNUSED(val);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
+nng_http_req_set_header(nng_http_req *req, const char *key, const char *val)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_req_set_header(req, key, val));
+#else
+ NNI_ARG_UNUSED(req);
+ NNI_ARG_UNUSED(key);
+ NNI_ARG_UNUSED(val);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
+nng_http_res_set_header(nng_http_res *res, const char *key, const char *val)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_res_set_header(res, key, val));
+#else
+ NNI_ARG_UNUSED(res);
+ NNI_ARG_UNUSED(key);
+ NNI_ARG_UNUSED(val);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
+nng_http_req_del_header(nng_http_req *req, const char *key)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_req_del_header(req, key));
+#else
+ NNI_ARG_UNUSED(req);
+ NNI_ARG_UNUSED(key);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
+nng_http_res_del_header(nng_http_res *res, const char *key)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_res_del_header(res, key));
+#else
+ NNI_ARG_UNUSED(res);
+ NNI_ARG_UNUSED(key);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
+nng_http_req_copy_data(nng_http_req *req, const void *data, size_t sz)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_req_copy_data(req, data, sz));
+#else
+ NNI_ARG_UNUSED(req);
+ NNI_ARG_UNUSED(data);
+ NNI_ARG_UNUSED(sz);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
+nng_http_res_copy_data(nng_http_res *res, const void *data, size_t sz)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_res_copy_data(res, data, sz));
+#else
+ NNI_ARG_UNUSED(res);
+ NNI_ARG_UNUSED(data);
+ NNI_ARG_UNUSED(sz);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
+nng_http_req_set_data(nng_http_req *req, const void *data, size_t sz)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_req_set_data(req, data, sz));
+#else
+ NNI_ARG_UNUSED(req);
+ NNI_ARG_UNUSED(data);
+ NNI_ARG_UNUSED(sz);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
+nng_http_res_set_data(nng_http_res *res, const void *data, size_t sz)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_res_set_data(res, data, sz));
+#else
+ NNI_ARG_UNUSED(res);
+ NNI_ARG_UNUSED(data);
+ NNI_ARG_UNUSED(sz);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+const char *
+nng_http_req_get_method(nng_http_req *req)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_req_get_method(req));
+#else
+ NNI_ARG_UNUSED(req);
+ return (NULL);
+#endif
+}
+
+const char *
+nng_http_req_get_version(nng_http_req *req)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_req_get_version(req));
+#else
+ NNI_ARG_UNUSED(req);
+ return (NULL);
+#endif
+}
+
+const char *
+nng_http_req_get_uri(nng_http_req *req)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_req_get_uri(req));
+#else
+ NNI_ARG_UNUSED(req);
+ return (NULL);
+#endif
+}
+
+int
+nng_http_req_set_method(nng_http_req *req, const char *meth)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_req_set_method(req, meth));
+#else
+ NNI_ARG_UNUSED(req);
+ NNI_ARG_UNUSED(meth);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
+nng_http_req_set_version(nng_http_req *req, const char *vers)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_req_set_version(req, vers));
+#else
+ NNI_ARG_UNUSED(req);
+ NNI_ARG_UNUSED(vers);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
+nng_http_req_set_uri(nng_http_req *req, const char *uri)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_req_set_uri(req, uri));
+#else
+ NNI_ARG_UNUSED(req);
+ NNI_ARG_UNUSED(uri);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+uint16_t
+nng_http_res_get_status(nng_http_res *res)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_res_get_status(res));
+#else
+ NNI_ARG_UNUSED(res);
+ return (0);
+#endif
+}
+
+const char *
+nng_http_res_get_version(nng_http_res *res)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_res_get_version(res));
+#else
+ NNI_ARG_UNUSED(res);
+ return (NULL);
+#endif
+}
+
+const char *
+nng_http_res_get_reason(nng_http_res *res)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_res_get_reason(res));
+#else
+ NNI_ARG_UNUSED(res);
+ return (NULL);
+#endif
+}
+
+int
+nng_http_res_set_status(nng_http_res *res, uint16_t status)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_res_set_status(res, status));
+#else
+ NNI_ARG_UNUSED(res);
+ NNI_ARG_UNUSED(status);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
+nng_http_res_set_version(nng_http_res *res, const char *vers)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_res_set_version(res, vers));
+#else
+ NNI_ARG_UNUSED(res);
+ NNI_ARG_UNUSED(vers);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
+nng_http_res_set_reason(nng_http_res *res, const char *rsn)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_res_set_reason(res, rsn));
+#else
+ NNI_ARG_UNUSED(res);
+ NNI_ARG_UNUSED(rsn);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+void
+nng_http_conn_close(nng_http_conn *conn)
+{
+#ifdef NNG_SUPP_HTTP
+ // API version of this closes *and* frees the structure.
+ nni_http_conn_fini(conn);
+#else
+ NNI_ARG_UNUSED(conn);
+#endif
+}
+
+void
+nng_http_conn_read(nng_http_conn *conn, nng_aio *aio)
+{
+#ifdef NNG_SUPP_HTTP
+ nni_http_read(conn, aio);
+#else
+ NNI_ARG_UNUSED(conn);
+ NNI_ARG_UNUSED(aio);
+#endif
+}
+
+void
+nng_http_conn_read_all(nng_http_conn *conn, nng_aio *aio)
+{
+#ifdef NNG_SUPP_HTTP
+ nni_http_read_full(conn, aio);
+#else
+ NNI_ARG_UNUSED(conn);
+ NNI_ARG_UNUSED(aio);
+#endif
+}
+
+void
+nng_http_conn_write(nng_http_conn *conn, nng_aio *aio)
+{
+#ifdef NNG_SUPP_HTTP
+ nni_http_write(conn, aio);
+#else
+ NNI_ARG_UNUSED(conn);
+ NNI_ARG_UNUSED(aio);
+#endif
+}
+
+void
+nng_http_conn_write_all(nng_http_conn *conn, nng_aio *aio)
+{
+#ifdef NNG_SUPP_HTTP
+ nni_http_write_full(conn, aio);
+#else
+ NNI_ARG_UNUSED(conn);
+ NNI_ARG_UNUSED(aio);
+#endif
+}
+
+void
+nng_http_conn_write_req(nng_http_conn *conn, nng_http_req *req, nng_aio *aio)
+{
+#ifdef NNG_SUPP_HTTP
+ nni_http_write_req(conn, req, aio);
+#else
+ NNI_ARG_UNUSED(conn);
+ NNI_ARG_UNUSED(req);
+ NNI_ARG_UNUSED(aio);
+#endif
+}
+
+void
+nng_http_conn_write_res(nng_http_conn *conn, nng_http_res *res, nng_aio *aio)
+{
+#ifdef NNG_SUPP_HTTP
+ nni_http_write_res(conn, res, aio);
+#else
+ NNI_ARG_UNUSED(conn);
+ NNI_ARG_UNUSED(res);
+ NNI_ARG_UNUSED(aio);
+#endif
+}
+
+void
+nng_http_conn_read_req(nng_http_conn *conn, nng_http_req *req, nng_aio *aio)
+{
+#ifdef NNG_SUPP_HTTP
+ nni_http_read_req(conn, req, aio);
+#else
+ NNI_ARG_UNUSED(conn);
+ NNI_ARG_UNUSED(req);
+ NNI_ARG_UNUSED(aio);
+#endif
+}
+
+void
+nng_http_conn_read_res(nng_http_conn *conn, nng_http_res *res, nng_aio *aio)
+{
+#ifdef NNG_SUPP_HTTP
+ nni_http_read_res(conn, res, aio);
+#else
+ NNI_ARG_UNUSED(conn);
+ NNI_ARG_UNUSED(res);
+ NNI_ARG_UNUSED(aio);
+#endif
+}
+
+int
+nng_http_handler_alloc(
+ nng_http_handler **hp, const char *uri, void (*cb)(nng_aio *))
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_handler_init(hp, uri, cb));
+#else
+ NNI_ARG_UNUSED(hp);
+ NNI_ARG_UNUSED(uri);
+ NNI_ARG_UNUSED(cb);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+void
+nng_http_handler_free(nng_http_handler *h)
+{
+#ifdef NNG_SUPP_HTTP
+ nni_http_handler_fini(h);
+#else
+ NNI_ARG_UNUSED(h);
+#endif
+}
+
+int
+nng_http_handler_alloc_file(
+ nng_http_handler **hp, const char *uri, const char *path)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_handler_init_file(hp, uri, path));
+#else
+ NNI_ARG_UNUSED(hp);
+ NNI_ARG_UNUSED(uri);
+ NNI_ARG_UNUSED(path);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
+nng_http_handler_alloc_directory(
+ nng_http_handler **hp, const char *uri, const char *path)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_handler_init_directory(hp, uri, path));
+#else
+ NNI_ARG_UNUSED(hp);
+ NNI_ARG_UNUSED(uri);
+ NNI_ARG_UNUSED(path);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
+nng_http_handler_alloc_static(nng_http_handler **hp, const char *uri,
+ const void *data, size_t size, const char *ctype)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_handler_init_static(hp, uri, data, size, ctype));
+#else
+ NNI_ARG_UNUSED(hp);
+ NNI_ARG_UNUSED(uri);
+ NNI_ARG_UNUSED(data);
+ NNI_ARG_UNUSED(size);
+ NNI_ARG_UNUSED(ctype);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
+nng_http_handler_set_method(nng_http_handler *h, const char *meth)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_handler_set_method(h, meth));
+#else
+ NNI_ARG_UNUSED(h);
+ NNI_ARG_UNUSED(meth);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
+nng_http_handler_set_host(nng_http_handler *h, const char *host)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_handler_set_host(h, host));
+#else
+ NNI_ARG_UNUSED(h);
+ NNI_ARG_UNUSED(host);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
+nng_http_handler_set_data(nng_http_handler *h, void *dat, void (*dtor)(void *))
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_handler_set_data(h, dat, dtor));
+#else
+ NNI_ARG_UNUSED(h);
+ NNI_ARG_UNUSED(dat);
+ NNI_ARG_UNUSED(dtor);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+void *
+nng_handler_get_data(nng_http_handler *h)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_handler_get_data(h));
+#else
+ NNI_ARG_UNUSED(h);
+ return (NULL);
+#endif
+}
+
+int
+nng_http_server_hold(nng_http_server **srvp, const nng_url *url)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_server_init(srvp, url));
+#else
+ NNI_ARG_UNUSED(srvp);
+ NNI_ARG_UNUSED(url);
+#endif
+}
+
+void
+nng_http_server_release(nng_http_server *srv)
+{
+#ifdef NNG_SUPP_HTTP
+ nni_http_server_fini(srv);
+#else
+ NNI_ARG_UNUSED(srv);
+#endif
+}
+
+int
+nng_http_server_start(nng_http_server *srv)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_server_start(srv));
+#else
+ NNI_ARG_UNUSED(srv);
+#endif
+}
+
+void
+nng_http_server_stop(nng_http_server *srv)
+{
+#ifdef NNG_SUPP_HTTP
+ nni_http_server_stop(srv);
+#else
+ NNI_ARG_UNUSED(srv);
+#endif
+}
+
+int
+nng_http_server_add_handler(nng_http_server *srv, nng_http_handler *h)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_server_add_handler(srv, h));
+#else
+ NNI_ARG_UNUSED(srv);
+ NNI_ARG_UNUSED(h);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
+nng_http_server_del_handler(nng_http_server *srv, nng_http_handler *h)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_server_del_handler(srv, h));
+#else
+ NNI_ARG_UNUSED(srv);
+ NNI_ARG_UNUSED(h);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
+nng_http_server_set_tls(nng_http_server *srv, nng_tls_config *cfg)
+{
+#if defined(NNG_SUPP_HTTP) && defined(NNG_SUPP_TLS)
+ return (nni_http_server_set_tls(srv, cfg));
+#else
+ NNI_ARG_UNUSED(srv);
+ NNI_ARG_UNUSED(cfg);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
+nng_http_server_get_tls(nng_http_server *srv, nng_tls_config **cfgp)
+{
+#if defined(NNG_SUPP_HTTP) && defined(NNG_SUPP_TLS)
+ return (nni_http_server_get_tls(srv, cfgp));
+#else
+ NNI_ARG_UNUSED(srv);
+ NNI_ARG_UNUSED(cfgp);
+ return (NNG_ENOTSUP);
+#endif
+}
diff --git a/src/supplemental/http/server.c b/src/supplemental/http/http_server.c
index ecb544ea..21a88f9e 100644
--- a/src/supplemental/http/server.c
+++ b/src/supplemental/http/http_server.c
@@ -27,22 +27,21 @@ static nni_initializer http_server_initializer = {
.i_once = 0,
};
-struct nni_http_handler {
+struct nng_http_handler {
nni_list_node node;
- void ** args;
- unsigned nargs;
char * path;
- const char * method;
+ char * method;
char * host;
bool tree;
int refcnt;
+ void * data;
+ nni_cb dtor;
void (*cb)(nni_aio *);
- void (*dtor)(nni_http_handler *);
};
typedef struct nni_http_ctx {
nni_list_node node;
- nni_http * http;
+ nni_http_conn * conn;
nni_http_server *server;
nni_http_req * req;
nni_http_res * res;
@@ -56,7 +55,7 @@ typedef struct nni_http_ctx {
nni_reap_item reap;
} http_sconn;
-struct nni_http_server {
+struct nng_http_server {
nng_sockaddr addr;
nni_list_node node;
int refcnt;
@@ -82,16 +81,15 @@ nni_http_handler_init(
if ((h = NNI_ALLOC_STRUCT(h)) == NULL) {
return (NNG_ENOMEM);
}
- if ((h->path = nni_strdup(path)) == NULL) {
- NNI_FREE_STRUCT(h);
+ if (((h->path = nni_strdup(path)) == NULL) ||
+ ((h->method = nni_strdup("GET")) == NULL)) {
+ nni_http_handler_fini(h);
return (NNG_ENOMEM);
}
NNI_LIST_NODE_INIT(&h->node);
h->cb = cb;
- h->args = NULL;
- h->nargs = 0;
+ h->data = NULL;
h->dtor = NULL;
- h->method = "GET";
h->host = NULL;
h->tree = false;
h->refcnt = 0;
@@ -106,54 +104,44 @@ nni_http_handler_fini(nni_http_handler *h)
return;
}
if (h->dtor != NULL) {
- h->dtor(h);
- }
- if (h->nargs > 0) {
- nni_free(h->args, h->nargs * sizeof(void *));
+ h->dtor(h->data);
}
nni_strfree(h->host);
nni_strfree(h->path);
+ nni_strfree(h->method);
NNI_FREE_STRUCT(h);
}
int
-nni_http_handler_set_data(nni_http_handler *h, void *data, unsigned index)
+nni_http_handler_set_data(nni_http_handler *h, void *data, nni_cb dtor)
{
if (h->refcnt != 0) {
return (NNG_EBUSY);
}
- if (index >= h->nargs) {
- void ** newargs;
- unsigned newnargs = index + 4; // +4 to reduce allocations
- if ((newargs = nni_alloc(newnargs * sizeof(void *))) == NULL) {
- return (NNG_ENOMEM);
- }
-
- memcpy(newargs, h->args, h->nargs * sizeof(void *));
- for (unsigned i = h->nargs; i < newnargs; i++) {
- newargs[i] = NULL;
- }
- nni_free(h->args, h->nargs * sizeof(void *));
- h->args = newargs;
- h->nargs = newnargs;
- }
- h->args[index] = data;
+ h->data = data;
+ h->dtor = dtor;
return (0);
}
void *
-nni_http_handler_get_data(nni_http_handler *h, unsigned index)
+nni_http_handler_get_data(nni_http_handler *h)
+{
+ return (h->data);
+}
+
+const char *
+nni_http_handler_get_uri(nni_http_handler *h)
{
- return ((index < h->nargs) ? h->args[index] : NULL);
+ return (h->path);
}
int
-nni_http_handler_set_tree(nni_http_handler *h, bool is_tree)
+nni_http_handler_set_tree(nni_http_handler *h)
{
if (h->refcnt != 0) {
return (NNG_EBUSY);
}
- h->tree = is_tree;
+ h->tree = true;
return (0);
}
@@ -180,21 +168,20 @@ nni_http_handler_set_host(nni_http_handler *h, const char *host)
int
nni_http_handler_set_method(nni_http_handler *h, const char *method)
{
+ char *dupmeth;
if (h->refcnt != 0) {
return (NNG_EBUSY);
}
- h->method = method;
- return (0);
-}
-
-int
-nni_http_handler_set_dtor(
- nni_http_handler *h, void (*dtor)(nni_http_handler *))
-{
- if (h->refcnt != 0) {
- return (NNG_EBUSY);
+ if (method == NULL) {
+ nni_strfree(h->method);
+ h->method = NULL;
+ return (0);
}
- h->dtor = dtor;
+ if ((dupmeth = nni_strdup(method)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ nni_strfree(h->method);
+ h->method = dupmeth;
return (0);
}
@@ -213,14 +200,14 @@ http_sconn_reap(void *arg)
nni_aio_stop(sc->txdataio);
nni_aio_stop(sc->cbaio);
- if (sc->http != NULL) {
- nni_http_fini(sc->http);
+ if (sc->conn != NULL) {
+ nni_http_conn_fini(sc->conn);
}
if (sc->req != NULL) {
- nni_http_req_fini(sc->req);
+ nni_http_req_free(sc->req);
}
if (sc->res != NULL) {
- nni_http_res_fini(sc->res);
+ nni_http_res_free(sc->res);
}
nni_aio_fini(sc->rxaio);
nni_aio_fini(sc->txaio);
@@ -249,9 +236,7 @@ http_sconn_fini(http_sconn *sc)
static void
http_sconn_close_locked(http_sconn *sc)
{
- nni_http_server *s;
- s = sc->server;
- nni_http *h;
+ nni_http_conn *conn;
if (sc->closed) {
return;
@@ -259,13 +244,13 @@ http_sconn_close_locked(http_sconn *sc)
NNI_ASSERT(!sc->finished);
sc->closed = true;
- nni_aio_cancel(sc->rxaio, NNG_ECLOSED);
- nni_aio_cancel(sc->txaio, NNG_ECLOSED);
- nni_aio_cancel(sc->txdataio, NNG_ECLOSED);
- nni_aio_cancel(sc->cbaio, NNG_ECLOSED);
+ nni_aio_abort(sc->rxaio, NNG_ECLOSED);
+ nni_aio_abort(sc->txaio, NNG_ECLOSED);
+ nni_aio_abort(sc->txdataio, NNG_ECLOSED);
+ nni_aio_abort(sc->cbaio, NNG_ECLOSED);
- if ((h = sc->http) != NULL) {
- nni_http_close(h);
+ if ((conn = sc->conn) != NULL) {
+ nni_http_conn_close(conn);
}
http_sconn_fini(sc);
}
@@ -293,7 +278,7 @@ http_sconn_txdatdone(void *arg)
}
if (sc->res != NULL) {
- nni_http_res_fini(sc->res);
+ nni_http_res_free(sc->res);
sc->res = NULL;
}
@@ -303,7 +288,7 @@ http_sconn_txdatdone(void *arg)
}
nni_http_req_reset(sc->req);
- nni_http_read_req(sc->http, sc->req, sc->rxaio);
+ nni_http_read_req(sc->conn, sc->req, sc->rxaio);
}
static void
@@ -311,42 +296,23 @@ http_sconn_txdone(void *arg)
{
http_sconn *sc = arg;
nni_aio * aio = sc->txaio;
- int rv;
- void * data;
- size_t size;
- if ((rv = nni_aio_result(aio)) != 0) {
+ if (nni_aio_result(aio) != 0) {
http_sconn_close(sc);
return;
}
- // For HEAD requests, we just treat like "GET" but don't send
- // the data. (Required per HTTP.)
- if (strcmp(nni_http_req_get_method(sc->req), "HEAD") == 0) {
- size = 0;
- } else {
- nni_http_res_get_data(sc->res, &data, &size);
- }
- if (size) {
- // Submit data.
- sc->txdataio->a_niov = 1;
- sc->txdataio->a_iov[0].iov_buf = data;
- sc->txdataio->a_iov[0].iov_len = size;
- nni_http_write_full(sc->http, sc->txdataio);
- return;
- }
-
if (sc->close) {
http_sconn_close(sc);
return;
}
if (sc->res != NULL) {
- nni_http_res_fini(sc->res);
+ nni_http_res_free(sc->res);
sc->res = NULL;
}
nni_http_req_reset(sc->req);
- nni_http_read_req(sc->http, sc->req, sc->rxaio);
+ nni_http_read_req(sc->conn, sc->req, sc->rxaio);
}
static char
@@ -364,6 +330,7 @@ http_hexval(char c)
return (0);
}
+// XXX: REPLACE THIS WITH CODE USING THE URL FRAMEWORK.
static char *
http_uri_canonify(char *path)
{
@@ -430,7 +397,7 @@ http_sconn_error(http_sconn *sc, uint16_t err)
{
nni_http_res *res;
- if (nni_http_res_init_error(&res, err) != 0) {
+ if (nni_http_res_alloc_error(&res, err) != 0) {
http_sconn_close(sc);
return;
}
@@ -441,32 +408,24 @@ http_sconn_error(http_sconn *sc, uint16_t err)
}
}
sc->res = res;
- nni_http_write_res(sc->http, res, sc->txaio);
+ nni_http_write_res(sc->conn, res, sc->txaio);
}
int
-nni_http_hijack(nni_http_ctx *ctx)
+nni_http_hijack(nni_http_conn *conn)
{
- nni_http_server *s = ctx->server;
+ http_sconn *sc;
- nni_mtx_lock(&s->mtx);
- ctx->http = NULL;
- ctx->req = NULL;
- nni_mtx_unlock(&s->mtx);
- return (0);
-}
+ sc = nni_http_conn_get_ctx(conn);
+ if (sc != NULL) {
+ nni_http_server *s = sc->server;
+ nni_http_conn_set_ctx(conn, NULL);
-int
-nni_http_ctx_stream(nni_http_ctx *ctx, nni_http **hpp)
-{
- nni_http_server *s = ctx->server;
-
- nni_mtx_lock(&s->mtx);
- if ((*hpp = ctx->http) == NULL) {
+ nni_mtx_lock(&s->mtx);
+ sc->conn = NULL;
+ sc->req = NULL;
nni_mtx_unlock(&s->mtx);
- return (NNG_ECLOSED);
}
- nni_mtx_unlock(&s->mtx);
return (0);
}
@@ -498,12 +457,12 @@ http_sconn_rxdone(void *arg)
// cope with HTTP/2.
if ((val = nni_http_req_get_version(req)) == NULL) {
sc->close = true;
- http_sconn_error(sc, NNI_HTTP_STATUS_BAD_REQUEST);
+ http_sconn_error(sc, NNG_HTTP_STATUS_BAD_REQUEST);
return;
}
if (strncmp(val, "HTTP/1.", 7) != 0) {
sc->close = true;
- http_sconn_error(sc, NNI_HTTP_STATUS_HTTP_VERSION_NOT_SUPP);
+ http_sconn_error(sc, NNG_HTTP_STATUS_HTTP_VERSION_NOT_SUPP);
return;
}
if (strcmp(val, "HTTP/1.1") != 0) {
@@ -540,7 +499,8 @@ http_sconn_rxdone(void *arg)
host = nni_http_req_get_header(req, "Host");
if ((host == NULL) && (needhost)) {
// Per RFC 2616 14.23 we have to send 400 status here.
- http_sconn_error(sc, NNI_HTTP_STATUS_BAD_REQUEST);
+ http_sconn_error(sc, NNG_HTTP_STATUS_BAD_REQUEST);
+ nni_free(uri, urisz);
return;
}
@@ -616,16 +576,16 @@ http_sconn_rxdone(void *arg)
nni_mtx_unlock(&s->mtx);
if (badmeth) {
http_sconn_error(
- sc, NNI_HTTP_STATUS_METHOD_NOT_ALLOWED);
+ sc, NNG_HTTP_STATUS_METHOD_NOT_ALLOWED);
} else {
- http_sconn_error(sc, NNI_HTTP_STATUS_NOT_FOUND);
+ http_sconn_error(sc, NNG_HTTP_STATUS_NOT_FOUND);
}
return;
}
nni_aio_set_input(sc->cbaio, 0, sc->req);
nni_aio_set_input(sc->cbaio, 1, h);
- nni_aio_set_input(sc->cbaio, 2, sc);
+ nni_aio_set_input(sc->cbaio, 2, sc->conn);
// Technically, probably callback should initialize this with
// start, but we do it instead.
@@ -668,7 +628,7 @@ http_sconn_cbdone(void *arg)
// If its an upgrader, and they didn't give us back a response,
// it means that they took over, and we should just discard
// this session, without closing the underlying channel.
- if (sc->http == NULL) {
+ if (sc->conn == NULL) {
// If this happens, then the session was hijacked.
// We close the context, but the http channel stays up.
http_sconn_close(sc);
@@ -684,14 +644,24 @@ http_sconn_cbdone(void *arg)
nni_http_res_set_header(res, "Connection", "close");
}
sc->res = res;
- nni_http_write_res(sc->http, res, sc->txaio);
+ if (strcmp(nni_http_req_get_method(sc->req), "HEAD") == 0) {
+ void * data;
+ size_t size;
+ // prune off the data, but preserve the content-length
+ // header. By passing NULL here, we leave off the old
+ // data, but the non-zero size means we don't clobber
+ // the HTTP header.
+ nni_http_res_get_data(res, &data, &size);
+ nni_http_res_set_data(res, NULL, size);
+ }
+ nni_http_write_res(sc->conn, res, sc->txaio);
} else if (sc->close) {
http_sconn_close(sc);
} else {
// Presumably client already sent a response.
// Wait for another request.
nni_http_req_reset(sc->req);
- nni_http_read_req(sc->http, sc->req, sc->rxaio);
+ nni_http_read_req(sc->conn, sc->req, sc->rxaio);
}
}
@@ -706,7 +676,7 @@ http_sconn_init(http_sconn **scp, nni_http_server *s, nni_plat_tcp_pipe *tcp)
return (NNG_ENOMEM);
}
- if (((rv = nni_http_req_init(&sc->req)) != 0) ||
+ if (((rv = nni_http_req_alloc(&sc->req, NULL)) != 0) ||
((rv = nni_aio_init(&sc->rxaio, http_sconn_rxdone, sc)) != 0) ||
((rv = nni_aio_init(&sc->txaio, http_sconn_txdone, sc)) != 0) ||
((rv = nni_aio_init(&sc->txdataio, http_sconn_txdatdone, sc)) !=
@@ -718,14 +688,15 @@ http_sconn_init(http_sconn **scp, nni_http_server *s, nni_plat_tcp_pipe *tcp)
}
if (s->tls != NULL) {
- rv = nni_http_init_tls(&sc->http, s->tls, tcp);
+ rv = nni_http_conn_init_tls(&sc->conn, s->tls, tcp);
} else {
- rv = nni_http_init_tcp(&sc->http, tcp);
+ rv = nni_http_conn_init_tcp(&sc->conn, tcp);
}
if (rv != 0) {
http_sconn_close(sc);
return (rv);
}
+ nni_http_conn_set_ctx(sc->conn, sc);
*scp = sc;
return (0);
}
@@ -765,7 +736,7 @@ http_server_acccb(void *arg)
sc->server = s;
nni_list_append(&s->conns, sc);
- nni_http_read_req(sc->http, sc->req, sc->rxaio);
+ nni_http_read_req(sc->conn, sc->req, sc->rxaio);
nni_plat_tcp_ep_accept(s->tep, s->accaio);
nni_mtx_unlock(&s->mtx);
}
@@ -816,14 +787,12 @@ nni_http_server_fini(nni_http_server *s)
}
static int
-http_server_init(nni_http_server **serverp, nni_url *url)
+http_server_init(nni_http_server **serverp, const nni_url *url)
{
nni_http_server *s;
int rv;
- const char * port;
nni_aio * aio;
- port = url->u_port;
if ((strcmp(url->u_scheme, "http") != 0) &&
#ifdef NNG_SUPP_TLS
(strcmp(url->u_scheme, "https") != 0) &&
@@ -874,7 +843,7 @@ http_server_init(nni_http_server **serverp, nni_url *url)
http_server_fini(s);
return (rv);
}
- aio->a_addr = &s->addr;
+ nni_aio_set_input(aio, 0, &s->addr);
nni_plat_tcp_resolv(s->hostname, s->port, NNG_AF_UNSPEC, true, aio);
nni_aio_wait(aio);
rv = nni_aio_result(aio);
@@ -889,7 +858,7 @@ http_server_init(nni_http_server **serverp, nni_url *url)
}
int
-nni_http_server_init(nni_http_server **serverp, nni_url *url)
+nni_http_server_init(nni_http_server **serverp, const nni_url *url)
{
int rv;
nni_http_server *s;
@@ -1058,15 +1027,22 @@ nni_http_server_add_handler(nni_http_server *s, nni_http_handler *h)
return (0);
}
-void
+int
nni_http_server_del_handler(nni_http_server *s, nni_http_handler *h)
{
+ int rv = NNG_ENOENT;
+ nni_http_handler *srch;
nni_mtx_lock(&s->mtx);
- if (nni_list_node_active(&h->node)) {
- nni_list_remove(&s->handlers, h);
- h->refcnt--;
+ NNI_LIST_FOREACH (&s->handlers, srch) {
+ if (srch == h) {
+ nni_list_remove(&s->handlers, h);
+ h->refcnt--;
+ rv = 0;
+ break;
+ }
}
nni_mtx_unlock(&s->mtx);
+ return (rv);
}
// Very limited MIME type map. Used only if the handler does not
@@ -1127,6 +1103,11 @@ http_lookup_type(const char *path)
return (NULL);
}
+typedef struct http_file {
+ char *path;
+ char *ctype;
+} http_file;
+
static void
http_handle_file(nni_aio *aio)
{
@@ -1135,31 +1116,36 @@ http_handle_file(nni_aio *aio)
void * data;
size_t size;
int rv;
- char * path = nni_http_handler_get_data(h, 0);
- char * ctype = nni_http_handler_get_data(h, 1);
+ http_file * hf = nni_http_handler_get_data(h);
+ char * path = nni_http_handler_get_data(h);
+ const char * ctype;
+
+ if ((ctype = hf->ctype) == NULL) {
+ ctype = "application/octet-stream";
+ }
// This is a very simplistic file server, suitable only for small
// files. In the future we can use an AIO based file read, where
// we read files a bit at a time, or even mmap them, and serve
// them up chunkwise. Applications could even come up with their own
// caching version of the http handler.
- if ((rv = nni_file_get(path, &data, &size)) != 0) {
+ if ((rv = nni_file_get(hf->path, &data, &size)) != 0) {
uint16_t status;
switch (rv) {
case NNG_ENOMEM:
- status = NNI_HTTP_STATUS_INTERNAL_SERVER_ERROR;
+ status = NNG_HTTP_STATUS_INTERNAL_SERVER_ERROR;
break;
case NNG_ENOENT:
- status = NNI_HTTP_STATUS_NOT_FOUND;
+ status = NNG_HTTP_STATUS_NOT_FOUND;
break;
case NNG_EPERM:
- status = NNI_HTTP_STATUS_FORBIDDEN;
+ status = NNG_HTTP_STATUS_FORBIDDEN;
break;
default:
- status = NNI_HTTP_STATUS_INTERNAL_SERVER_ERROR;
+ status = NNG_HTTP_STATUS_INTERNAL_SERVER_ERROR;
break;
}
- if ((rv = nni_http_res_init_error(&res, status)) != 0) {
+ if ((rv = nni_http_res_alloc_error(&res, status)) != 0) {
nni_aio_finish_error(aio, rv);
return;
}
@@ -1167,14 +1153,13 @@ http_handle_file(nni_aio *aio)
nni_aio_finish(aio, 0, 0);
return;
}
- if (((rv = nni_http_res_init(&res)) != 0) ||
- ((rv = nni_http_res_set_status(res, NNI_HTTP_STATUS_OK, "OK")) !=
- 0) ||
+ if (((rv = nni_http_res_alloc(&res)) != 0) ||
+ ((rv = nni_http_res_set_status(res, NNG_HTTP_STATUS_OK)) != 0) ||
((rv = nni_http_res_set_header(res, "Content-Type", ctype)) !=
0) ||
((rv = nni_http_res_copy_data(res, data, size)) != 0)) {
if (res != NULL) {
- nni_http_res_fini(res);
+ nni_http_res_free(res);
}
nni_free(data, size);
nni_aio_finish_error(aio, rv);
@@ -1187,10 +1172,14 @@ http_handle_file(nni_aio *aio)
}
static void
-http_file_dtor(nni_http_handler *h)
+http_file_free(void *arg)
{
- char *p = nni_http_handler_get_data(h, 0);
- nni_strfree(p);
+ http_file *hf;
+ if ((hf = arg) != NULL) {
+ nni_strfree(hf->path);
+ nni_strfree(hf->ctype);
+ NNI_FREE_STRUCT(hf);
+ }
}
int
@@ -1198,8 +1187,12 @@ nni_http_handler_init_file_ctype(nni_http_handler **hpp, const char *uri,
const char *path, const char *ctype)
{
nni_http_handler *h;
+ http_file * hf;
int rv;
- char * p;
+
+ if ((hf = NNI_ALLOC_STRUCT(hf)) == NULL) {
+ return (NNG_ENOMEM);
+ }
// Later we might want to do this in the server side, if we support
// custom media type lists on a per-server basis. For now doing this
@@ -1209,19 +1202,19 @@ nni_http_handler_init_file_ctype(nni_http_handler **hpp, const char *uri,
ctype = "application/octet-stream";
}
}
- if ((p = nni_strdup(path)) == NULL) {
+ if (((hf->path = nni_strdup(path)) == NULL) ||
+ ((hf->ctype = nni_strdup(ctype)) == NULL)) {
+ http_file_free(hf);
return (NNG_ENOMEM);
}
if ((rv = nni_http_handler_init(&h, uri, http_handle_file)) != 0) {
- nni_strfree(p);
+ http_file_free(hf);
return (rv);
}
- if (((rv = nni_http_handler_set_data(h, p, 0)) != 0) ||
- ((rv = nni_http_handler_set_data(h, (void *) ctype, 1)) != 0) ||
- ((rv = nni_http_handler_set_dtor(h, http_file_dtor)))) {
- nni_strfree(p);
+ if ((rv = nni_http_handler_set_data(h, hf, http_file_free)) != 0) {
+ http_file_free(hf);
nni_http_handler_fini(h);
return (rv);
}
@@ -1238,16 +1231,7 @@ nni_http_handler_init_file(
}
static void
-http_directory_dtor(nni_http_handler *h)
-{
- char *path = nni_http_handler_get_data(h, 0);
- char *base = nni_http_handler_get_data(h, 1);
- nni_strfree(path);
- nni_strfree(base);
-}
-
-static void
-http_handle_directory(nni_aio *aio)
+http_handle_dir(nni_aio *aio)
{
nni_http_req * req = nni_aio_get_input(aio, 0);
nni_http_handler *h = nni_aio_get_input(aio, 1);
@@ -1255,8 +1239,9 @@ http_handle_directory(nni_aio *aio)
void * data;
size_t size;
int rv;
- char * path = nni_http_handler_get_data(h, 0);
- const char * base = nni_http_handler_get_data(h, 1); // base uri
+ http_file * hf = nni_http_handler_get_data(h);
+ const char * path = hf->path;
+ const char * base = nni_http_handler_get_uri(h); // base uri
const char * uri = nni_http_req_get_uri(req);
const char * ctype;
char * dst;
@@ -1335,19 +1320,19 @@ http_handle_directory(nni_aio *aio)
switch (rv) {
case NNG_ENOMEM:
- status = NNI_HTTP_STATUS_INTERNAL_SERVER_ERROR;
+ status = NNG_HTTP_STATUS_INTERNAL_SERVER_ERROR;
break;
case NNG_ENOENT:
- status = NNI_HTTP_STATUS_NOT_FOUND;
+ status = NNG_HTTP_STATUS_NOT_FOUND;
break;
case NNG_EPERM:
- status = NNI_HTTP_STATUS_FORBIDDEN;
+ status = NNG_HTTP_STATUS_FORBIDDEN;
break;
default:
- status = NNI_HTTP_STATUS_INTERNAL_SERVER_ERROR;
+ status = NNG_HTTP_STATUS_INTERNAL_SERVER_ERROR;
break;
}
- if ((rv = nni_http_res_init_error(&res, status)) != 0) {
+ if ((rv = nni_http_res_alloc_error(&res, status)) != 0) {
nni_aio_finish_error(aio, rv);
return;
}
@@ -1356,14 +1341,13 @@ http_handle_directory(nni_aio *aio)
return;
}
- if (((rv = nni_http_res_init(&res)) != 0) ||
- ((rv = nni_http_res_set_status(res, NNI_HTTP_STATUS_OK, "OK")) !=
- 0) ||
+ if (((rv = nni_http_res_alloc(&res)) != 0) ||
+ ((rv = nni_http_res_set_status(res, NNG_HTTP_STATUS_OK)) != 0) ||
((rv = nni_http_res_set_header(res, "Content-Type", ctype)) !=
0) ||
((rv = nni_http_res_copy_data(res, data, size)) != 0)) {
if (res != NULL) {
- nni_http_res_fini(res);
+ nni_http_res_free(res);
}
nni_free(data, size);
nni_aio_finish_error(aio, rv);
@@ -1379,31 +1363,27 @@ int
nni_http_handler_init_directory(
nni_http_handler **hpp, const char *uri, const char *path)
{
+ http_file * hf;
nni_http_handler *h;
int rv;
char * p;
- char * b;
- if ((p = nni_strdup(path)) == NULL) {
+ if ((hf = NNI_ALLOC_STRUCT(hf)) == NULL) {
return (NNG_ENOMEM);
}
- if ((b = nni_strdup(uri)) == NULL) {
- nni_strfree(p);
+ if ((hf->path = nni_strdup(path)) == NULL) {
+ NNI_FREE_STRUCT(hf);
return (NNG_ENOMEM);
}
- if ((rv = nni_http_handler_init(&h, uri, http_handle_directory)) !=
- 0) {
- nni_strfree(p);
+ if ((rv = nni_http_handler_init(&h, uri, http_handle_dir)) != 0) {
+ http_file_free(hf);
return (rv);
}
- if (((rv = nni_http_handler_set_data(h, p, 0)) != 0) ||
- ((rv = nni_http_handler_set_data(h, b, 1)) != 0) ||
- ((rv = nni_http_handler_set_tree(h, true)) != 0) ||
- ((rv = nni_http_handler_set_dtor(h, http_directory_dtor)))) {
- nni_strfree(p);
- nni_strfree(b);
+ if (((rv = nni_http_handler_set_tree(h)) != 0) ||
+ ((rv = nni_http_handler_set_data(h, hf, http_file_free)) != 0)) {
+ http_file_free(hf);
nni_http_handler_fini(h);
return (rv);
}
@@ -1412,32 +1392,34 @@ nni_http_handler_init_directory(
return (0);
}
+typedef struct http_static {
+ void * data;
+ size_t size;
+ char * ctype;
+} http_static;
+
static void
http_handle_static(nni_aio *aio)
{
- void * data;
- size_t size;
- char * ctype;
+ http_static * hs;
+ const char * ctype;
nni_http_handler *h;
nni_http_res * r = NULL;
int rv;
- h = nni_aio_get_input(aio, 1);
- data = nni_http_handler_get_data(h, 0);
- size = (size_t)(uintptr_t) nni_http_handler_get_data(h, 1);
- ctype = nni_http_handler_get_data(h, 2);
+ h = nni_aio_get_input(aio, 1);
+ hs = nni_http_handler_get_data(h);
- if (ctype == NULL) {
+ if ((ctype = hs->ctype) == NULL) {
ctype = "application/octet-stream";
}
- if (((rv = nni_http_res_init(&r)) != 0) ||
+ if (((rv = nni_http_res_alloc(&r)) != 0) ||
((rv = nni_http_res_set_header(r, "Content-Type", ctype)) != 0) ||
- ((rv = nni_http_res_set_status(r, NNI_HTTP_STATUS_OK, "OK")) !=
- 0) ||
- ((rv = nni_http_res_set_data(r, data, size)) != 0)) {
+ ((rv = nni_http_res_set_status(r, NNG_HTTP_STATUS_OK)) != 0) ||
+ ((rv = nni_http_res_set_data(r, hs->data, hs->size)) != 0)) {
if (r != NULL) {
- nni_http_res_fini(r);
+ nni_http_res_free(r);
}
nni_aio_finish_error(aio, rv);
return;
@@ -1448,12 +1430,15 @@ http_handle_static(nni_aio *aio)
}
static void
-http_static_dtor(nni_http_handler *h)
+http_static_free(void *arg)
{
- void * data = nni_http_handler_get_data(h, 0);
- size_t size = (size_t)(uintptr_t) nni_http_handler_get_data(h, 1);
+ http_static *hs;
- nni_free(data, size);
+ if ((hs = arg) != NULL) {
+ nni_free(hs->data, hs->size);
+ nni_strfree(hs->ctype);
+ NNI_FREE_STRUCT(hs);
+ }
}
int
@@ -1462,23 +1447,26 @@ nni_http_handler_init_static(nni_http_handler **hpp, const char *uri,
{
nni_http_handler *h;
int rv;
- void * dat;
+ http_static * hs;
- if ((dat = nni_alloc(size)) == NULL) {
+ if ((hs = NNI_ALLOC_STRUCT(hs)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ if (((hs->ctype = nni_strdup(ctype)) == NULL) ||
+ ((size > 0) && ((hs->data = nni_alloc(size)) == NULL))) {
+ http_static_free(hs);
return (NNG_ENOMEM);
}
- memcpy(dat, data, size);
+ hs->size = size;
+ memcpy(hs->data, data, size);
if ((rv = nni_http_handler_init(&h, uri, http_handle_static)) != 0) {
- nni_free(dat, size);
+ http_static_free(hs);
return (rv);
}
- if (((rv = nni_http_handler_set_data(h, dat, 0)) != 0) ||
- ((rv = nni_http_handler_set_data(h, (void *) size, 1)) != 0) ||
- ((rv = nni_http_handler_set_data(h, (void *) ctype, 2)) != 0) ||
- ((rv = nni_http_handler_set_dtor(h, http_static_dtor)) != 0)) {
- nni_free(dat, size);
+ if ((rv = nni_http_handler_set_data(h, hs, http_static_free)) != 0) {
+ http_static_free(hs);
nni_http_handler_fini(h);
return (rv);
}
@@ -1541,4 +1529,4 @@ static void
http_server_sys_fini(void)
{
nni_mtx_fini(&http_servers_lk);
-} \ No newline at end of file
+}