refactor: move steps to own file
parent
87312f2d84
commit
0d5c6d0f39
|
@ -1,3 +1,6 @@
|
||||||
CMakeFiles/
|
*
|
||||||
build/
|
|
||||||
.git/
|
!src/
|
||||||
|
!Makefile
|
||||||
|
!thirdparty/
|
||||||
|
!include/
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
#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_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_len == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A zero here means there's no content length header
|
||||||
|
if (ctx->req.body_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_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_len * sizeof(uint8_t));
|
||||||
|
ctx->req.body_received = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bytes_to_copy = MIN(conn->rbuf_size - conn->rbuf_read,
|
||||||
|
ctx->req.body_len - ctx->req.body_received);
|
||||||
|
memcpy(&ctx->req.body.buf[ctx->req.body_received],
|
||||||
|
&conn->rbuf[conn->rbuf_read], bytes_to_copy);
|
||||||
|
ctx->req.body_received += bytes_to_copy;
|
||||||
|
conn->rbuf_read += bytes_to_copy;
|
||||||
|
|
||||||
|
return ctx->req.body_received == ctx->req.body_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool http_loop_step_body_to_file(event_loop_conn *conn) {
|
||||||
|
http_loop_ctx *ctx = conn->ctx;
|
||||||
|
|
||||||
|
if (ctx->req.body_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_file_name, "wb");
|
||||||
|
ctx->req.body_received = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bytes_to_write = MIN(conn->rbuf_size - conn->rbuf_read,
|
||||||
|
ctx->req.body_len - ctx->req.body_received);
|
||||||
|
size_t bytes_written = fwrite(&conn->rbuf[conn->rbuf_read], sizeof(uint8_t),
|
||||||
|
bytes_to_write, ctx->req.body.file);
|
||||||
|
ctx->req.body_received += bytes_written;
|
||||||
|
conn->rbuf_read += bytes_written;
|
||||||
|
|
||||||
|
return ctx->req.body_received == ctx->req.body_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)) {
|
||||||
|
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;
|
||||||
|
}
|
|
@ -1,5 +1,3 @@
|
||||||
#include <math.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "http_loop.h"
|
#include "http_loop.h"
|
||||||
|
@ -32,132 +30,3 @@ void http_loop_res_add_header(http_loop_ctx *ctx, http_header type,
|
||||||
|
|
||||||
ctx->res.header_count++;
|
ctx->res.header_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* 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_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_len == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A zero here means there's no content length header
|
|
||||||
if (ctx->req.body_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_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_len * sizeof(uint8_t));
|
|
||||||
ctx->req.body_received = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t bytes_to_copy = MIN(conn->rbuf_size - conn->rbuf_read,
|
|
||||||
ctx->req.body_len - ctx->req.body_received);
|
|
||||||
memcpy(&ctx->req.body.buf[ctx->req.body_received],
|
|
||||||
&conn->rbuf[conn->rbuf_read], bytes_to_copy);
|
|
||||||
ctx->req.body_received += bytes_to_copy;
|
|
||||||
conn->rbuf_read += bytes_to_copy;
|
|
||||||
|
|
||||||
return ctx->req.body_received == ctx->req.body_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool http_loop_step_body_to_file(event_loop_conn *conn) {
|
|
||||||
http_loop_ctx *ctx = conn->ctx;
|
|
||||||
|
|
||||||
if (ctx->req.body_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_file_name, "wb");
|
|
||||||
ctx->req.body_received = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t bytes_to_write = MIN(conn->rbuf_size - conn->rbuf_read,
|
|
||||||
ctx->req.body_len - ctx->req.body_received);
|
|
||||||
size_t bytes_written = fwrite(&conn->rbuf[conn->rbuf_read], sizeof(uint8_t),
|
|
||||||
bytes_to_write, ctx->req.body.file);
|
|
||||||
ctx->req.body_received += bytes_written;
|
|
||||||
conn->rbuf_read += bytes_written;
|
|
||||||
|
|
||||||
return ctx->req.body_received == ctx->req.body_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)) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue