#include #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; }