Skip to content

Commit

Permalink
Start if support for zero alloc headers.
Browse files Browse the repository at this point in the history
A few standard headers like content-length can always be provided
in the connection object, avoiding the need for strdup or allocations
for the header block.  This eliminates up to 3 allocs for each header.
  • Loading branch information
gdamore committed Jan 8, 2025
1 parent 4fe6e49 commit b75e784
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 49 deletions.
5 changes: 5 additions & 0 deletions src/supplemental/http/http_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ extern int nni_http_res_set_data(nni_http_res *, const void *, size_t);
extern int nni_http_req_alloc_data(nni_http_req *, size_t);
extern int nni_http_res_alloc_data(nni_http_res *, size_t);
extern const char *nni_http_req_get_uri(const nni_http_req *);
extern void nni_http_res_set_content_type(nni_http_res *, const char *);
extern void nni_http_req_set_content_type(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 *);
Expand Down Expand Up @@ -389,4 +391,7 @@ extern int nni_http_conn_set_error(
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);

#endif // NNG_SUPPLEMENTAL_HTTP_HTTP_API_H
41 changes: 29 additions & 12 deletions src/supplemental/http/http_conn.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <stdio.h>
#include <string.h>

#include "core/list.h"
#include "core/nng_impl.h"
#include "nng/http.h"
#include "supplemental/tls/tls_api.h"
Expand Down Expand Up @@ -843,7 +844,7 @@ nni_http_conn_set_status(nng_http *conn, uint16_t status, const char *reason)
}

