aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2025-01-05 11:47:03 -0800
committerGarrett D'Amore <garrett@damore.org>2025-01-05 22:39:17 -0800
commitbce6a79fc55852032e9d653b099a121353aaa238 (patch)
tree6e6aa44e73ea7e671a50103539c55443df8c4dc2
parentd31844817a5b304a894c5b963cd52aeb9e47c627 (diff)
downloadnng-bce6a79fc55852032e9d653b099a121353aaa238.tar.gz
nng-bce6a79fc55852032e9d653b099a121353aaa238.tar.bz2
nng-bce6a79fc55852032e9d653b099a121353aaa238.zip
http: changing transaction API to inline req and res structures
This is a step towards simplifying this API and ultimately simplifying the HTTP callback API used for the server side.
-rw-r--r--include/nng/supplemental/http/http.h13
-rw-r--r--src/supplemental/http/http_api.h13
-rw-r--r--src/supplemental/http/http_client.c11
-rw-r--r--src/supplemental/http/http_conn.c20
-rw-r--r--src/supplemental/http/http_msg.c143
-rw-r--r--src/supplemental/http/http_msg.h57
-rw-r--r--src/supplemental/http/http_public.c39
-rw-r--r--tests/httpclient.c26
8 files changed, 211 insertions, 111 deletions
diff --git a/include/nng/supplemental/http/http.h b/include/nng/supplemental/http/http.h
index f00c63a9..53f8436c 100644
--- a/include/nng/supplemental/http/http.h
+++ b/include/nng/supplemental/http/http.h
@@ -1,5 +1,5 @@
//
-// Copyright 2024 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2025 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
// Copyright 2020 Dirac Research <robert.bielik@dirac.com>
//
@@ -143,6 +143,9 @@ NNG_DECL void nng_http_req_set_method(nng_http_req *, const char *);
// not support HTTP/2 at all. Null sets the default ("HTTP/1.1").
NNG_DECL int nng_http_req_set_version(nng_http_req *, const char *);
+// nng_http_req_set_url is used to change the URL of a request.
+NNG_DECL int nng_http_req_set_url(nng_http_req *, const nng_url *);
+
// nng_http_req_set_uri is used to change the URI of a request. This
// should be an "abs-uri", that is a path, plus query and fragment if
// needed. The scheme, host, and port don't belong here. The URI should
@@ -239,6 +242,11 @@ NNG_DECL int nng_http_res_copy_data(nng_http_res *, const void *, size_t);
// normally only used for exchanging HTTP requests and responses.
typedef struct nng_http_conn nng_http_conn;
+// These methods obtain a pointer to the request or response structure
+// that is embedded in the conn structure.
+NNG_DECL nng_http_req *nng_http_conn_req(nng_http_conn *);
+NNG_DECL nng_http_res *nng_http_conn_res(nng_http_conn *);
+
// nng_http_conn_close closes the underlying channel. Applications should
// not use this channel after this operation is performed.
NNG_DECL void nng_http_conn_close(nng_http_conn *);
@@ -526,8 +534,7 @@ NNG_DECL void nng_http_client_connect(nng_http_client *, nng_aio *);
// single HTTP transaction). It will not automatically close the connection,
// unless some kind of significant error occurs. The caller should close
// the connection if the aio does not complete successfully.
-NNG_DECL void nng_http_conn_transact(
- nng_http_conn *, nng_http_req *, nng_http_res *, nng_aio *);
+NNG_DECL void nng_http_conn_transact(nng_http_conn *, nng_aio *);
// nng_http_client_transact is used to execute a single transaction to a
// server. The connection is opened, and will be closed when the transaction is
diff --git a/src/supplemental/http/http_api.h b/src/supplemental/http/http_api.h
index 29a8c5c1..a6dfa6ca 100644
--- a/src/supplemental/http/http_api.h
+++ b/src/supplemental/http/http_api.h
@@ -34,13 +34,14 @@ typedef struct nng_http_chunks nni_http_chunks;
extern const char *nni_http_reason(uint16_t);
-extern int nni_http_req_init(nni_http_req **);
+extern void 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 void nni_http_res_init(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 *);
@@ -74,6 +75,9 @@ extern int nni_http_chunks_parse(nni_http_chunks *, void *, size_t, size_t *);
extern void nni_http_read_chunks(
nni_http_conn *, nni_http_chunks *, nni_aio *);
+extern nni_http_req *nni_http_conn_req(nni_http_conn *);
+extern nni_http_res *nni_http_conn_res(nni_http_conn *);
+
// 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 *);
@@ -137,6 +141,7 @@ extern const char *nni_http_req_get_uri(const nni_http_req *);
extern void 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 int nni_http_req_set_url(nni_http_req *, const nng_url *);
extern uint16_t nni_http_res_get_status(const nni_http_res *);
extern void nni_http_res_set_status(nni_http_res *, uint16_t);
extern const char *nni_http_res_get_version(const nni_http_res *);
@@ -375,9 +380,9 @@ extern void nni_http_client_connect(nni_http_client *, nni_aio *);
// unless some kind of significant error occurs. The caller should dispose
// of the connection if the aio does not complete successfully.
// Note that this will fail with NNG_ENOTSUP if the server attempts to reply
-// with a chunked transfer encoding.
-extern void nni_http_transact_conn(
- nni_http_conn *, nni_http_req *, nni_http_res *, nni_aio *);
+// with a chunked transfer encoding. The request and response used are the
+// ones associated with the connection.
+extern void nni_http_transact_conn(nni_http_conn *, nni_aio *);
// nni_http_transact is used to execute a single transaction to a server.
// The connection is opened, and will be closed when the transaction is
diff --git a/src/supplemental/http/http_client.c b/src/supplemental/http/http_client.c
index 52b6874d..8701a96b 100644
--- a/src/supplemental/http/http_client.c
+++ b/src/supplemental/http/http_client.c
@@ -361,8 +361,7 @@ http_txn_cancel(nni_aio *aio, void *arg, int rv)
// for Chunked Transfer Encoding is missing. Note that cancelling the aio
// is generally fatal to the connection.
void
-nni_http_transact_conn(
- nni_http_conn *conn, nni_http_req *req, nni_http_res *res, nni_aio *aio)
+nni_http_transact_conn(nni_http_conn *conn, nni_aio *aio)
{
http_txn *txn;
int rv;
@@ -380,10 +379,12 @@ nni_http_transact_conn(
nni_aio_list_init(&txn->aios);
txn->client = NULL;
txn->conn = conn;
- txn->req = req;
- txn->res = res;
+ txn->req = nni_http_conn_req(conn);
+ txn->res = nni_http_conn_res(conn);
txn->state = HTTP_SENDING;
+ nni_http_res_reset(txn->res);
+
nni_mtx_lock(&http_txn_lk);
if (!nni_aio_start(aio, http_txn_cancel, txn)) {
nni_mtx_unlock(&http_txn_lk);
@@ -392,7 +393,7 @@ nni_http_transact_conn(
}
nni_http_res_reset(txn->res);
nni_list_append(&txn->aios, aio);
- nni_http_write_req(conn, req, txn->aio);
+ nni_http_write_req(conn, txn->req, txn->aio);
nni_mtx_unlock(&http_txn_lk);
}
diff --git a/src/supplemental/http/http_conn.c b/src/supplemental/http/http_conn.c
index 44860fe6..912f2cc9 100644
--- a/src/supplemental/http/http_conn.c
+++ b/src/supplemental/http/http_conn.c
@@ -17,6 +17,7 @@
#include "supplemental/tls/tls_api.h"
#include "http_api.h"
+#include "http_msg.h"
// We insist that individual headers fit in 8K.
// If you need more than that, you need something we can't do.
@@ -52,6 +53,9 @@ struct nng_http_conn {
nni_mtx mtx;
+ nng_http_req req;
+ nng_http_res res;
+
enum read_flavor rd_flavor;
uint8_t *rd_buf;
size_t rd_get;
@@ -62,6 +66,18 @@ struct nng_http_conn {
enum write_flavor wr_flavor;
};
+nng_http_req *
+nni_http_conn_req(nng_http_conn *conn)
+{
+ return (&conn->req);
+}
+
+nng_http_res *
+nni_http_conn_res(nng_http_conn *conn)
+{
+ return (&conn->res);
+}
+
void
nni_http_conn_set_ctx(nni_http_conn *conn, void *ctx)
{
@@ -651,6 +667,8 @@ 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_free(conn->rd_buf, conn->rd_bufsz);
nni_mtx_fini(&conn->mtx);
NNI_FREE_STRUCT(conn);
@@ -667,6 +685,8 @@ http_init(nni_http_conn **connp, nng_stream *data)
nni_mtx_init(&conn->mtx);
nni_aio_list_init(&conn->rdq);
nni_aio_list_init(&conn->wrq);
+ nni_http_req_init(&conn->req);
+ nni_http_res_init(&conn->res);
if ((conn->rd_buf = nni_alloc(HTTP_BUFSIZE)) == NULL) {
nni_http_conn_fini(conn);
diff --git a/src/supplemental/http/http_msg.c b/src/supplemental/http/http_msg.c
index 44981354..08c594ac 100644
--- a/src/supplemental/http/http_msg.c
+++ b/src/supplemental/http/http_msg.c
@@ -1,5 +1,5 @@
//
-// Copyright 2024 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2025 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
@@ -16,48 +16,9 @@
#include "core/nng_impl.h"
#include "http_api.h"
+#include "http_msg.h"
#include "nng/supplemental/http/http.h"
-// Note that as we parse headers, the rule is that if a header is already
-// present, then we can append it to the existing header, separated by
-// a comma. From experience, for example, Firefox uses a Connection:
-// header with two values, "keepalive", and "upgrade".
-typedef struct http_header {
- char *name;
- char *value;
- nni_list_node node;
-} http_header;
-
-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 nng_http_req {
- nni_list hdrs;
- nni_http_entity data;
- char meth[32];
- char *uri;
- const char *vers;
- char *buf;
- size_t bufsz;
- bool parsed;
-};
-
-struct nng_http_res {
- nni_list hdrs;
- nni_http_entity data;
- uint16_t code;
- char *rsn;
- const char *vers;
- char *buf;
- size_t bufsz;
- bool parsed;
- bool iserr;
-};
-
static int
http_set_string(char **strp, const char *val)
{
@@ -601,13 +562,9 @@ nni_http_res_get_buf(nni_http_res *res, void **data, size_t *szp)
return (0);
}
-int
-nni_http_req_alloc(nni_http_req **reqp, const nng_url *url)
+void
+nni_http_req_init(nni_http_req *req)
{
- nni_http_req *req;
- if ((req = NNI_ALLOC_STRUCT(req)) == NULL) {
- return (NNG_ENOMEM);
- }
NNI_LIST_INIT(&req->hdrs, http_header, node);
req->buf = NULL;
req->bufsz = 0;
@@ -617,34 +574,56 @@ nni_http_req_alloc(nni_http_req **reqp, const nng_url *url)
req->uri = NULL;
nni_http_req_set_version(req, NNG_HTTP_VERSION_1_1);
nni_http_req_set_method(req, "GET");
- if (url != NULL) {
- const char *host;
- char host_buf[264]; // 256 + 8 for port
- int rv;
- rv = nni_asprintf(&req->uri, "%s%s%s%s%s", url->u_path,
- url->u_query ? "?" : "", url->u_query ? url->u_query : "",
- url->u_fragment ? "#" : "",
- url->u_fragment ? url->u_fragment : "");
- if (rv != 0) {
- NNI_FREE_STRUCT(req);
- return (NNG_ENOMEM);
- }
+}
+
+int
+nni_http_req_set_url(nni_http_req *req, const nng_url *url)
+{
+ if (url == NULL) {
+ return (0);
+ }
+ const char *host;
+ char host_buf[264]; // 256 + 8 for port
+ int rv;
+ rv = nni_asprintf(&req->uri, "%s%s%s%s%s", url->u_path,
+ url->u_query ? "?" : "", url->u_query ? url->u_query : "",
+ url->u_fragment ? "#" : "",
+ url->u_fragment ? url->u_fragment : "");
+ if (rv != 0) {
+ 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 (nni_url_default_port(url->u_scheme) == url->u_port) {
- host = url->u_hostname;
+ // 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 (nni_url_default_port(url->u_scheme) == url->u_port) {
+ host = url->u_hostname;
+ } else {
+ if (strchr(url->u_hostname, ':')) {
+ snprintf(host_buf, sizeof(host_buf), "[%s]:%u",
+ url->u_hostname, url->u_port);
} else {
- if (strchr(url->u_hostname, ':')) {
- snprintf(host_buf, sizeof(host_buf), "[%s]:%u",
- url->u_hostname, url->u_port);
- } else {
- snprintf(host_buf, sizeof(host_buf), "%s:%u",
- url->u_hostname, url->u_port);
- }
- host = host_buf;
+ snprintf(host_buf, sizeof(host_buf), "%s:%u",
+ url->u_hostname, url->u_port);
}
- if ((rv = nni_http_req_add_header(req, "Host", host)) != 0) {
+ host = host_buf;
+ }
+ if ((rv = nni_http_req_set_header(req, "Host", host)) != 0) {
+ return (rv);
+ }
+ return (0);
+}
+
+int
+nni_http_req_alloc(nni_http_req **reqp, const nng_url *url)
+{
+ nni_http_req *req;
+ if ((req = NNI_ALLOC_STRUCT(req)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ nni_http_req_init(req);
+ if (url != NULL) {
+ int rv;
+ if ((rv = nni_http_req_set_url(req, url)) != 0) {
nni_http_req_free(req);
return (rv);
}
@@ -653,13 +632,9 @@ nni_http_req_alloc(nni_http_req **reqp, const nng_url *url)
return (0);
}
-int
-nni_http_res_alloc(nni_http_res **resp)
+void
+nni_http_res_init(nni_http_res *res)
{
- nni_http_res *res;
- if ((res = NNI_ALLOC_STRUCT(res)) == NULL) {
- return (NNG_ENOMEM);
- }
NNI_LIST_INIT(&res->hdrs, http_header, node);
res->buf = NULL;
res->bufsz = 0;
@@ -669,7 +644,17 @@ nni_http_res_alloc(nni_http_res **resp)
res->vers = NNG_HTTP_VERSION_1_1;
res->rsn = NULL;
res->code = 0;
- *resp = res;
+}
+
+int
+nni_http_res_alloc(nni_http_res **resp)
+{
+ nni_http_res *res;
+ if ((res = NNI_ALLOC_STRUCT(res)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ nni_http_res_init(res);
+ *resp = res;
return (0);
}
diff --git a/src/supplemental/http/http_msg.h b/src/supplemental/http/http_msg.h
new file mode 100644
index 00000000..7d9e7dcf
--- /dev/null
+++ b/src/supplemental/http/http_msg.h
@@ -0,0 +1,57 @@
+//
+// Copyright 2025 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.
+//
+
+#ifndef NNG_SUPPLEMENTAL_HTTP_HTTP_MSG
+#define NNG_SUPPLEMENTAL_HTTP_HTTP_MSG
+
+#include "core/defs.h"
+#include "core/list.h"
+
+// Note that as we parse headers, the rule is that if a header is already
+// present, then we can append it to the existing header, separated by
+// a comma. From experience, for example, Firefox uses a Connection:
+// header with two values, "keepalive", and "upgrade".
+typedef struct http_header {
+ char *name;
+ char *value;
+ nni_list_node node;
+} http_header;
+
+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 nng_http_req {
+ nni_list hdrs;
+ nni_http_entity data;
+ char meth[32];
+ char *uri;
+ const char *vers;
+ char *buf;
+ size_t bufsz;
+ bool parsed;
+};
+
+struct nng_http_res {
+ nni_list hdrs;
+ nni_http_entity data;
+ uint16_t code;
+ char *rsn;
+ const char *vers;
+ char *buf;
+ size_t bufsz;
+ bool parsed;
+ bool iserr;
+};
+
+#endif
diff --git a/src/supplemental/http/http_public.c b/src/supplemental/http/http_public.c
index 88ebaa68..8a81404e 100644
--- a/src/supplemental/http/http_public.c
+++ b/src/supplemental/http/http_public.c
@@ -303,6 +303,18 @@ nng_http_req_set_version(nng_http_req *req, const char *vers)
}
int
+nng_http_req_set_url(nng_http_req *req, const nng_url *url)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_req_set_url(req, url));
+#else
+ NNI_ARG_UNUSED(req);
+ NNI_ARG_UNUSED(url);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
nng_http_req_set_uri(nng_http_req *req, const char *uri)
{
#ifdef NNG_SUPP_HTTP
@@ -382,6 +394,26 @@ nng_http_res_set_reason(nng_http_res *res, const char *rsn)
#endif
}
+nng_http_req *
+nng_http_conn_req(nng_http_conn *conn)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_conn_req(conn));
+#else
+ return (NULL);
+#endif
+}
+
+nng_http_res *
+nng_http_conn_res(nng_http_conn *conn)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_conn_res(conn));
+#else
+ return (NULL);
+#endif
+}
+
void
nng_http_conn_close(nng_http_conn *conn)
{
@@ -875,15 +907,12 @@ nng_http_client_transact(
}
void
-nng_http_conn_transact(
- nng_http_conn *conn, nng_http_req *req, nng_http_res *res, nng_aio *aio)
+nng_http_conn_transact(nng_http_conn *conn, nng_aio *aio)
{
#ifdef NNG_SUPP_HTTP
- nni_http_transact_conn(conn, req, res, aio);
+ nni_http_transact_conn(conn, aio);
#else
NNI_ARG_UNUSED(conn);
- NNI_ARG_UNUSED(req);
- NNI_ARG_UNUSED(res);
nni_aio_finish_error(aio, NNG_ENOTSUP);
#endif
}
diff --git a/tests/httpclient.c b/tests/httpclient.c
index ca6c521f..f3307fc1 100644
--- a/tests/httpclient.c
+++ b/tests/httpclient.c
@@ -1,5 +1,5 @@
//
-// Copyright 2021 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2025 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
@@ -136,20 +136,13 @@ TestMain("HTTP Client", {
});
Convey("Connection reuse works", {
+ nng_http_res *res;
nng_http_req *req;
- nng_http_res *res1;
- nng_http_res *res2;
void *data;
size_t len;
nng_http_conn *conn = NULL;
- So(nng_http_req_alloc(&req, url) == 0);
- So(nng_http_res_alloc(&res1) == 0);
- So(nng_http_res_alloc(&res2) == 0);
Reset({
- nng_http_req_free(req);
- nng_http_res_free(res1);
- nng_http_res_free(res2);
if (conn != NULL) {
nng_http_conn_close(conn);
}
@@ -160,17 +153,20 @@ TestMain("HTTP Client", {
So(nng_aio_result(aio) == 0);
conn = nng_aio_get_output(aio, 0);
- nng_http_conn_transact(conn, req, res1, aio);
+ req = nng_http_conn_req(conn);
+ res = nng_http_conn_res(conn);
+ So(nng_http_req_set_url(req, url) == 0);
+ nng_http_conn_transact(conn, aio);
nng_aio_wait(aio);
So(nng_aio_result(aio) == 0);
- So(nng_http_res_get_status(res1) == 200);
- nng_http_res_get_data(res1, &data, &len);
+ So(nng_http_res_get_status(res) == 200);
+ nng_http_res_get_data(res, &data, &len);
- nng_http_conn_transact(conn, req, res2, aio);
+ nng_http_conn_transact(conn, aio);
nng_aio_wait(aio);
So(nng_aio_result(aio) == 0);
- So(nng_http_res_get_status(res2) == 200);
- nng_http_res_get_data(res2, &data, &len);
+ So(nng_http_res_get_status(res) == 200);
+ nng_http_res_get_data(res, &data, &len);
});
});