feat: PoC uploading of pastes

c-web-server
Jef Roosens 2023-05-30 10:17:46 +02:00
parent dfbd179c7a
commit 87312f2d84
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
6 changed files with 122 additions and 27 deletions

View File

@ -48,6 +48,7 @@ typedef struct http_request {
} body; } body;
size_t body_len; size_t body_len;
size_t body_received; size_t body_received;
char *body_file_name;
regmatch_t regex_groups[HTTP_MAX_REGEX_GROUPS]; regmatch_t regex_groups[HTTP_MAX_REGEX_GROUPS];
struct phr_header headers[HTTP_MAX_ALLOWED_HEADERS]; struct phr_header headers[HTTP_MAX_ALLOWED_HEADERS];
size_t num_headers; size_t num_headers;

View File

@ -111,11 +111,15 @@ void http_loop_res_add_header(http_loop_ctx *ctx, http_header type,
*/ */
bool http_loop_step_body_to_buf(event_loop_conn *conn); bool http_loop_step_body_to_buf(event_loop_conn *conn);
bool http_loop_step_body_to_file(event_loop_conn *conn);
/* /*
* Authenticate the request using the X-Api-Key header * Authenticate the request using the X-Api-Key header
*/ */
bool http_loop_step_auth(event_loop_conn *conn); bool http_loop_step_auth(event_loop_conn *conn);
bool http_loop_step_switch_res(event_loop_conn *conn);
/** /**
* Initialize a new http loop * Initialize a new http loop
*/ */

View File

@ -3,7 +3,7 @@
#include "http_loop.h" #include "http_loop.h"
extern http_route lander_routes[3]; extern http_route lander_routes[4];
bool lander_get_index(event_loop_conn *conn); bool lander_get_index(event_loop_conn *conn);
@ -11,4 +11,6 @@ bool lander_get_entry(event_loop_conn *conn);
bool lander_post_redirect(event_loop_conn *conn); bool lander_post_redirect(event_loop_conn *conn);
bool lander_post_paste(event_loop_conn *conn);
#endif #endif

View File

@ -53,35 +53,50 @@ static bool string_to_num(size_t *res, const char *s, size_t len) {
return true; 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) { bool http_loop_step_body_to_buf(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx; http_loop_ctx *ctx = conn->ctx;
if (ctx->req.body_len == 0) { if (ctx->req.body_len == 0) {
for (size_t i = 0; i < ctx->req.num_headers; i++) { if (!try_parse_content_length(conn)) {
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 true;
}
// The content length was actually 0, so we can instantly return here
else if (ctx->req.body_len == 0) {
return true;
}
}
}
// 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 true; return true;
} }
@ -95,6 +110,30 @@ bool http_loop_step_body_to_buf(event_loop_conn *conn) {
memcpy(&ctx->req.body.buf[ctx->req.body_received], memcpy(&ctx->req.body.buf[ctx->req.body_received],
&conn->rbuf[conn->rbuf_read], bytes_to_copy); &conn->rbuf[conn->rbuf_read], bytes_to_copy);
ctx->req.body_received += 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; return ctx->req.body_received == ctx->req.body_len;
} }
@ -116,3 +155,9 @@ bool http_loop_step_auth(event_loop_conn *conn) {
return true; return true;
} }
bool http_loop_step_switch_res(event_loop_conn *conn) {
conn->state = event_loop_conn_state_res;
return true;
}

View File

@ -15,4 +15,10 @@ http_route lander_routes[] = {
.method = http_post, .method = http_post,
.path = "/s/", .path = "/s/",
.steps = {http_loop_step_auth, http_loop_step_body_to_buf, .steps = {http_loop_step_auth, http_loop_step_body_to_buf,
lander_post_redirect, NULL}}}; lander_post_redirect, NULL}},
{.type = http_route_literal,
.method = http_post,
.path = "/p/",
.steps = {http_loop_step_auth, lander_post_paste,
http_loop_step_body_to_file, http_loop_step_switch_res, NULL}},
};

View File

@ -38,3 +38,40 @@ bool lander_post_redirect(event_loop_conn *conn) {
return true; return true;
} }
bool lander_post_paste(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx;
char *key;
Entry *new_entry = entry_new(Paste, "");
TrieExitCode res = trie_add_random(ctx->g->trie, &key, new_entry, false);
if (res != Ok) {
error("trie_add_random failed with exit code %i", res);
ctx->res.status = http_internal_server_error;
conn->state = event_loop_conn_state_res;
return true;
}
// Add a slash to the key and add it as the location header
size_t key_len = strlen(key);
char *buf = malloc(key_len + 2);
memcpy(&buf[1], key, key_len);
buf[0] = '/';
buf[key_len + 1] = '\0';
http_loop_res_add_header(ctx, http_header_location, buf, true);
// TODO free this
char *fname = malloc(8 + key_len);
sprintf(fname, "pastes/%s", key);
ctx->req.body_file_name = fname;
free(key);
return true;
}