Skip to content

Commit

Permalink
http: header improvements
Browse files Browse the repository at this point in the history
Try harder to avoid some allocations, use Host separately and inherit
from the client.
  • Loading branch information
gdamore committed Jan 8, 2025
1 parent 14b0c42 commit 4fb5e4a
Show file tree
Hide file tree
Showing 11 changed files with 144 additions and 49 deletions.
2 changes: 0 additions & 2 deletions demo/http_client/http_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
21 changes: 21 additions & 0 deletions docs/ref/api/http.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions docs/ref/xref.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 4 additions & 11 deletions include/nng/http.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 *);

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
//
Expand Down
9 changes: 8 additions & 1 deletion src/supplemental/http/http_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
20 changes: 18 additions & 2 deletions src/supplemental/http/http_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
//

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "core/nng_impl.h"

#include "http_api.h"
#include "http_msg.h"

static nni_mtx http_txn_lk = NNI_MTX_INITIALIZER;

Expand All @@ -24,6 +26,7 @@ struct nng_http_client {
nni_mtx mtx;
bool closed;
nni_aio aio;
char host[260];
nng_stream_dialer *dialer;
};

Expand Down Expand Up @@ -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);

Expand All @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
Expand Down
60 changes: 57 additions & 3 deletions src/supplemental/http/http_conn.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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)

Check warning on line 968 in src/supplemental/http/http_conn.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_conn.c#L968

Added line #L968 was not covered by tests
{
if (nni_strcasecmp(key, "Host") == 0) {
nni_http_conn_set_host(conn, val);
return (true);

Check warning on line 972 in src/supplemental/http/http_conn.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_conn.c#L971-L972

Added lines #L971 - L972 were not covered by tests
}
return (false);
}

Check warning on line 975 in src/supplemental/http/http_conn.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_conn.c#L974-L975

Added lines #L974 - L975 were not covered by tests

int
nni_http_add_request_header(nng_http *conn, const char *key, const char *val)

Check warning on line 978 in src/supplemental/http/http_conn.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_conn.c#L978

Added line #L978 was not covered by tests
{
if (http_set_request_known_header(conn, key, val)) {
return (0);

Check warning on line 981 in src/supplemental/http/http_conn.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_conn.c#L981

Added line #L981 was not covered by tests
}

return (nni_http_req_add_header(&conn->req, key, val));
}

Check warning on line 985 in src/supplemental/http/http_conn.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_conn.c#L984-L985

Added lines #L984 - L985 were not covered by tests

int
nni_http_set_request_header(nng_http *conn, const char *key, const char *val)

Check warning on line 988 in src/supplemental/http/http_conn.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_conn.c#L988

Added line #L988 was not covered by tests
{
if (http_set_request_known_header(conn, key, val)) {
return (0);

Check warning on line 991 in src/supplemental/http/http_conn.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_conn.c#L991

Added line #L991 was not covered by tests
}

return (nni_http_req_set_header(&conn->req, key, val));
}

Check warning on line 995 in src/supplemental/http/http_conn.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_conn.c#L994-L995

Added lines #L994 - L995 were not covered by tests

void
nni_http_conn_set_response_content_type(nng_http *conn, const char *ctype)

Check warning on line 998 in src/supplemental/http/http_conn.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_conn.c#L998

Added line #L998 was not covered by tests
{
Expand Down Expand Up @@ -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);
Expand Down
34 changes: 19 additions & 15 deletions src/supplemental/http/http_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Check warning on line 131 in src/supplemental/http/http_msg.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_msg.c#L131

Added line #L131 was not covered by tests
}
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);
}
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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)

Check warning on line 339 in src/supplemental/http/http_msg.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_msg.c#L339

Added line #L339 was not covered by tests
{
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;
Expand All @@ -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)

Check warning on line 351 in src/supplemental/http/http_msg.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_msg.c#L351

Added line #L351 was not covered by tests
{
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;
Expand Down
4 changes: 3 additions & 1 deletion src/supplemental/http/http_msg.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading

0 comments on commit 4fb5e4a

Please sign in to comment.