feat: support adding up to 4 headers
parent
8f9de37a95
commit
dd0ba31506
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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++;
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue