lander/src/http_loop/http_loop_steps.c

134 lines
3.7 KiB
C

#include <math.h>
#include "lander.h"
/*
* Converts a string to a number, returning true if the string contained a valid
* positive number.
*/
static bool string_to_num(size_t *res, const char *s, size_t len) {
*res = 0;
for (size_t i = 0; i < len; i++) {
int val = s[i] - '0';
if (val < 0 || val > 9) {
return false;
}
*res += val * (int)pow(10, (len - 1) - i);
}
return true;
}
/*
* Try to find and parse the Content-Length header. This function returns true
* if it was successful. If false is returned, the underlying step should
* immediately exit.
*/
static bool try_parse_content_length(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx;
for (size_t i = 0; i < ctx->req.num_headers; i++) {
struct phr_header *header = &ctx->req.headers[i];
if (strncmp(header->name, "Content-Length", header->name_len) == 0) {
// If the content length header is present but contains an invalid
// number, we return a bad request error
if (!string_to_num(&ctx->req.body.expected_len, header->value,
header->value_len)) {
ctx->res.status = http_bad_request;
conn->state = event_loop_conn_state_res;
return false;
}
// The content length was actually 0, so we can instantly return here
else if (ctx->req.body.expected_len == 0) {
return false;
}
}
}
// A zero here means there's no content length header
if (ctx->req.body.expected_len == 0) {
ctx->res.status = http_length_required;
conn->state = event_loop_conn_state_res;
return false;
}
return true;
}
bool http_loop_step_body_to_buf(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx;
if (ctx->req.body.expected_len == 0) {
if (!try_parse_content_length(conn)) {
return true;
}
ctx->req.body.type = http_body_buf;
ctx->req.body.buf = malloc(ctx->req.body.expected_len * sizeof(uint8_t));
ctx->req.body.len = 0;
}
size_t bytes_to_copy = MIN(conn->rbuf_size - conn->rbuf_read,
ctx->req.body.expected_len - ctx->req.body.len);
memcpy(&ctx->req.body.buf[ctx->req.body.len], &conn->rbuf[conn->rbuf_read],
bytes_to_copy);
ctx->req.body.len += bytes_to_copy;
conn->rbuf_read += bytes_to_copy;
return ctx->req.body.len == ctx->req.body.expected_len;
}
bool http_loop_step_body_to_file(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx;
if (ctx->req.body.expected_len == 0) {
if (!try_parse_content_length(conn)) {
return true;
}
ctx->req.body.type = http_body_file;
ctx->req.body.file = fopen(ctx->req.body.fname, "wb");
ctx->req.body.len = 0;
}
size_t bytes_to_write = MIN(conn->rbuf_size - conn->rbuf_read,
ctx->req.body.expected_len - ctx->req.body.len);
size_t bytes_written = fwrite(&conn->rbuf[conn->rbuf_read], sizeof(uint8_t),
bytes_to_write, ctx->req.body.file);
ctx->req.body.len += bytes_written;
conn->rbuf_read += bytes_written;
return ctx->req.body.len == ctx->req.body.expected_len;
}
bool http_loop_step_auth(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx;
for (size_t i = 0; i < ctx->req.num_headers; i++) {
struct phr_header *header = &ctx->req.headers[i];
if ((strncmp("X-Api-Key", header->name, header->name_len) == 0) &&
(strncmp(header->value, ctx->g->api_key, header->value_len) == 0) &&
(strlen(ctx->g->api_key) == header->value_len)) {
return true;
}
}
ctx->res.status = http_unauthorized;
conn->state = event_loop_conn_state_res;
return true;
}
bool http_loop_step_switch_res(event_loop_conn *conn) {
conn->state = event_loop_conn_state_res;
return true;
}