lander/src/http_loop/http_loop_res.c

133 lines
4.2 KiB
C
Raw Normal View History

#include <stdio.h>
2023-05-29 17:53:57 +02:00
#include <sys/stat.h>
#include "http_loop.h"
#include "log.h"
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
static const char *http_response_format = "HTTP/1.1 %i %s\n"
2023-05-29 14:48:50 +02:00
"Server: lander/" LANDER_VERSION "\n"
2023-05-29 15:34:46 +02:00
"Content-Length: %lu\n";
2023-05-29 15:34:46 +02:00
/*
* 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) {
2023-05-29 12:43:50 +02:00
if (res->type == 0) {
res->type = http_ok;
}
const char *response_type_name =
http_response_type_names[res->type / 100 - 1][res->type % 100];
2023-05-29 15:34:46 +02:00
// First we calculate the size of the start of the header
int buf_size = snprintf(NULL, 0, http_response_format, res->type,
response_type_name, res->body_len);
2023-05-29 15:34:46 +02:00
// 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);
2023-05-29 15:34:46 +02:00
buf_size = sprintf(buf, http_response_format, res->type, response_type_name,
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;
2023-05-29 15:34:46 +02:00
res->head_len = buf_size + 1;
}
void http_loop_write_response(event_loop_conn *conn) {
http_response *res = &((http_loop_ctx *)conn->ctx)->res;
// Create head response
if (res->head == NULL) {
http_loop_init_header(res);
}
// The final iteration marks the end of the response, after which we reset the
// context so a next request can be processed
if (res->head_written == res->head_len &&
res->body_written == res->body_len) {
http_loop_ctx_reset(conn->ctx);
conn->state = event_loop_conn_state_req;
return;
}
if (res->head_written < res->head_len) {
size_t bytes_to_write = MIN(res->head_len - res->head_written,
EVENT_LOOP_BUFFER_SIZE - conn->wbuf_size);
memcpy(&conn->wbuf[conn->wbuf_size], &res->head[res->head_written],
bytes_to_write);
conn->wbuf_size += bytes_to_write;
res->head_written += bytes_to_write;
}
if (res->body_written < res->body_len) {
size_t bytes_to_write = MIN(res->body_len - res->body_written,
EVENT_LOOP_BUFFER_SIZE - conn->wbuf_size);
2023-05-29 17:53:57 +02:00
size_t bytes_written;
switch (res->body_type) {
case http_response_body_buf:
memcpy(&conn->wbuf[conn->wbuf_size], &res->body[res->body_written],
bytes_to_write);
conn->wbuf_size += bytes_to_write;
res->body_written += bytes_to_write;
break;
case http_response_body_file:
bytes_written = fread(&conn->wbuf[conn->wbuf_size], sizeof(uint8_t),
bytes_to_write, res->body);
conn->wbuf_size += bytes_written;
res->body_written += bytes_written;
break;
}
}
}
2023-05-29 12:43:50 +02:00
2023-05-29 17:53:57 +02:00
void http_loop_res_set_body_buf(http_loop_ctx *ctx, const char *body,
size_t body_len, bool owned) {
ctx->res.body_type = http_response_body_buf;
ctx->res.body = (void *)body;
2023-05-29 12:43:50 +02:00
ctx->res.body_len = body_len;
ctx->res.owns_body = owned;
}
2023-05-29 15:34:46 +02:00
2023-05-29 17:53:57 +02:00
void http_loop_res_set_body_file(http_loop_ctx *ctx, const char *filename) {
struct stat st;
stat(filename, &st);
// TODO error handling
FILE *f = fopen(filename, "r");
ctx->res.body_type = http_response_body_file;
ctx->res.body = f;
ctx->res.body_len = st.st_size;
}
2023-05-29 15:34:46 +02:00
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++;
}