From f42d0c6ef956d119e8762a3ecda37886fa055637 Mon Sep 17 00:00:00 2001 From: Garrett D'Amore Date: Sun, 5 Jan 2025 16:46:03 -0800 Subject: http: server callback API simplified This simplified API lets callbacks obtain the response from the connection objection directly, and does not require the aio to carry it as a parameter. Further, the request and response are both stored inline in the connection, reducing allocations. This is at present only for the server; the client will get a similar set of changes. --- docs/man/nng_http_conn_read_req.3http.adoc | 10 +- docs/man/nng_http_conn_write_res.3http.adoc | 7 +- docs/man/nng_http_handler_alloc.3http.adoc | 29 ++-- include/nng/supplemental/http/http.h | 30 ++-- src/supplemental/http/http_api.h | 17 +- src/supplemental/http/http_conn.c | 36 +++-- src/supplemental/http/http_msg.c | 46 ++++-- src/supplemental/http/http_public.c | 12 +- src/supplemental/http/http_server.c | 230 ++++++++++++---------------- src/supplemental/http/http_server_test.c | 12 +- src/supplemental/websocket/websocket.c | 78 +++------- 11 files changed, 231 insertions(+), 276 deletions(-) diff --git a/docs/man/nng_http_conn_read_req.3http.adoc b/docs/man/nng_http_conn_read_req.3http.adoc index 01d790cc..4a8d3e05 100644 --- a/docs/man/nng_http_conn_read_req.3http.adoc +++ b/docs/man/nng_http_conn_read_req.3http.adoc @@ -1,6 +1,6 @@ = nng_http_conn_read_req(3http) // -// Copyright 2018 Staysail Systems, Inc. +// Copyright 2025 Staysail Systems, Inc. // Copyright 2018 Capitar IT Group BV // // This document is supplied under the terms of the MIT License, a @@ -20,15 +20,15 @@ nng_http_conn_read_req - read HTTP request #include #include -void nng_http_conn_read_req(nng_http_conn *conn, nng_http_req *req, - nng_aio *aio); +void nng_http_conn_read_req(nng_http_conn *conn, nng_aio *aio); ---- == DESCRIPTION The `nng_http_conn_read_req()` function starts an asynchronous read from the -HTTP connection _conn_, reading an HTTP request into the _req_, including all -of the related headers. +HTTP connection _conn_, reading an HTTP request into the request object +associated with _conn_, including all of the related headers. +(The request object can be obtained via `nng_http_conn_req()`. NOTE: Any HTTP entity/body data associated with the request is *not* read automatically. diff --git a/docs/man/nng_http_conn_write_res.3http.adoc b/docs/man/nng_http_conn_write_res.3http.adoc index 6c998785..e88ad164 100644 --- a/docs/man/nng_http_conn_write_res.3http.adoc +++ b/docs/man/nng_http_conn_write_res.3http.adoc @@ -1,6 +1,6 @@ = nng_http_conn_write_res(3http) // -// Copyright 2018 Staysail Systems, Inc. +// Copyright 2025 Staysail Systems, Inc. // Copyright 2018 Capitar IT Group BV // // This document is supplied under the terms of the MIT License, a @@ -20,14 +20,13 @@ nng_http_conn_write_res - write HTTP response #include #include -void nng_http_conn_write_res(nng_http_conn *conn, nng_http_res *res, - nng_aio *aio); +void nng_http_conn_write_res(nng_http_conn *conn, nng_aio *aio); ---- == DESCRIPTION The `nng_http_conn_write_res()` function starts an asynchronous write of -the HTTP response _res_ to the connection _conn_. +the HTTP response associated with the connection _conn_. The entire response is sent, including headers, and if present, the response body data. (The response body can be set with diff --git a/docs/man/nng_http_handler_alloc.3http.adoc b/docs/man/nng_http_handler_alloc.3http.adoc index 9291fd56..23171450 100644 --- a/docs/man/nng_http_handler_alloc.3http.adoc +++ b/docs/man/nng_http_handler_alloc.3http.adoc @@ -1,6 +1,6 @@ = nng_http_handler_alloc(3http) // -// Copyright 2018 Staysail Systems, Inc. +// Copyright 2025 Staysail Systems, Inc. // Copyright 2018 Capitar IT Group BV // Copyright 2020 Dirac Research // @@ -23,8 +23,10 @@ nng_http_handler_alloc - allocate HTTP server handler typedef struct nng_http_handler nng_http_handler; +typedef void (*nng_http_hander_func)(nng_http_conn *conn, void *arg, nng_aio *aio); + int nng_http_handler_alloc(nng_http_handler **hp, const char *path, - void (*func)(nng_aio *); + nng_http_handler_func cb); int nng_http_handler_alloc_directory(nng_http_handler **hp, const char *path, const char *dirname); @@ -72,24 +74,19 @@ rather than just a single element. The generic (first) form of this creates a handler that uses a user-supplied function to process HTTP requests. This function uses the asynchronous I/O framework. -The function takes a pointer to an xref:nng_aio.5.adoc[`nng_aio`] structure. -The _aio_ will be passed with the following input values (retrieved with -xref:nng_aio_get_input.3.adoc[`nng_aio_get_input()`]): +The function receives the connection on _conn_, and the data that it set +previously with `nng_http_handler_set_data` as the second argument. The +final argument is the _aio_, which must be "finished" to complete the operation. - 0: `nng_http_req *` __request__:: The client's HTTP request. - 1: `nng_http_handler *` __handler__:: Pointer to the handler object. - 2: `nng_http_conn *` __conn__:: The underlying HTTP connection. +The function takes a pointer to an xref:nng_aio.5.adoc[`nng_aio`] structure. -The handler should create an `nng_http_res *` response (such as via -xref:nng_http_res_alloc.3http.adoc[`nng_http_res_alloc()`] or -xref:nng_http_res_alloc_error.3http.adoc[`nng_http_res_alloc_error()`]) and store that -in as the first output (index 0) with -xref:nng_aio_set_output.3.adoc[`nng_aio_set_output()`]. +The handler should obtain `nng_http_res *` response from the +connection (`nng_http_conn_res`) and update it to reflect the final status. -Alternatively, the handler may send the HTTP response (and any associated -body data) itself using the connection. -In that case the output at index 0 of the _aio_ should be NULL. +The handler may call `nng_http_conn_write_res` to send the response, or +it may simply let the framework do so on its behalf. The server will perform +this step if the callback has not already done so. Finally, using the xref:nng_aio_finish.3.adoc[`nng_aio_finish()`] function, the _aio_ should be completed successfully. diff --git a/include/nng/supplemental/http/http.h b/include/nng/supplemental/http/http.h index 53f8436c..344dbbe0 100644 --- a/include/nng/supplemental/http/http.h +++ b/include/nng/supplemental/http/http.h @@ -275,15 +275,13 @@ NNG_DECL void nng_http_conn_write_req( nng_http_conn *, nng_http_req *, nng_aio *); // nng_http_conn_write_res writes the entire response. It will also write any -// data that has been attached. -NNG_DECL void nng_http_conn_write_res( - nng_http_conn *, nng_http_res *, nng_aio *); +// data that has been attached. It uses the res object in the conn. +NNG_DECL void nng_http_conn_write_res(nng_http_conn *, nng_aio *); // nng_http_conn_read_req reads an entire request, EXCEPT for any entity // data. The caller is responsible for processing the headers in the request // and reading any submitted entity data itself. -NNG_DECL void nng_http_conn_read_req( - nng_http_conn *, nng_http_req *, nng_aio *); +NNG_DECL void nng_http_conn_read_req(nng_http_conn *, nng_aio *); // nng_http_conn_read_res reads an entire response, EXCEPT for any entity // data. The caller is responsible for processing the headers in the response @@ -310,22 +308,20 @@ typedef struct nng_http_handler nng_http_handler; // is registered with the server, and that a handler can only be registered // once per server. // -// The callback function will receive the following arguments (via -// nng_aio_get_input(): nng_http_request *, nng_http_handler *, and -// nng_http_conn *. 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 connection, which can be used -// to hijack the session. -// -// Upon completion, the handler should store an nng_http_res * as the -// first output using nng_aio_set_output. If it does not do so, or supplies -// NULL, then it must send a response itself. +// The callback function should obtain the request (if needed), and +// the response from the connection object using nng_http_conn_req +// and nng_http_conn_res. If the connection is hijacked, then the +// response object will not be used, otherwise the server will send it on +// behalf of the client. // // The callback should complete with a result of 0 in most circumstances. // If it completes with an error, then the connection is terminated, after -// possibly sending a 500 error response to the client. +// possibly sending a 500 error response to the client. The callback signals +// completion by nng_aio_finish. The second argument to this function is the +// handler data that was optionally set by nng_handler_set_data. +typedef void (*nng_http_handler_func)(nng_http_conn *, void *, nng_aio *); NNG_DECL int nng_http_handler_alloc( - nng_http_handler **, const char *, void (*)(nng_aio *)); + nng_http_handler **, const char *, nng_http_handler_func); // nng_http_handler_free frees the handler. This actually just drops a // reference count on the handler, as it may be in use by an existing diff --git a/src/supplemental/http/http_api.h b/src/supplemental/http/http_api.h index a6dfa6ca..1630198d 100644 --- a/src/supplemental/http/http_api.h +++ b/src/supplemental/http/http_api.h @@ -114,12 +114,13 @@ extern int nni_http_conn_getopt( extern int nni_http_req_alloc(nni_http_req **, const nng_url *); extern int nni_http_res_alloc(nni_http_res **); extern int nni_http_res_alloc_error(nni_http_res **, uint16_t); +extern int nni_http_res_set_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 void nni_http_read_req(nni_http_conn *, nni_aio *); +extern void nni_http_write_res(nni_http_conn *, nni_aio *); extern const char *nni_http_req_get_header(const nni_http_req *, const char *); extern const char *nni_http_res_get_header(const nni_http_res *, const char *); @@ -258,15 +259,8 @@ extern int nni_http_hijack(nni_http_conn *); // Note that methods which modify a handler cannot be called while the handler // is registered with the server, and that a handler can only be registered // once per server. -// -// The callback function will receive the following arguments (via -// nng_aio_get_input(): nni_http_request *, nni_http_handler *, and -// nni_http_conn_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 -// to hijack the session. extern int nni_http_handler_init( - nni_http_handler **, const char *, void (*)(nni_aio *)); + nni_http_handler **, const char *, nng_http_handler_func); // nni_http_handler_init_file creates a handler with a function to serve // up a file named in the last argument. @@ -395,4 +389,7 @@ extern void nni_http_transact( // upper layer scheme. extern const char *nni_http_stream_scheme(const char *); +// Private method used for the server. +extern bool nni_http_conn_res_sent(nni_http_conn *conn); + #endif // NNG_SUPPLEMENTAL_HTTP_HTTP_API_H diff --git a/src/supplemental/http/http_conn.c b/src/supplemental/http/http_conn.c index 912f2cc9..9ed882f1 100644 --- a/src/supplemental/http/http_conn.c +++ b/src/supplemental/http/http_conn.c @@ -63,6 +63,8 @@ struct nng_http_conn { size_t rd_bufsz; bool rd_buffered; + bool res_sent; + enum write_flavor wr_flavor; }; @@ -509,10 +511,13 @@ http_wr_submit(nni_http_conn *conn, nni_aio *aio, enum write_flavor flavor) } void -nni_http_read_req(nni_http_conn *conn, nni_http_req *req, nni_aio *aio) +nni_http_read_req(nni_http_conn *conn, nni_aio *aio) { - nni_aio_set_prov_data(aio, req); + nni_aio_set_prov_data(aio, &conn->req); + // clear the sent flag (used for the server) + conn->res_sent = false; + nni_http_req_reset(&conn->req); nni_mtx_lock(&conn->mtx); http_rd_submit(conn, aio, HTTP_RD_REQ); nni_mtx_unlock(&conn->mtx); @@ -590,16 +595,18 @@ nni_http_write_req(nni_http_conn *conn, nni_http_req *req, nni_aio *aio) } void -nni_http_write_res(nni_http_conn *conn, nni_http_res *res, nni_aio *aio) +nni_http_write_res(nni_http_conn *conn, nni_aio *aio) { - int rv; - void *buf; - size_t bufsz; - void *data; - size_t size; - nni_iov iov[2]; - int nio; - + int rv; + void *buf; + size_t bufsz; + void *data; + size_t size; + nni_iov iov[2]; + int nio; + nng_http_res *res = nng_http_conn_res(conn); + + conn->res_sent = true; if ((rv = nni_http_res_get_buf(res, &buf, &bufsz)) != 0) { nni_aio_finish_error(aio, rv); return; @@ -713,3 +720,10 @@ nni_http_conn_init(nni_http_conn **connp, nng_stream *stream) } return (rv); } + +// private to the HTTP framework, used on the server +bool +nni_http_conn_res_sent(nni_http_conn *conn) +{ + return (conn->res_sent); +} diff --git a/src/supplemental/http/http_msg.c b/src/supplemental/http/http_msg.c index 08c594ac..6b12496c 100644 --- a/src/supplemental/http/http_msg.c +++ b/src/supplemental/http/http_msg.c @@ -263,8 +263,10 @@ static int http_entity_alloc_data(nni_http_entity *entity, size_t size) { void *newdata; - if ((newdata = nni_zalloc(size)) == NULL) { - return (NNG_ENOMEM); + if (size != 0) { + if ((newdata = nni_zalloc(size)) == NULL) { + return (NNG_ENOMEM); + } } http_entity_set_data(entity, newdata, size); entity->own = true; @@ -1057,25 +1059,37 @@ nni_http_alloc_html_error(char **html, uint16_t code, const char *details) } int -nni_http_res_alloc_error(nni_http_res **resp, uint16_t err) +nni_http_res_set_error(nni_http_res *res, uint16_t err) { - char *html = NULL; - nni_http_res *res = NULL; - int rv; - - if (((rv = nni_http_res_alloc(&res)) != 0) || - ((rv = nni_http_alloc_html_error(&html, err, NULL)) != 0) || + int rv; + char *html = NULL; + if (((rv = nni_http_alloc_html_error(&html, err, NULL)) != 0) || ((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_strfree(html); - nni_http_res_free(res); - } else { - nni_strfree(html); - res->code = err; - res->iserr = true; - *resp = res; + return (rv); } + nni_strfree(html); + res->code = err; + res->iserr = true; + return (0); +} - return (rv); +int +nni_http_res_alloc_error(nni_http_res **resp, uint16_t err) +{ + nni_http_res *res; + int rv; + + if ((rv = nni_http_res_alloc(&res)) != 0) { + return (rv); + } + rv = nni_http_res_set_error(res, err); + if (rv != 0) { + nni_http_res_free(res); + return (rv); + } + *resp = res; + return (0); } diff --git a/src/supplemental/http/http_public.c b/src/supplemental/http/http_public.c index 8a81404e..dd35151a 100644 --- a/src/supplemental/http/http_public.c +++ b/src/supplemental/http/http_public.c @@ -482,25 +482,23 @@ nng_http_conn_write_req(nng_http_conn *conn, nng_http_req *req, nng_aio *aio) } void -nng_http_conn_write_res(nng_http_conn *conn, nng_http_res *res, nng_aio *aio) +nng_http_conn_write_res(nng_http_conn *conn, nng_aio *aio) { #ifdef NNG_SUPP_HTTP - nni_http_write_res(conn, res, aio); + nni_http_write_res(conn, aio); #else NNI_ARG_UNUSED(conn); - NNI_ARG_UNUSED(res); nni_aio_finish_error(aio, NNG_ENOTSUP); #endif } void -nng_http_conn_read_req(nng_http_conn *conn, nng_http_req *req, nng_aio *aio) +nng_http_conn_read_req(nng_http_conn *conn, nng_aio *aio) { #ifdef NNG_SUPP_HTTP - nni_http_read_req(conn, req, aio); + nni_http_read_req(conn, aio); #else NNI_ARG_UNUSED(conn); - NNI_ARG_UNUSED(req); nni_aio_finish_error(aio, NNG_ENOTSUP); #endif } @@ -519,7 +517,7 @@ nng_http_conn_read_res(nng_http_conn *conn, nng_http_res *res, nng_aio *aio) int nng_http_handler_alloc( - nng_http_handler **hp, const char *uri, void (*cb)(nng_aio *)) + nng_http_handler **hp, const char *uri, nng_http_handler_func cb) { #ifdef NNG_SUPP_HTTP return (nni_http_handler_init(hp, uri, cb)); diff --git a/src/supplemental/http/http_server.c b/src/supplemental/http/http_server.c index c6c453c9..87285417 100644 --- a/src/supplemental/http/http_server.c +++ b/src/supplemental/http/http_server.c @@ -1,5 +1,5 @@ // -// Copyright 2024 Staysail Systems, Inc. +// Copyright 2025 Staysail Systems, Inc. // Copyright 2018 Capitar IT Group BV // Copyright 2018 QXSoftware // Copyright 2019 Devolutions @@ -20,35 +20,35 @@ #include "core/nng_impl.h" #include "http_api.h" +#include "nng/supplemental/http/http.h" #ifndef NNG_HTTP_MAX_URI #define NNG_HTTP_MAX_URI 1024 #endif struct nng_http_handler { - nni_list_node node; - char uri[NNG_HTTP_MAX_URI]; - char method[32]; - char host[256]; // RFC 1035 - nng_sockaddr host_addr; - bool host_ip; - bool tree; - bool tree_exclusive; - nni_atomic_int ref; - nni_atomic_bool busy; - size_t maxbody; - bool getbody; - void *data; - nni_cb dtor; - void (*cb)(nni_aio *); + nni_list_node node; + char uri[NNG_HTTP_MAX_URI]; + char method[32]; + char host[256]; // RFC 1035 + nng_sockaddr host_addr; + bool host_ip; + bool tree; + bool tree_exclusive; + nni_atomic_int ref; + nni_atomic_bool busy; + size_t maxbody; + bool getbody; + void *data; + nni_cb dtor; + nng_http_handler_func cb; + void *arg; }; typedef struct http_sconn { nni_list_node node; nni_http_conn *conn; nni_http_server *server; - nni_http_req *req; - nni_http_res *res; nni_http_handler *handler; // set if we deferred to read body nni_http_handler *release; // set if we dispatched handler bool close; @@ -103,7 +103,7 @@ static nni_reap_list http_server_reap_list = { int nni_http_handler_init( - nni_http_handler **hp, const char *uri, void (*cb)(nni_aio *)) + nni_http_handler **hp, const char *uri, nng_http_handler_func cb) { nni_http_handler *h; @@ -255,8 +255,6 @@ http_sc_reap(void *arg) if (sc->conn != NULL) { nni_http_conn_fini(sc->conn); } - nni_http_req_free(sc->req); - nni_http_res_free(sc->res); nni_aio_fini(&sc->rxaio); nni_aio_fini(&sc->txaio); nni_aio_fini(&sc->txdataio); @@ -307,17 +305,13 @@ http_sconn_txdatdone(void *arg) return; } - nni_http_res_free(sc->res); - sc->res = NULL; - if (sc->close) { http_sconn_close(sc); return; } sc->handler = NULL; - nni_http_req_reset(sc->req); - nni_http_read_req(sc->conn, sc->req, &sc->rxaio); + nni_http_read_req(sc->conn, &sc->rxaio); } static void @@ -336,11 +330,8 @@ http_sconn_txdone(void *arg) return; } - nni_http_res_free(sc->res); - sc->res = NULL; sc->handler = NULL; - nni_http_req_reset(sc->req); - nni_http_read_req(sc->conn, sc->req, &sc->rxaio); + nni_http_read_req(sc->conn, &sc->rxaio); } static char @@ -425,26 +416,20 @@ http_sconn_error(http_sconn *sc, uint16_t err) { nni_http_res *res; - if (nni_http_res_alloc(&res) != 0) { - http_sconn_close(sc); - return; - } + res = nng_http_conn_res(sc->conn); nni_http_res_set_status(res, err); if (nni_http_server_res_error(sc->server, res) != 0) { - nni_http_res_free(res); http_sconn_close(sc); return; } if (sc->close) { if (nni_http_res_set_header(res, "Connection", "close") != 0) { - nni_http_res_free(res); http_sconn_close(sc); return; } } - sc->res = res; - nni_http_write_res(sc->conn, res, &sc->txaio); + nni_http_write_res(sc->conn, &sc->txaio); } int @@ -459,7 +444,6 @@ nni_http_hijack(nni_http_conn *conn) nni_mtx_lock(&s->mtx); sc->conn = NULL; - sc->req = NULL; nni_mtx_unlock(&s->mtx); } return (0); @@ -531,7 +515,7 @@ http_sconn_rxdone(void *arg) nni_http_handler *h = NULL; nni_http_handler *head = NULL; const char *val; - nni_http_req *req = sc->req; + nni_http_req *req = nng_http_conn_req(sc->conn); char *uri; size_t urisz; char *path; @@ -693,9 +677,6 @@ http_sconn_rxdone(void *arg) finish: sc->release = h; sc->handler = NULL; - 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->conn); // Set a reference -- this because the callback may be running // asynchronously even after it gets removed from the server. @@ -704,7 +685,11 @@ finish: nni_aio_reset(&sc->cbaio); nni_mtx_unlock(&s->mtx); - h->cb(&sc->cbaio); + + // make sure the response is freshly initialized + nni_http_res_reset(nni_http_conn_res(sc->conn)); + + h->cb(sc->conn, h->data, &sc->cbaio); } static void @@ -730,8 +715,6 @@ http_sconn_cbdone(void *arg) return; } - res = nni_aio_get_output(aio, 0); - // If it's 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. @@ -741,7 +724,8 @@ http_sconn_cbdone(void *arg) http_sconn_close(sc); return; } - if (res != NULL) { + res = nni_http_conn_res(sc->conn); + if (!nni_http_conn_res_sent(sc->conn)) { const char *val; val = nni_http_res_get_header(res, "Connection"); if ((val != NULL) && (strstr(val, "close") != NULL)) { @@ -750,8 +734,9 @@ http_sconn_cbdone(void *arg) if (sc->close) { nni_http_res_set_header(res, "Connection", "close"); } - sc->res = res; - if (strcmp(nni_http_req_get_method(sc->req), "HEAD") == 0) { + if (strcmp( + nni_http_req_get_method(nng_http_conn_req(sc->conn)), + "HEAD") == 0) { void *data; size_t size; // prune off the data, but preserve the content-length @@ -763,15 +748,14 @@ http_sconn_cbdone(void *arg) } else if (nni_http_res_is_error(res)) { (void) nni_http_server_res_error(s, res); } - nni_http_write_res(sc->conn, res, &sc->txaio); + nni_http_write_res(sc->conn, &sc->txaio); } else if (sc->close) { http_sconn_close(sc); } else { // Presumably client already sent a response. // Wait for another request. sc->handler = NULL; - nni_http_req_reset(sc->req); - nni_http_read_req(sc->conn, sc->req, &sc->rxaio); + nni_http_read_req(sc->conn, &sc->rxaio); } } @@ -791,8 +775,7 @@ http_sconn_init(http_sconn **scp, nng_stream *stream) nni_aio_init(&sc->txdataio, http_sconn_txdatdone, sc); nni_aio_init(&sc->cbaio, http_sconn_cbdone, sc); - if (((rv = nni_http_req_alloc(&sc->req, NULL)) != 0) || - ((rv = nni_http_conn_init(&sc->conn, stream)) != 0)) { + if ((rv = nni_http_conn_init(&sc->conn, stream)) != 0) { // Can't even accept the incoming request. Hard close. http_sconn_close(sc); return (rv); @@ -839,7 +822,7 @@ http_server_acccb(void *arg) nni_list_append(&s->conns, sc); sc->handler = NULL; - nni_http_read_req(sc->conn, sc->req, &sc->rxaio); + nni_http_read_req(sc->conn, &sc->rxaio); nng_stream_listener_accept(s->listener, aio); nni_mtx_unlock(&s->mtx); } @@ -1333,20 +1316,20 @@ http_lookup_type(const char *path) } typedef struct http_file { + char *base; char *path; char *ctype; } http_file; static void -http_handle_file(nni_aio *aio) +http_handle_file(nni_http_conn *conn, void *arg, nni_aio *aio) { - nni_http_handler *h = nni_aio_get_input(aio, 1); - nni_http_res *res = NULL; - void *data; - size_t size; - int rv; - http_file *hf = nni_http_handler_get_data(h); - const char *ctype; + nni_http_res *res = nng_http_conn_res(conn); + void *data; + size_t size; + int rv; + http_file *hf = arg; + const char *ctype; if ((ctype = hf->ctype) == NULL) { ctype = "application/octet-stream"; @@ -1373,7 +1356,7 @@ http_handle_file(nni_aio *aio) status = NNG_HTTP_STATUS_INTERNAL_SERVER_ERROR; break; } - if ((rv = nni_http_res_alloc_error(&res, status)) != 0) { + if ((rv = nni_http_res_set_error(res, status)) != 0) { nni_aio_finish_error(aio, rv); return; } @@ -1381,11 +1364,9 @@ http_handle_file(nni_aio *aio) nni_aio_finish(aio, 0, 0); return; } - if (((rv = nni_http_res_alloc(&res)) != 0) || - ((rv = nni_http_res_set_header(res, "Content-Type", ctype)) != + if (((rv = nni_http_res_set_header(res, "Content-Type", ctype)) != 0) || ((rv = nni_http_res_copy_data(res, data, size)) != 0)) { - nni_http_res_free(res); nni_free(data, size); nni_aio_finish_error(aio, rv); return; @@ -1405,6 +1386,7 @@ http_file_free(void *arg) if ((hf = arg) != NULL) { nni_strfree(hf->path); nni_strfree(hf->ctype); + nni_strfree(hf->base); NNI_FREE_STRUCT(hf); } } @@ -1457,23 +1439,22 @@ nni_http_handler_init_file( } static void -http_handle_dir(nni_aio *aio) +http_handle_dir(nng_http_conn *conn, void *arg, nng_aio *aio) { - nni_http_req *req = nni_aio_get_input(aio, 0); - nni_http_handler *h = nni_aio_get_input(aio, 1); - nni_http_res *res = NULL; - void *data; - size_t size; - int rv; - 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; - size_t len; - size_t pnsz; - char *pn; + nni_http_req *req = nni_http_conn_req(conn); + nni_http_res *res = nni_http_conn_res(conn); + void *data; + size_t size; + int rv; + http_file *hf = arg; + const char *path = hf->path; + const char *base = hf->base; + const char *uri = nni_http_req_get_uri(req); + const char *ctype; + char *dst; + size_t len; + size_t pnsz; + char *pn; len = strlen(base); if (base[1] != '\0' && // Allows "/" as base @@ -1563,7 +1544,7 @@ http_handle_dir(nni_aio *aio) status = NNG_HTTP_STATUS_INTERNAL_SERVER_ERROR; break; } - if ((rv = nni_http_res_alloc_error(&res, status)) != 0) { + if ((rv = nni_http_res_set_error(res, status)) != 0) { nni_aio_finish_error(aio, rv); return; } @@ -1572,11 +1553,9 @@ http_handle_dir(nni_aio *aio) return; } - if (((rv = nni_http_res_alloc(&res)) != 0) || - ((rv = nni_http_res_set_header(res, "Content-Type", ctype)) != + if (((rv = nni_http_res_set_header(res, "Content-Type", ctype)) != 0) || ((rv = nni_http_res_copy_data(res, data, size)) != 0)) { - nni_http_res_free(res); nni_free(data, size); nni_aio_finish_error(aio, rv); return; @@ -1600,8 +1579,9 @@ nni_http_handler_init_directory( if ((hf = NNI_ALLOC_STRUCT(hf)) == NULL) { return (NNG_ENOMEM); } - if ((hf->path = nni_strdup(path)) == NULL) { - NNI_FREE_STRUCT(hf); + if (((hf->path = nni_strdup(path)) == NULL) || + ((hf->base = nni_strdup(uri)) == NULL)) { + http_file_free(hf); return (NNG_ENOMEM); } @@ -1621,29 +1601,25 @@ nni_http_handler_init_directory( typedef struct http_redirect { uint16_t code; char *where; + char *from; } http_redirect; static void -http_handle_redirect(nni_aio *aio) +http_handle_redirect(nng_http_conn *conn, void *data, nng_aio *aio) { - nni_http_res *r = NULL; - char *html = NULL; - char *msg = NULL; - char *loc = NULL; - http_redirect *hr; - nni_http_handler *h; - int rv; - nni_http_req *req; - const char *base; - const char *uri; - - req = nni_aio_get_input(aio, 0); - h = nni_aio_get_input(aio, 1); - base = nni_http_handler_get_uri(h); // base uri + nni_http_res *res = nng_http_conn_res(conn); + nni_http_req *req = nng_http_conn_req(conn); + char *html = NULL; + char *msg = NULL; + char *loc = NULL; + http_redirect *hr = data; + int rv; + const char *base; + const char *uri; + + base = hr->from; // base uri uri = nni_http_req_get_uri(req); - hr = nni_http_handler_get_data(h); - // If we are doing a full tree, then include the entire suffix. if (strncmp(uri, base, strlen(base)) == 0) { rv = nni_asprintf(&loc, "%s%s", hr->where, uri + strlen(base)); @@ -1664,31 +1640,31 @@ http_handle_redirect(nni_aio *aio) // because it is probably going to another server. This also // keeps us from having to consume the entity body, we can just // discard it. - if ((rv != 0) || ((rv = nni_http_res_alloc(&r)) != 0) || + if ((rv != 0) || ((rv = nni_http_alloc_html_error(&html, hr->code, msg)) != 0) || - ((rv = nni_http_res_set_header(r, "Connection", "close")) != 0) || + ((rv = nni_http_res_set_header(res, "Connection", "close")) != + 0) || ((rv = nni_http_res_set_header( - r, "Content-Type", "text/html; charset=UTF-8")) != 0) || - ((rv = nni_http_res_set_header(r, "Location", loc)) != 0) || - ((rv = nni_http_res_copy_data(r, html, strlen(html))) != 0)) { + res, "Content-Type", "text/html; charset=UTF-8")) != 0) || + ((rv = nni_http_res_set_header(res, "Location", loc)) != 0) || + ((rv = nni_http_res_copy_data(res, html, strlen(html))) != 0)) { if (loc != hr->where) { nni_strfree(loc); } nni_strfree(msg); nni_strfree(html); - nni_http_res_free(r); nni_aio_finish_error(aio, rv); return; } - nni_http_res_set_status(r, hr->code); + nni_http_res_set_status(res, hr->code); if (loc != hr->where) { nni_strfree(loc); } nni_strfree(msg); nni_strfree(html); - nni_aio_set_output(aio, 0, r); + nni_aio_set_output(aio, 0, res); nni_aio_finish(aio, 0, 0); } @@ -1699,6 +1675,7 @@ http_redirect_free(void *arg) if ((hr = arg) != NULL) { nni_strfree(hr->where); + nni_strfree(hr->from); NNI_FREE_STRUCT(hr); } } @@ -1714,8 +1691,9 @@ nni_http_handler_init_redirect(nni_http_handler **hpp, const char *uri, if ((hr = NNI_ALLOC_STRUCT(hr)) == NULL) { return (NNG_ENOMEM); } - if ((hr->where = nni_strdup(where)) == NULL) { - NNI_FREE_STRUCT(hr); + if (((hr->where = nni_strdup(where)) == NULL) || + ((hr->from = nni_strdup(uri)) == NULL)) { + http_redirect_free(hr); return (NNG_ENOMEM); } if (status == 0) { @@ -1747,25 +1725,21 @@ typedef struct http_static { } http_static; static void -http_handle_static(nni_aio *aio) +http_handle_static(nng_http_conn *conn, void *data, nni_aio *aio) { - http_static *hs; - const char *ctype; - nni_http_handler *h; - nni_http_res *r = NULL; - int rv; - - h = nni_aio_get_input(aio, 1); - hs = nni_http_handler_get_data(h); + http_static *hs = data; + const char *ctype; + nni_http_res *r = NULL; + int rv; if ((ctype = hs->ctype) == NULL) { ctype = "application/octet-stream"; } - if (((rv = nni_http_res_alloc(&r)) != 0) || - ((rv = nni_http_res_set_header(r, "Content-Type", ctype)) != 0) || + r = nng_http_conn_res(conn); + nng_http_res_reset(r); + if (((rv = nni_http_res_set_header(r, "Content-Type", ctype)) != 0) || ((rv = nni_http_res_set_data(r, hs->data, hs->size)) != 0)) { - nni_http_res_free(r); nni_aio_finish_error(aio, rv); return; } diff --git a/src/supplemental/http/http_server_test.c b/src/supplemental/http/http_server_test.c index 37c45f14..91411499 100644 --- a/src/supplemental/http/http_server_test.c +++ b/src/supplemental/http/http_server_test.c @@ -10,6 +10,7 @@ // // Basic HTTP server tests. +#include "core/defs.h" #include #include @@ -141,21 +142,20 @@ fail: } static void -httpecho(nng_aio *aio) +httpecho(nng_http_conn *conn, void *arg, nng_aio *aio) { - nng_http_req *req = nng_aio_get_input(aio, 0); - nng_http_res *res; + nng_http_req *req = nng_http_conn_req(conn); + nng_http_res *res = nng_http_conn_res(conn); int rv; void *body; size_t len; + NNI_ARG_UNUSED(arg); nng_http_req_get_data(req, &body, &len); - if (((rv = nng_http_res_alloc(&res)) != 0) || - ((rv = nng_http_res_copy_data(res, body, len)) != 0) || + if (((rv = nng_http_res_copy_data(res, body, len)) != 0) || ((rv = nng_http_res_set_header( res, "Content-type", "text/plain")) != 0)) { - nng_http_res_free(res); nng_aio_finish(aio, rv); return; } diff --git a/src/supplemental/websocket/websocket.c b/src/supplemental/websocket/websocket.c index 84d3fd72..70323b0c 100644 --- a/src/supplemental/websocket/websocket.c +++ b/src/supplemental/websocket/websocket.c @@ -1,5 +1,5 @@ // -// Copyright 2024 Staysail Systems, Inc. +// Copyright 2025 Staysail Systems, Inc. // Copyright 2018 Capitar IT Group BV // Copyright 2019 Devolutions // @@ -1243,12 +1243,6 @@ ws_fini(void *arg) if (ws->http) { nni_http_conn_fini(ws->http); } - if (ws->req) { - nni_http_req_free(ws->req); - } - if (ws->res) { - nni_http_res_free(ws->res); - } nni_strfree(ws->reqhdrs); nni_strfree(ws->reshdrs); @@ -1332,9 +1326,7 @@ ws_http_cb_dialer(nni_ws *ws, nni_aio *aio) // If we have no response structure, then this was completion // of sending the request. Prepare an empty response, and read it. if (ws->res == NULL) { - if ((rv = nni_http_res_alloc(&ws->res)) != 0) { - goto err; - } + ws->res = nni_http_conn_res(ws->http); nni_http_read_res(ws->http, ws->res, &ws->httpaio); nni_mtx_unlock(&d->mtx); return; @@ -1514,25 +1506,19 @@ ws_listener_free(void *arg) } static void -ws_handler(nni_aio *aio) +ws_handler(nng_http_conn *conn, void *arg, nng_aio *aio) { - nni_ws_listener *l; - nni_ws *ws; - nni_http_conn *conn; - nni_http_req *req; - nni_http_res *res; - nni_http_handler *h; - const char *ptr; - const char *proto; - uint16_t status; - int rv; - char key[29]; - ws_header *hdr; - - req = nni_aio_get_input(aio, 0); - h = nni_aio_get_input(aio, 1); - conn = nni_aio_get_input(aio, 2); - l = nni_http_handler_get_data(h); + nni_ws_listener *l = arg; + ; + nni_http_req *req = nng_http_conn_req(conn); + nni_http_res *res = nng_http_conn_res(conn); + nni_ws *ws; + const char *ptr; + const char *proto; + uint16_t status; + int rv; + char key[29]; + ws_header *hdr; nni_mtx_lock(&l->mtx); if (l->closed) { @@ -1595,24 +1581,16 @@ ws_handler(nni_aio *aio) goto err; } - if ((rv = nni_http_res_alloc(&res)) != 0) { - // Give a chance to reply to client. - status = NNG_HTTP_STATUS_INTERNAL_SERVER_ERROR; - goto err; - } - nni_http_res_set_status(res, NNG_HTTP_STATUS_SWITCHING); if ((SETH("Connection", "Upgrade") != 0) || (SETH("Upgrade", "websocket") != 0) || (SETH("Sec-WebSocket-Accept", key) != 0)) { status = NNG_HTTP_STATUS_INTERNAL_SERVER_ERROR; - nni_http_res_free(res); goto err; } if ((proto != NULL) && (SETH("Sec-WebSocket-Protocol", proto) != 0)) { status = NNG_HTTP_STATUS_INTERNAL_SERVER_ERROR; - nni_http_res_free(res); goto err; } @@ -1621,7 +1599,6 @@ ws_handler(nni_aio *aio) NNI_LIST_FOREACH (&l->headers, hdr) { if (SETH(hdr->name, hdr->value) != 0) { status = NNG_HTTP_STATUS_INTERNAL_SERVER_ERROR; - nni_http_res_free(res); goto err; } } @@ -1633,7 +1610,6 @@ ws_handler(nni_aio *aio) if (l->hookfn != NULL) { rv = l->hookfn(l->hookarg, req, res); if (rv != 0) { - nni_http_res_free(res); nni_aio_finish_error(aio, rv); nni_mtx_unlock(&l->mtx); return; @@ -1649,7 +1625,6 @@ ws_handler(nni_aio *aio) // the hook can also give back various other // headers, but it would be bad for it to alter // the websocket mandated headers.) - nni_http_req_free(req); nni_aio_set_output(aio, 0, res); nni_aio_finish(aio, 0, 0); nni_mtx_unlock(&l->mtx); @@ -1663,8 +1638,6 @@ ws_handler(nni_aio *aio) // We are good to go, provided we can get the websocket struct, // and send the reply. if ((rv = ws_init(&ws)) != 0) { - nni_http_req_free(req); - nni_http_res_free(res); status = NNG_HTTP_STATUS_INTERNAL_SERVER_ERROR; goto err; } @@ -1681,7 +1654,7 @@ ws_handler(nni_aio *aio) ws->listener = l; nni_list_append(&l->reply, ws); - nni_http_write_res(conn, res, &ws->httpaio); + nni_http_write_res(conn, &ws->httpaio); (void) nni_http_hijack(conn); nni_aio_set_output(aio, 0, NULL); nni_aio_finish(aio, 0, 0); @@ -1689,7 +1662,7 @@ ws_handler(nni_aio *aio) return; err: - if ((rv = nni_http_res_alloc_error(&res, status)) != 0) { + if ((rv = nni_http_res_set_error(res, status)) != 0) { nni_aio_finish_error(aio, rv); } else { nni_aio_set_output(aio, 0, res); @@ -2174,7 +2147,6 @@ ws_conn_cb(void *arg) nni_ws_dialer *d; nni_ws *ws; nni_aio *uaio; - nni_http_conn *http; nni_http_req *req = NULL; int rv; uint8_t raw[16]; @@ -2206,13 +2178,12 @@ ws_conn_cb(void *arg) return; } + ws->http = nni_aio_get_output(&ws->connaio, 0); nni_mtx_lock(&ws->mtx); uaio = ws->useraio; - http = nni_aio_get_output(&ws->connaio, 0); nni_aio_set_output(&ws->connaio, 0, NULL); if (uaio == NULL) { // This request was canceled for some reason. - nni_http_conn_fini(http); nni_mtx_unlock(&ws->mtx); ws_reap(ws); return; @@ -2224,8 +2195,10 @@ ws_conn_cb(void *arg) nni_base64_encode(raw, 16, wskey, 24); wskey[24] = '\0'; + req = nni_http_conn_req(ws->http); + #define SETH(h, v) nni_http_req_set_header(req, h, v) - if ((rv != 0) || ((rv = nni_http_req_alloc(&req, d->url)) != 0) || + if ((rv != 0) || ((rv = nni_http_req_set_url(req, d->url)) != 0) || ((rv = SETH("Upgrade", "websocket")) != 0) || ((rv = SETH("Connection", "Upgrade")) != 0) || ((rv = SETH("Sec-WebSocket-Key", wskey)) != 0) || @@ -2245,22 +2218,15 @@ ws_conn_cb(void *arg) } #undef SETH - ws->http = http; - ws->req = req; + ws->req = req; - nni_http_write_req(http, req, &ws->httpaio); + nni_http_write_req(ws->http, req, &ws->httpaio); nni_mtx_unlock(&ws->mtx); return; err: nni_aio_finish_error(uaio, rv); nni_mtx_unlock(&ws->mtx); - if (http != NULL) { - nni_http_conn_fini(http); - } - if (req != NULL) { - nni_http_req_free(req); - } ws_reap(ws); } -- cgit v1.2.3-70-g09d2