diff --git a/demo/http_client/http_client.c b/demo/http_client/http_client.c index 470589a9c..b2915fbaf 100644 --- a/demo/http_client/http_client.c +++ b/demo/http_client/http_client.c @@ -52,7 +52,6 @@ main(int argc, char **argv) nng_http_conn *conn; nng_url *url; nng_aio *aio; - nng_http_req *req; nng_http_res *res; const char *hdr; int rv; @@ -86,7 +85,6 @@ main(int argc, char **argv) // Get the connection, at the 0th output. conn = nng_aio_get_output(aio, 0); res = nng_http_conn_res(conn); - req = nng_http_conn_req(conn); // Request is already set up with URL, and for GET via HTTP/1.1. // The Host: header is already set up too. diff --git a/docs/ref/api/http.md b/docs/ref/api/http.md index fba3e8970..807da29b5 100644 --- a/docs/ref/api/http.md +++ b/docs/ref/api/http.md @@ -173,6 +173,27 @@ including any disposing of any underlying file descriptors or related resources. Once this function, no further access to the _conn_ structure may be made. +### Reset Connection State + +```c +void nng_http_reset(nng_http *conn); +``` + +The {{i:`nng_http_reset`}} function resets the request and response state of the +the connection _conn_. + +The "Host" parameter will be retained for client connections, but the URI will not. + +The intended purpose of this function is to clear the object state before reusing the _conn_ for +subsequent transactions. + +### Request and Response Headers + +```c +int nng_http_request_add_header(nng_http *conn, const char *key, const char *val); +int nng_http_request_set_header(nng_http *conn, const char *key, const char *val); +``` + ### Direct Read and Write ```c diff --git a/docs/ref/xref.md b/docs/ref/xref.md index f945e751c..327c073e3 100644 --- a/docs/ref/xref.md +++ b/docs/ref/xref.md @@ -253,6 +253,7 @@ [`nng_http_client_set_tls`]: /api/http.md#client-tls [`nng_http_client_get_tls`]: /api/http.md#client-tls [`nng_http_close`]: /api/http.md#closing-connections +[`nng_http_reset`]: /api/http.md#reset-connection-state [`nng_http_get_version`]: /api/http.md#http-protocol-versions [`nng_http_set_version`]: /api/http.md#http-protocol-versions [`nng_http_get_method`]: /api/http.md#http-method diff --git a/include/nng/http.h b/include/nng/http.h index fafd498a2..e3d580c7a 100644 --- a/include/nng/http.h +++ b/include/nng/http.h @@ -100,16 +100,6 @@ typedef struct nng_http_req nng_http_req; // the scheme, host, or port. NNG_DECL const char *nng_http_req_get_uri(const nng_http_req *); -// nng_http_req_set_header sets an HTTP header, replacing any previous value -// that might have been present. -NNG_DECL int nng_http_req_set_header( - nng_http_req *, const char *, const char *); - -// nng_http_req_add_header adds an HTTP header, without disrupting any other -// with the same name that might have been present. -NNG_DECL int nng_http_req_add_header( - nng_http_req *, const char *, const char *); - // nng_http_req_del_header deletes all occurrences of a named header. NNG_DECL int nng_http_req_del_header(nng_http_req *, const char *); @@ -209,7 +199,7 @@ NNG_DECL void nng_http_write(nng_http *, nng_aio *); // nng_http_write_all is like nng_http_write, but it does not // finish until either all the requested data is written, or an error occurs. -NNG_DECL void nng_http_conn_write_all(nng_http *, nng_aio *); +NNG_DECL void nng_http_write_all(nng_http *, nng_aio *); // nng_http_conn_write_req writes the entire request. It will also write any // data that has been attached. @@ -261,6 +251,9 @@ NNG_DECL void nng_http_set_method(nng_http *, const char *); // nng_http_get_method returns the method. NNG_DECL const char *nng_http_get_method(nng_http *); +NNG_DECL int nng_http_add_request_header( + nng_http *, const char *, const char *); + // nng_http_handler is a handler used on the server side to handle HTTP // requests coming into a specific URL. // diff --git a/src/supplemental/http/http_api.h b/src/supplemental/http/http_api.h index f600397c3..b564e7fb3 100644 --- a/src/supplemental/http/http_api.h +++ b/src/supplemental/http/http_api.h @@ -392,6 +392,13 @@ extern int nni_http_conn_set_redirect( nng_http *conn, uint16_t status, const char *reason, const char *dest); extern void nni_http_conn_set_response_content_type( - nng_http_conn *conn, const char *ctype); + nng_http *conn, const char *ctype); + +extern void nni_http_conn_set_host(nng_http *conn, const char *); +extern void nni_http_conn_reset(nng_http *conn); +extern int nni_http_add_request_header( + nng_http *conn, const char *key, const char *val); +extern int nni_http_set_request_header( + nng_http *conn, const char *key, const char *val); #endif // NNG_SUPPLEMENTAL_HTTP_HTTP_API_H diff --git a/src/supplemental/http/http_client.c b/src/supplemental/http/http_client.c index ec82472e9..6c69c8e11 100644 --- a/src/supplemental/http/http_client.c +++ b/src/supplemental/http/http_client.c @@ -10,12 +10,14 @@ // #include +#include #include #include #include "core/nng_impl.h" #include "http_api.h" +#include "http_msg.h" static nni_mtx http_txn_lk = NNI_MTX_INITIALIZER; @@ -24,6 +26,7 @@ struct nng_http_client { nni_mtx mtx; bool closed; nni_aio aio; + char host[260]; nng_stream_dialer *dialer; }; @@ -71,6 +74,8 @@ http_dial_cb(void *arg) NNI_ASSERT(stream != NULL); rv = nni_http_conn_init(&conn, stream); + + // set up the host header http_dial_start(c); nni_mtx_unlock(&c->mtx); @@ -79,7 +84,7 @@ http_dial_cb(void *arg) nni_aio_finish_error(aio, rv); return; } - + nni_http_conn_set_host(conn, c->host); nni_aio_set_output(aio, 0, conn); nni_aio_finish(aio, 0, 0); } @@ -110,7 +115,8 @@ nni_http_client_init(nni_http_client **cp, const nng_url *url) memcpy(&my_url, url, sizeof(my_url)); my_url.u_scheme = (char *) scheme; - if (strlen(url->u_hostname) == 0) { + if ((strlen(url->u_hostname) == 0) || + (strlen(url->u_hostname) > 253)) { // We require a valid hostname. return (NNG_EADDRINVAL); } @@ -122,6 +128,16 @@ nni_http_client_init(nni_http_client **cp, const nng_url *url) nni_aio_list_init(&c->aios); nni_aio_init(&c->aio, http_dial_cb, c); + if (nni_url_default_port(url->u_scheme) == url->u_port) { + snprintf(c->host, sizeof(c->host), "%s", url->u_hostname); + } else if (strchr(url->u_hostname, ':') != NULL) { + // IPv6 address, needs [wrapping] + snprintf(c->host, sizeof(c->host), "[%s]:%d", url->u_hostname, + url->u_port); + } else { + snprintf(c->host, sizeof(c->host), "%s:%d", url->u_hostname, + url->u_port); + } if ((rv = nng_stream_dialer_alloc_url(&c->dialer, &my_url)) != 0) { nni_http_client_fini(c); return (rv); diff --git a/src/supplemental/http/http_conn.c b/src/supplemental/http/http_conn.c index ab13e376e..9fff795fc 100644 --- a/src/supplemental/http/http_conn.c +++ b/src/supplemental/http/http_conn.c @@ -511,12 +511,22 @@ http_wr_submit(nni_http_conn *conn, nni_aio *aio, enum write_flavor flavor) } } +void +nni_http_conn_reset(nng_http_conn *conn) +{ + nni_http_req_reset(&conn->req); + nni_http_res_reset(&conn->res); + if (strlen(conn->req.host)) { + nni_http_conn_set_host(conn, conn->req.host); + } +} + void nni_http_read_req(nni_http_conn *conn, nni_aio *aio) { // clear the sent flag (used for the server) conn->res_sent = false; - nni_http_req_reset(&conn->req); + nni_http_conn_reset(conn); nni_mtx_lock(&conn->mtx); http_rd_submit(conn, aio, HTTP_RD_REQ); nni_mtx_unlock(&conn->mtx); @@ -939,6 +949,51 @@ nni_http_conn_set_redirect( return (http_conn_set_error(conn, status, reason, NULL, redirect)); } +void +nni_http_conn_set_host(nng_http_conn *conn, const char *host) +{ + if (host != conn->req.host) { + snprintf(conn->req.host, sizeof(conn->req.host), "%s", host); + } + nni_list_node_remove(&conn->req.host_header.node); + conn->req.host_header.name = "Host"; + conn->req.host_header.value = conn->req.host; + conn->req.host_header.static_name = true; + conn->req.host_header.static_value = true; + conn->req.host_header.alloc_header = false; + nni_list_prepend(&conn->req.hdrs, &conn->req.host_header); +} + +static bool +http_set_request_known_header(nng_http *conn, const char *key, const char *val) +{ + if (nni_strcasecmp(key, "Host") == 0) { + nni_http_conn_set_host(conn, val); + return (true); + } + return (false); +} + +int +nni_http_add_request_header(nng_http *conn, const char *key, const char *val) +{ + if (http_set_request_known_header(conn, key, val)) { + return (0); + } + + return (nni_http_req_add_header(&conn->req, key, val)); +} + +int +nni_http_set_request_header(nng_http *conn, const char *key, const char *val) +{ + if (http_set_request_known_header(conn, key, val)) { + return (0); + } + + return (nni_http_req_set_header(&conn->req, key, val)); +} + void nni_http_conn_set_response_content_type(nng_http *conn, const char *ctype) { @@ -976,8 +1031,7 @@ nni_http_conn_fini(nni_http_conn *conn) nni_aio_fini(&conn->wr_aio); nni_aio_fini(&conn->rd_aio); - nni_http_req_reset(&conn->req); - nni_http_res_reset(&conn->res); + nni_http_conn_reset(conn); nni_free(conn->rd_buf, conn->rd_bufsz); nni_mtx_fini(&conn->mtx); NNI_FREE_STRUCT(conn); diff --git a/src/supplemental/http/http_msg.c b/src/supplemental/http/http_msg.c index a77fc9ef4..705fa761e 100644 --- a/src/supplemental/http/http_msg.c +++ b/src/supplemental/http/http_msg.c @@ -113,37 +113,41 @@ http_del_header(nni_list *hdrs, const char *key) return (NNG_ENOENT); } -static void -http_del_all_headers(nni_list *hdrs, const char *key) -{ - while (http_del_header(hdrs, key) == 0) { - continue; - } -} - int nni_http_req_del_header(nni_http_req *req, const char *key) { - return (http_del_header(&req->hdrs, key)); + int rv = NNG_ENOENT; + while (http_del_header(&req->hdrs, key) == 0) { + rv = 0; + } + return (rv); } int nni_http_res_del_header(nni_http_res *res, const char *key) { - return (http_del_header(&res->hdrs, key)); + int rv = NNG_ENOENT; + while (http_del_header(&res->hdrs, key) == 0) { + rv = 0; + } + return (rv); } static int http_set_header(nni_list *hdrs, const char *key, const char *val) { http_header *h; + NNI_LIST_FOREACH (hdrs, h) { if (nni_strcasecmp(key, h->name) == 0) { char *news; if ((news = nni_strdup(val)) == NULL) { return (NNG_ENOMEM); } - nni_strfree(h->value); + if (!h->static_value) { + nni_strfree(h->value); + h->value = NULL; + } h->value = news; return (0); } @@ -309,7 +313,7 @@ void nni_http_req_set_content_length(nni_http_req *req, size_t size) { snprintf(req->clen, sizeof(req->clen), "%lu", (unsigned long) size); - http_del_all_headers(&req->hdrs, "Content-Length"); + nni_http_req_del_header(req, "Content-Length"); nni_list_node_remove(&req->content_length.node); req->content_length.name = "Content-Length"; req->content_length.value = req->clen; @@ -322,7 +326,7 @@ void nni_http_res_set_content_length(nni_http_res *res, size_t size) { snprintf(res->clen, sizeof(res->clen), "%lu", (unsigned long) size); - http_del_all_headers(&res->hdrs, "Content-Length"); + nni_http_res_del_header(res, "Content-Length"); nni_list_node_remove(&res->content_length.node); res->content_length.name = "Content-Length"; res->content_length.value = res->clen; @@ -334,7 +338,7 @@ nni_http_res_set_content_length(nni_http_res *res, size_t size) void nni_http_res_set_content_type(nni_http_res *res, const char *ctype) { - http_del_all_headers(&res->hdrs, "Content-Type"); + nni_http_res_del_header(res, "Content-Type"); nni_list_node_remove(&res->content_type.node); res->content_type.name = "Content-Type"; res->content_type.value = (char *) ctype; @@ -346,7 +350,7 @@ nni_http_res_set_content_type(nni_http_res *res, const char *ctype) void nni_http_req_set_content_type(nni_http_req *req, const char *ctype) { - http_del_all_headers(&req->hdrs, "Content-Type"); + nni_http_req_del_header(req, "Content-Type"); nni_list_node_remove(&req->content_type.node); req->content_type.name = "Content-Type"; req->content_type.value = (char *) ctype; diff --git a/src/supplemental/http/http_msg.h b/src/supplemental/http/http_msg.h index a9b340d54..8bec08963 100644 --- a/src/supplemental/http/http_msg.h +++ b/src/supplemental/http/http_msg.h @@ -38,14 +38,16 @@ struct nng_http_req { nni_list hdrs; nni_http_entity data; char meth[32]; + char clen[28]; + char host[260]; // 253 per IETF, plus 6 for :port plus null char *uri; const char *vers; char *buf; size_t bufsz; bool parsed; - char clen[28]; http_header content_type; http_header content_length; + http_header host_header; }; struct nng_http_res { diff --git a/src/supplemental/http/http_public.c b/src/supplemental/http/http_public.c index cf6d78dae..735b832f9 100644 --- a/src/supplemental/http/http_public.c +++ b/src/supplemental/http/http_public.c @@ -40,12 +40,12 @@ nng_http_res_get_header(const nng_http_res *res, const char *key) } int -nng_http_req_add_header(nng_http_req *req, const char *key, const char *val) +nng_http_add_request_header(nng_http *conn, const char *key, const char *val) { #ifdef NNG_SUPP_HTTP - return (nni_http_req_add_header(req, key, val)); + return (nni_http_add_request_header(conn, key, val)); #else - NNI_ARG_UNUSED(req); + NNI_ARG_UNUSED(conn); NNI_ARG_UNUSED(key); NNI_ARG_UNUSED(val); return (NNG_ENOTSUP); @@ -53,12 +53,12 @@ nng_http_req_add_header(nng_http_req *req, const char *key, const char *val) } int -nng_http_res_add_header(nng_http_res *res, const char *key, const char *val) +nng_http_set_request_header(nng_http *conn, const char *key, const char *val) { #ifdef NNG_SUPP_HTTP - return (nni_http_res_add_header(res, key, val)); + return (nni_http_set_request_header(conn, key, val)); #else - NNI_ARG_UNUSED(res); + NNI_ARG_UNUSED(conn); NNI_ARG_UNUSED(key); NNI_ARG_UNUSED(val); return (NNG_ENOTSUP); @@ -66,12 +66,12 @@ nng_http_res_add_header(nng_http_res *res, const char *key, const char *val) } int -nng_http_req_set_header(nng_http_req *req, const char *key, const char *val) +nng_http_res_add_header(nng_http_res *res, const char *key, const char *val) { #ifdef NNG_SUPP_HTTP - return (nni_http_req_set_header(req, key, val)); + return (nni_http_res_add_header(res, key, val)); #else - NNI_ARG_UNUSED(req); + NNI_ARG_UNUSED(res); NNI_ARG_UNUSED(key); NNI_ARG_UNUSED(val); return (NNG_ENOTSUP); @@ -791,8 +791,7 @@ void nng_http_reset(nng_http *conn) { #ifdef NNG_SUPP_HTTP - nni_http_req_reset(nni_http_conn_req(conn)); - nni_http_res_reset(nni_http_conn_res(conn)); + nni_http_conn_reset(conn); #else NNI_ARG_UNUSED(req); #endif diff --git a/tests/httpclient.c b/tests/httpclient.c index b3c37c47a..9526ee357 100644 --- a/tests/httpclient.c +++ b/tests/httpclient.c @@ -183,7 +183,7 @@ TestMain("HTTP Client", { nng_url *url; nng_http_req *req; nng_http_res *res; - nng_http_conn *conn; + nng_http *conn; So(nng_aio_alloc(&aio, NULL, NULL) == 0); @@ -203,8 +203,8 @@ TestMain("HTTP Client", { }); nng_aio_set_timeout(aio, 10); // 10 msec timeout - So(nng_http_req_set_header(req, "Cache-Control", "no-cache") == - 0); + So(nng_http_set_request_header( + conn, "Cache-Control", "no-cache") == 0); nng_http_conn_transact(conn, aio); nng_aio_wait(aio); So(nng_aio_result(aio) == NNG_ETIMEDOUT);