diff options
Diffstat (limited to 'src/supplemental/http')
| -rw-r--r-- | src/supplemental/http/CMakeLists.txt | 19 | ||||
| -rw-r--r-- | src/supplemental/http/http.c | 740 | ||||
| -rw-r--r-- | src/supplemental/http/http.h | 226 | ||||
| -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.c | 765 | ||||
| -rw-r--r-- | src/supplemental/http/http_msg.c | 318 | ||||
| -rw-r--r-- | src/supplemental/http/http_public.c | 668 | ||||
| -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 +} |
