feat: support adding up to 4 headers

c-web-server
Jef Roosens 2023-05-29 15:34:46 +02:00
parent 8f9de37a95
commit dd0ba31506
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
6 changed files with 69 additions and 6 deletions

View File

@ -9,6 +9,7 @@
#define HTTP_MAX_ALLOWED_HEADERS 16 #define HTTP_MAX_ALLOWED_HEADERS 16
extern const char *http_response_type_names[5][32]; extern const char *http_response_type_names[5][32];
extern const char *http_header_names[];
typedef enum http_request_method { typedef enum http_request_method {
http_get = 0, http_get = 0,
@ -111,6 +112,17 @@ typedef enum http_response_type {
http_network_authentication_required = 511 http_network_authentication_required = 511
} http_response_type; } http_response_type;
typedef enum http_header {
http_header_connection = 0,
http_header_redirect = 1
} http_header;
typedef struct http_response_header {
http_header type;
const char *value;
bool owned;
} http_response_header;
typedef struct http_response { typedef struct http_response {
http_response_type type; http_response_type type;
const char *head; const char *head;
@ -121,6 +133,8 @@ typedef struct http_response {
size_t body_written; size_t body_written;
// If false, the body won't be freed // If false, the body won't be freed
bool owns_body; bool owns_body;
http_response_header headers[4];
size_t header_count;
} http_response; } http_response;
#endif #endif

View File

@ -97,6 +97,9 @@ void http_loop_process_request(event_loop_conn *conn);
void http_loop_res_set_body(http_loop_ctx *ctx, const char *body, void http_loop_res_set_body(http_loop_ctx *ctx, const char *body,
size_t body_len, bool owned); size_t body_len, bool owned);
void http_loop_res_add_header(http_loop_ctx *ctx, http_header type,
const char *value, bool owned);
/** /**
* Initialize a new http loop * Initialize a new http loop
*/ */

View File

@ -86,4 +86,9 @@ const char *http_response_type_names[5][32] = {
}, },
}; };
const char *http_header_names[] = {
"Connection",
"Redirect"
};
// clang-format on // clang-format on

View File

@ -33,6 +33,14 @@ void http_loop_ctx_reset(http_loop_ctx *ctx) {
free((void *)ctx->res.body); free((void *)ctx->res.body);
} }
for (size_t i = 0; i < ctx->res.header_count; i++) {
if (ctx->res.headers[i].owned) {
free((void *)ctx->res.headers[i].value);
}
}
ctx->res.header_count = 0;
ctx->res.body = NULL; ctx->res.body = NULL;
ctx->res.type = 0; ctx->res.type = 0;

View File

@ -7,8 +7,14 @@
static const char *http_response_format = "HTTP/1.1 %i %s\n" static const char *http_response_format = "HTTP/1.1 %i %s\n"
"Server: lander/" LANDER_VERSION "\n" "Server: lander/" LANDER_VERSION "\n"
"Content-Length: %lu\n\n"; "Content-Length: %lu\n";
/*
* This function precalculates the size of the total buffer required using
* snprintf. When this function is called with a buf size of 0, it never tries
* to write any data, but it does return the amount of bytes that would be
* written.
*/
void http_loop_init_header(http_response *res) { void http_loop_init_header(http_response *res) {
if (res->type == 0) { if (res->type == 0) {
res->type = http_ok; res->type = http_ok;
@ -17,16 +23,33 @@ void http_loop_init_header(http_response *res) {
const char *response_type_name = const char *response_type_name =
http_response_type_names[res->type / 100 - 1][res->type % 100]; http_response_type_names[res->type / 100 - 1][res->type % 100];
// Running snprintf with size 0 prevents it from writing any bytes, while // First we calculate the size of the start of the header
// still letting it calculate how many bytes it would have written
int buf_size = snprintf(NULL, 0, http_response_format, res->type, int buf_size = snprintf(NULL, 0, http_response_format, res->type,
response_type_name, res->body_len); response_type_name, res->body_len);
// We add each header's required size
for (size_t i = 0; i < res->header_count; i++) {
buf_size +=
snprintf(NULL, 0, "%s: %s\n", http_header_names[res->headers[i].type],
res->headers[i].value);
}
// The + 1 is required to store the final null byte, but we will replace it
// with the required final newline
char *buf = malloc(buf_size + 1); char *buf = malloc(buf_size + 1);
sprintf(buf, http_response_format, res->type, response_type_name, buf_size = sprintf(buf, http_response_format, res->type, response_type_name,
res->body_len); res->body_len);
for (size_t i = 0; i < res->header_count; i++) {
buf_size +=
sprintf(&buf[buf_size], "%s: %s\n",
http_header_names[res->headers[i].type], res->headers[i].value);
}
buf[buf_size] = '\n';
res->head = buf; res->head = buf;
res->head_len = buf_size; res->head_len = buf_size + 1;
} }
void http_loop_write_response(event_loop_conn *conn) { void http_loop_write_response(event_loop_conn *conn) {
@ -73,3 +96,12 @@ void http_loop_res_set_body(http_loop_ctx *ctx, const char *body,
ctx->res.body_len = body_len; ctx->res.body_len = body_len;
ctx->res.owns_body = owned; ctx->res.owns_body = owned;
} }
void http_loop_res_add_header(http_loop_ctx *ctx, http_header type,
const char *value, bool owned) {
ctx->res.headers[ctx->res.header_count].type = type;
ctx->res.headers[ctx->res.header_count].value = value;
ctx->res.headers[ctx->res.header_count].owned = owned;
ctx->res.header_count++;
}

View File

@ -18,6 +18,7 @@ bool lander_get_index(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx; http_loop_ctx *ctx = conn->ctx;
http_loop_res_set_body(ctx, index_page, sizeof(index_page) - 1, false); http_loop_res_set_body(ctx, index_page, sizeof(index_page) - 1, false);
http_loop_res_add_header(ctx, http_header_connection, "close", false);
conn->state = event_loop_conn_state_res; conn->state = event_loop_conn_state_res;