static int
http_conn_set_error(nng_http_conn *conn, uint16_t status, const char *reason,
http_conn_set_error(nng_http *conn, uint16_t status, const char *reason,
const char *body, const char *redirect)
{
int rv;
Expand Down Expand Up @@ -899,10 +900,13 @@ http_conn_set_error(nng_http_conn *conn, uint16_t status, const char *reason,
body = content;
}
if (strlen(body) > 0) {
if ((rv = nni_http_res_set_header(&conn->res, "Content-Type",
"text/html; charset=UTF-8")) != 0) {
return (rv);
}
(void) nni_http_res_del_header(&conn->res, "Content-Type");
nni_list_node_remove(&conn->res.content_type.node);
conn->res.content_type.name = "Content-Type";
conn->res.content_type.value = "text/html; charset=UTF-8";
conn->res.content_type.static_name = true;
conn->res.content_type.static_value = true;
nni_list_append(&conn->res.hdrs, &conn->res.content_type);
return (
nni_http_res_copy_data(&conn->res, body, strlen(body)));
}
Expand All @@ -911,23 +915,36 @@ http_conn_set_error(nng_http_conn *conn, uint16_t status, const char *reason,

int
nni_http_conn_set_error(
nng_http_conn *conn, uint16_t status, const char *reason, const char *body)
nng_http *conn, uint16_t status, const char *reason, const char *body)
{
return (http_conn_set_error(conn, status, reason, body, NULL));
}

int
nni_http_conn_set_redirect(nng_http_conn *conn, uint16_t status,
const char *reason, const char *redirect)
nni_http_conn_set_redirect(
nng_http *conn, uint16_t status, const char *reason, const char *redirect)
{
int rv;
if ((rv = nni_http_res_set_header(&conn->res, "Location", redirect)) !=
0) {
return (rv);
char *loc;
if ((loc = nni_strdup(redirect)) == NULL) {
return (NNG_ENOMEM);
}
(void) nni_http_res_del_header(&conn->res, "Location");
nni_list_node_remove(&conn->res.location.node);
nni_http_free_header(&conn->res.location);
conn->res.location.name = "Location";
conn->res.location.value = loc;
conn->res.location.static_name = true;
conn->res.location.static_value = true;
nni_list_prepend(&conn->res.hdrs, &conn->res.location);
return (http_conn_set_error(conn, status, reason, NULL, redirect));
}

void
nni_http_conn_set_response_content_type(nng_http *conn, const char *ctype)
{
nni_http_res_set_content_type(&conn->res, ctype);
}

int
nni_http_conn_getopt(
nni_http_conn *conn, const char *name, void *buf, size_t *szp, nni_type t)
Expand Down
122 changes: 88 additions & 34 deletions src/supplemental/http/http_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <stdlib.h>
#include <string.h>

#include "core/list.h"
#include "core/nng_impl.h"
#include "http_api.h"
#include "http_msg.h"
Expand All @@ -33,15 +34,29 @@ http_set_string(char **strp, const char *val)
return (0);
}

void
nni_http_free_header(http_header *h)
{
nni_list_node_remove(&h->node);
if (!h->static_name) {
nni_strfree(h->name);
h->name = NULL;
}
if (!h->static_value) {
nni_strfree(h->value);
h->value = NULL;
}
if (h->alloc_header) {
NNI_FREE_STRUCT(h);
}
}

static void
http_headers_reset(nni_list *hdrs)
{
http_header *h;
while ((h = nni_list_first(hdrs)) != NULL) {
nni_list_remove(hdrs, h);
nni_strfree(h->name);
nni_strfree(h->value);
NNI_FREE_STRUCT(h);
nni_http_free_header(h);
}
}

Expand Down Expand Up @@ -91,16 +106,21 @@ http_del_header(nni_list *hdrs, const char *key)
http_header *h;
NNI_LIST_FOREACH (hdrs, h) {
if (nni_strcasecmp(key, h->name) == 0) {
nni_list_remove(hdrs, h);
nni_strfree(h->name);
nni_free(h->value, strlen(h->value) + 1);
NNI_FREE_STRUCT(h);
nni_http_free_header(h);
return (0);
}
}
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)
{
Expand Down Expand Up @@ -132,6 +152,7 @@ http_set_header(nni_list *hdrs, const char *key, const char *val)
if ((h = NNI_ALLOC_STRUCT(h)) == NULL) {
return (NNG_ENOMEM);
}
h->alloc_header = true;
if ((h->name = nni_strdup(key)) == NULL) {
NNI_FREE_STRUCT(h);
return (NNG_ENOMEM);
Expand Down Expand Up @@ -178,6 +199,7 @@ http_add_header(nni_list *hdrs, const char *key, const char *val)
if ((h = NNI_ALLOC_STRUCT(h)) == NULL) {
return (NNG_ENOMEM);
}
h->alloc_header = true;
if ((h->name = nni_strdup(key)) == NULL) {
NNI_FREE_STRUCT(h);
return (NNG_ENOMEM);
Expand Down Expand Up @@ -264,14 +286,6 @@ http_entity_copy_data(nni_http_entity *entity, const void *data, size_t size)
return (rv);
}

static int
http_set_content_length(nni_http_entity *entity, nni_list *hdrs)
{
char buf[16];
(void) snprintf(buf, sizeof(buf), "%u", (unsigned) entity->size);
return (http_set_header(hdrs, "Content-Length", buf));
}

static void
http_entity_get_data(nni_http_entity *entity, void **datap, size_t *sizep)
{
Expand All @@ -291,41 +305,82 @@ nni_http_res_get_data(nni_http_res *res, void **datap, size_t *sizep)
http_entity_get_data(&res->data, datap, sizep);
}

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_list_node_remove(&req->content_length.node);
req->content_length.name = "Content-Length";
req->content_length.value = req->clen;
req->content_length.static_name = true;
req->content_length.static_value = true;
nni_list_append(&req->hdrs, &req->content_length);
}

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_list_node_remove(&res->content_length.node);
res->content_length.name = "Content-Length";
res->content_length.value = res->clen;
res->content_length.static_name = true;
res->content_length.static_value = true;
nni_list_append(&res->hdrs, &res->content_length);
}

void
nni_http_res_set_content_type(nni_http_res *res, const char *ctype)
{
http_del_all_headers(&res->hdrs, "Content-Type");
nni_list_node_remove(&res->content_type.node);
res->content_type.name = "Content-Type";
res->content_type.value = (char *) ctype;
res->content_length.static_name = true;
res->content_length.static_value = true;
nni_list_append(&res->hdrs, &res->content_type);
}

void
nni_http_req_set_content_type(nni_http_req *req, const char *ctype)
{
http_del_all_headers(&req->hdrs, "Content-Type");
nni_list_node_remove(&req->content_type.node);
req->content_type.name = "Content-Type";
req->content_type.value = (char *) ctype;
req->content_length.static_name = true;
req->content_length.static_value = true;
nni_list_append(&req->hdrs, &req->content_type);
}

int
nni_http_req_set_data(nni_http_req *req, const void *data, size_t size)
{
int rv;

http_entity_set_data(&req->data, data, size);
if ((rv = http_set_content_length(&req->data, &req->hdrs)) != 0) {
http_entity_set_data(&req->data, NULL, 0);
}
return (rv);
nni_http_req_set_content_length(req, size);
return (0);
}

int
nni_http_res_set_data(nni_http_res *res, const void *data, size_t size)
{
int rv;

http_entity_set_data(&res->data, data, size);
if ((rv = http_set_content_length(&res->data, &res->hdrs)) != 0) {
http_entity_set_data(&res->data, NULL, 0);
}
nni_http_res_set_content_length(res, size);
res->iserr = false;
return (rv);
return (0);
}

int
nni_http_req_copy_data(nni_http_req *req, const void *data, size_t size)
{
int rv;

if (((rv = http_entity_copy_data(&req->data, data, size)) != 0) ||
((rv = http_set_content_length(&req->data, &req->hdrs)) != 0)) {
http_entity_set_data(&req->data, NULL, 0);
if ((rv = http_entity_copy_data(&req->data, data, size)) != 0) {
return (rv);
}
nni_http_req_set_content_length(req, size);
return (0);
}

Expand All @@ -345,11 +400,10 @@ nni_http_res_copy_data(nni_http_res *res, const void *data, size_t size)
{
int rv;

if (((rv = http_entity_copy_data(&res->data, data, size)) != 0) ||
((rv = http_set_content_length(&res->data, &res->hdrs)) != 0)) {
http_entity_set_data(&res->data, NULL, 0);
if ((rv = http_entity_copy_data(&res->data, data, size)) != 0) {
return (rv);
}
nni_http_res_set_content_length(res, size);
res->iserr = false;
return (0);
}
Expand Down
13 changes: 13 additions & 0 deletions src/supplemental/http/http_msg.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ typedef struct http_header {
char *name;
char *value;
nni_list_node node;
bool static_name : 1; // name is static, do not free it
bool static_value : 1; // value is static, do not free it
bool alloc_header : 1; // header is heap allocated
} http_header;

typedef struct nni_http_entity {
Expand All @@ -40,6 +43,9 @@ struct nng_http_req {
char *buf;
size_t bufsz;
bool parsed;
char clen[28];
http_header content_type;
http_header content_length;
};

struct nng_http_res {
Expand All @@ -52,6 +58,13 @@ struct nng_http_res {
size_t bufsz;
bool parsed;
bool iserr;
char clen[28];
http_header location;
http_header content_type;
http_header content_length;
http_header connection;
};

extern void nni_http_free_header(http_header *);

#endif
6 changes: 3 additions & 3 deletions src/supplemental/http/http_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ http_sconn_error(http_sconn *sc, uint16_t err)
}

if (sc->close) {
// TODO: Static header
if (nni_http_res_set_header(res, "Connection", "close") != 0) {
http_sconn_close(sc);
return;
Expand Down Expand Up @@ -1325,9 +1326,8 @@ http_handle_file(nng_http *conn, void *arg, nni_aio *aio)
nni_aio_finish(aio, 0, 0);
return;
}
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_set_content_type(res, ctype);
if ((rv = nni_http_res_copy_data(res, data, size)) != 0) {
nni_free(data, size);
nni_aio_finish_error(aio, rv);
return;
Expand Down

0 comments on commit b75e784

Please sign in to comment.