From f3da5c78efcbb00a9bd3be50fffe72a169892c1c Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Tue, 5 Dec 2023 19:12:19 +0100 Subject: [PATCH] feat(lander): implement redirect posting using lnm --- include/lander.h | 12 ++-- lnm/include/lnm/http/loop.h | 2 + lnm/include/lnm/http/req.h | 4 +- lnm/src/http/lnm_http_loop_process.c | 6 +- lnm/src/http/lnm_http_loop_steps.c | 24 +++++++ lnm/src/http/lnm_http_req.c | 4 ++ src/lander/lander.c | 10 +-- src/lander/lander_get.c | 96 +++++++++++++++------------- src/lander/lander_post.c | 36 +++++------ src/main.c | 10 +++ 10 files changed, 123 insertions(+), 81 deletions(-) create mode 100644 lnm/src/http/lnm_http_loop_steps.c diff --git a/include/lander.h b/include/lander.h index 7220559..82d41a6 100644 --- a/include/lander.h +++ b/include/lander.h @@ -42,21 +42,19 @@ void lander_ctx_free(lander_ctx *ctx); lnm_http_step_err lander_get_index(lnm_http_conn *conn); -bool lander_get_entry(event_loop_conn *conn); +lnm_http_step_err lander_get_entry(lnm_http_conn *conn); -bool lander_post_redirect(event_loop_conn *conn); +lnm_http_step_err lander_post_redirect(lnm_http_conn *conn); bool lander_post_paste(event_loop_conn *conn); bool lander_post_paste(event_loop_conn *conn); -bool lander_post_redirect(event_loop_conn *conn); - bool lander_stream_body_to_entry(event_loop_conn *conn); bool lander_stream_body_to_client(event_loop_conn *conn); -bool lander_post_redirect_body_to_attr(event_loop_conn *conn); +lnm_http_step_err lander_post_redirect_body_to_attr(lnm_http_conn *conn); bool lander_remove_entry(event_loop_conn *conn); @@ -71,7 +69,7 @@ void lander_header_to_attr(http_loop_ctx *ctx, const char *header, /** * Store the attribute's value as the provided header, if present. */ -void lander_attr_to_header(http_loop_ctx *ctx, lander_attr_type attr_type, - http_header header_type); +void lander_attr_to_header(lnm_http_loop_ctx *ctx, lander_attr_type attr_type, + lnm_http_header header_type); #endif diff --git a/lnm/include/lnm/http/loop.h b/lnm/include/lnm/http/loop.h index 482560d..867a93e 100644 --- a/lnm/include/lnm/http/loop.h +++ b/lnm/include/lnm/http/loop.h @@ -128,4 +128,6 @@ typedef struct lnm_http_loop_ctx { void *c; } lnm_http_loop_ctx; +lnm_http_step_err lnm_http_loop_step_body_to_buf(lnm_http_conn *conn); + #endif diff --git a/lnm/include/lnm/http/req.h b/lnm/include/lnm/http/req.h index 5d8f46a..e40d388 100644 --- a/lnm/include/lnm/http/req.h +++ b/lnm/include/lnm/http/req.h @@ -35,8 +35,10 @@ typedef struct lnm_http_req { } headers; struct { uint64_t expected_len; + uint64_t len; + char *buf; + bool owned; } body; - uint64_t content_length; } lnm_http_req; typedef enum lnm_http_parse_err { diff --git a/lnm/src/http/lnm_http_loop_process.c b/lnm/src/http/lnm_http_loop_process.c index 2a6649a..8122058 100644 --- a/lnm/src/http/lnm_http_loop_process.c +++ b/lnm/src/http/lnm_http_loop_process.c @@ -99,7 +99,7 @@ void lnm_http_loop_process_parse_headers(lnm_http_conn *conn) { size_t value_len; if (lnm_http_req_header_get(&value, &value_len, req, lnm_http_header_content_length) == lnm_err_ok) { - req->content_length = lnm_atoi(value, value_len); + req->body.expected_len = lnm_atoi(value, value_len); } ctx->state = lnm_http_loop_state_steps; @@ -149,8 +149,8 @@ void lnm_http_loop_state_process_add_headers(lnm_http_conn *conn) { } sprintf(buf, "%lu", res->body.len); - lnm_http_res_add_header_len(res, lnm_http_header_content_length, buf, - digits, true); + lnm_http_res_add_header_len(res, lnm_http_header_content_length, buf, digits, + true); ctx->state = lnm_http_loop_state_write_status_line; } diff --git a/lnm/src/http/lnm_http_loop_steps.c b/lnm/src/http/lnm_http_loop_steps.c new file mode 100644 index 0000000..fde3d70 --- /dev/null +++ b/lnm/src/http/lnm_http_loop_steps.c @@ -0,0 +1,24 @@ +#include + +#include "lnm/http/loop.h" +#include "lnm/loop.h" + +lnm_http_step_err lnm_http_loop_step_body_to_buf(lnm_http_conn *conn) { + lnm_http_loop_ctx *ctx = conn->ctx; + + if (ctx->req.body.buf == NULL) { + ctx->req.body.buf = malloc(ctx->req.body.expected_len * sizeof(char)); + ctx->req.body.len = 0; + } + + size_t to_read = LNM_MIN(conn->r.size - conn->r.read, + ctx->req.body.expected_len - ctx->req.body.len); + memcpy(&ctx->req.body.buf[ctx->req.body.len], &conn->r.buf[conn->r.read], + to_read); + ctx->req.body.len += to_read; + conn->r.read += to_read; + + return ctx->req.body.len == ctx->req.body.expected_len + ? lnm_http_step_err_done + : lnm_http_step_err_io_needed; +} diff --git a/lnm/src/http/lnm_http_req.c b/lnm/src/http/lnm_http_req.c index fa6e1da..a601e32 100644 --- a/lnm/src/http/lnm_http_req.c +++ b/lnm/src/http/lnm_http_req.c @@ -63,6 +63,10 @@ lnm_http_parse_err lnm_http_req_parse(lnm_http_req *req, char *buf, } void lnm_http_req_reset(lnm_http_req *req) { + if (req->body.owned) { + free(req->body.buf); + } + memset(req, 0, sizeof(lnm_http_req)); } diff --git a/src/lander/lander.c b/src/lander/lander.c index 6d316d2..471c280 100644 --- a/src/lander/lander.c +++ b/src/lander/lander.c @@ -104,16 +104,12 @@ void lander_header_to_attr(http_loop_ctx *ctx, const char *header_name, } } -void lander_attr_to_header(http_loop_ctx *ctx, lander_attr_type attr_type, - http_header header_type) { +void lander_attr_to_header(lnm_http_loop_ctx *ctx, lander_attr_type attr_type, + lnm_http_header header_type) { lander_ctx *c_ctx = ctx->c; lsm_str *value; if (lsm_entry_attr_get(&value, c_ctx->entry, attr_type) == lsm_error_ok) { - char *buf = malloc(lsm_str_len(value) + 1); - memcpy(buf, lsm_str_ptr(value), lsm_str_len(value)); - buf[lsm_str_len(value)] = '\0'; - - http_res_add_header(&ctx->res, header_type, buf, true); + lnm_http_res_add_header_len(&ctx->res, header_type, (char *)lsm_str_ptr(value), lsm_str_len(value), false); } } diff --git a/src/lander/lander_get.c b/src/lander/lander_get.c index 3d05c4d..66ea77a 100644 --- a/src/lander/lander_get.c +++ b/src/lander/lander_get.c @@ -1,5 +1,7 @@ #include +#include "lnm/http/consts.h" +#include "lnm/http/loop.h" #include "lnm/loop.h" #include "event_loop.h" @@ -24,17 +26,13 @@ lnm_http_step_err lander_get_index(lnm_http_conn *conn) { lnm_http_res_body_set_buf(&ctx->res, (char *)index_page, sizeof(index_page) - 1, false); + lnm_http_res_add_header(&ctx->res, lnm_http_header_content_type, "text/html", false); - /* http_res_set_body_buf(&ctx->res, index_page, sizeof(index_page) - 1, - * false); */ - /* http_res_set_mime_type(&ctx->res, http_mime_html); */ - - /* conn->state = event_loop_conn_state_res; */ return lnm_http_step_err_done; } -void lander_get_redirect(event_loop_conn *conn) { - http_loop_ctx *ctx = conn->ctx; +lnm_http_step_err lander_get_redirect(lnm_http_conn *conn) { + lnm_http_loop_ctx *ctx = conn->ctx; lander_ctx *c_ctx = ctx->c; // For redirects, the URL is stored as an in-memory attribute @@ -45,43 +43,49 @@ void lander_get_redirect(event_loop_conn *conn) { lsm_error_ok) { error("Entry of type redirect detected without URL attribute"); - ctx->res.status = http_internal_server_error; + ctx->res.status = lnm_http_status_internal_server_error; lsm_entry_close(c_ctx->entry); c_ctx->entry = NULL; - return; + return lnm_http_step_err_res; } - char *buf = malloc(lsm_str_len(url_attr_val) + 1); - memcpy(buf, lsm_str_ptr(url_attr_val), lsm_str_len(url_attr_val)); + lnm_http_res_add_header_len(&ctx->res, lnm_http_header_location, (char *)lsm_str_ptr(url_attr_val), lsm_str_len(url_attr_val), false); - buf[lsm_str_len(url_attr_val)] = '\0'; + ctx->res.status = lnm_http_status_moved_permanently; - ctx->res.status = http_moved_permanently; - http_res_add_header(&ctx->res, http_header_location, buf, true); - - // We no longer need the entry at this point, so we can unlock it early - // This will also signal to the response code not to read any data from - // the entry - lsm_entry_close(c_ctx->entry); - c_ctx->entry = NULL; + return lnm_http_step_err_done; } -void lander_get_paste(event_loop_conn *conn) { - http_loop_ctx *ctx = conn->ctx; +lnm_err lander_entry_data_streamer(uint64_t *written, char *buf, lnm_http_conn *conn, + uint64_t offset, uint64_t len) { + // TODO respect offset variable + + lnm_http_loop_ctx *ctx = conn->ctx; lander_ctx *c_ctx = ctx->c; - ctx->res.body.expected_len = lsm_entry_data_len(c_ctx->entry); - http_res_set_mime_type(&ctx->res, http_mime_txt); + lsm_entry_data_read(written, buf, c_ctx->entry, len); + + return lnm_err_ok; } -void lander_get_file(event_loop_conn *conn) { - http_loop_ctx *ctx = conn->ctx; +lnm_http_step_err lander_get_paste(lnm_http_conn *conn) { + lnm_http_loop_ctx *ctx = conn->ctx; lander_ctx *c_ctx = ctx->c; - ctx->res.body.expected_len = lsm_entry_data_len(c_ctx->entry); + lnm_http_res_body_set_fn(&ctx->res, lander_entry_data_streamer, lsm_entry_data_len(c_ctx->entry)); + lnm_http_res_add_header(&ctx->res, lnm_http_header_content_type, "text/plain", false); + + return lnm_http_step_err_done; +} + +lnm_http_step_err lander_get_file(lnm_http_conn *conn) { + lnm_http_loop_ctx *ctx = conn->ctx; + lander_ctx *c_ctx = ctx->c; + + lnm_http_res_body_set_fn(&ctx->res, lander_entry_data_streamer, lsm_entry_data_len(c_ctx->entry)); lander_attr_to_header(ctx, lander_attr_type_content_type, - http_header_content_type); + lnm_http_header_content_type); lsm_str *value; char *buf; @@ -90,23 +94,25 @@ void lander_get_file(event_loop_conn *conn) { lsm_error_ok) { buf = malloc(24 + lsm_str_len(value)); int len = lsm_str_len(value); - sprintf(buf, "attachment; filename=\"%*s\"", len, lsm_str_ptr(value)); + sprintf(buf, "attachment; filename=\"%.*s\"", len, lsm_str_ptr(value)); } else { buf = malloc(11); strcpy(buf, "attachment"); } - http_res_add_header(&ctx->res, http_header_content_disposition, buf, true); + lnm_http_res_add_header(&ctx->res, lnm_http_header_content_disposition, buf, true); + + return lnm_http_step_err_done; } -bool lander_get_entry(event_loop_conn *conn) { - http_loop_ctx *ctx = conn->ctx; +lnm_http_step_err lander_get_entry(lnm_http_conn *conn) { + lnm_http_loop_ctx *ctx = conn->ctx; lander_ctx *c_ctx = ctx->c; - http_loop_gctx *gctx = ctx->g; + lnm_http_loop_gctx *gctx = ctx->g; lander_gctx *c_gctx = gctx->c; - const char *key_s = &ctx->req.path[ctx->req.regex_groups[1].rm_so]; - int key_len = ctx->req.regex_groups[1].rm_eo - ctx->req.regex_groups[1].rm_so; + const char *key_s = &ctx->req.path.s[ctx->req.path.groups[1].rm_so]; + int key_len = ctx->req.path.groups[1].rm_eo - ctx->req.path.groups[1].rm_so; lsm_str *key; lsm_str_init_copy_n(&key, (char *)key_s, key_len); @@ -115,32 +121,32 @@ bool lander_get_entry(event_loop_conn *conn) { case lsm_error_ok: break; case lsm_error_not_found: - ctx->res.status = http_not_found; - conn->state = event_loop_conn_state_res; - return true; + ctx->res.status = lnm_http_status_not_found; + return lnm_http_step_err_res; default: - ctx->res.status = http_internal_server_error; - conn->state = event_loop_conn_state_res; - return true; + ctx->res.status = lnm_http_status_internal_server_error; + return lnm_http_step_err_res; } lander_entry_type t; lsm_entry_attr_get_uint8_t((uint8_t *)&t, c_ctx->entry, lander_attr_type_entry_type); + lnm_http_step_err res; + switch (t) { case lander_entry_type_redirect: - lander_get_redirect(conn); + res = lander_get_redirect(conn); break; case lander_entry_type_paste: - lander_get_paste(conn); + res = lander_get_paste(conn); break; case lander_entry_type_file: - lander_get_file(conn); + res = lander_get_file(conn); break; } - return true; + return res; } bool lander_stream_body_to_client(event_loop_conn *conn) { diff --git a/src/lander/lander_post.c b/src/lander/lander_post.c index 9711d03..5800737 100644 --- a/src/lander/lander_post.c +++ b/src/lander/lander_post.c @@ -3,6 +3,7 @@ #include "lander.h" #include "log.h" #include "lsm/store.h" +#include "lnm/loop.h" static void randomize_key(char *key, int len) { size_t charset_len = strlen(lander_key_charset); @@ -19,26 +20,26 @@ static void randomize_key(char *key, int len) { * * @return true on success, false otherwise */ -bool lander_insert_entry(http_loop_ctx *ctx) { - http_loop_gctx *gctx = ctx->g; +bool lander_insert_entry(lnm_http_loop_ctx *ctx) { + lnm_http_loop_gctx *gctx = ctx->g; lander_gctx *c_gctx = gctx->c; lander_ctx *c_ctx = ctx->c; lsm_str *key; int key_len; - if (ctx->req.regex_groups[2].rm_eo == ctx->req.regex_groups[2].rm_so) { + if (ctx->req.path.groups[2].rm_eo == ctx->req.path.groups[2].rm_so) { // Generate a random key to insert bool secure = - (ctx->req.regex_groups[1].rm_eo - ctx->req.regex_groups[1].rm_so) == 1; + (ctx->req.path.groups[1].rm_eo - ctx->req.path.groups[1].rm_so) == 1; key_len = secure ? 16 : 4; char *key_s = malloc((key_len + 1) * sizeof(char)); randomize_key(key_s, key_len); lsm_str_init(&key, key_s); } else { - const char *key_s = &ctx->req.path[ctx->req.regex_groups[2].rm_so]; - key_len = ctx->req.regex_groups[2].rm_eo - ctx->req.regex_groups[2].rm_so; + const char *key_s = &ctx->req.path.s[ctx->req.path.groups[2].rm_so]; + key_len = ctx->req.path.groups[2].rm_eo - ctx->req.path.groups[2].rm_so; lsm_str_init_copy_n(&key, key_s, key_len); } @@ -46,12 +47,12 @@ bool lander_insert_entry(http_loop_ctx *ctx) { // TODO free key on error switch (lsm_store_insert(&c_ctx->entry, c_gctx->store, key)) { case lsm_error_already_present: - ctx->res.status = http_conflict; + ctx->res.status = lnm_http_status_conflict; return false; case lsm_error_ok: break; default: - ctx->res.status = http_internal_server_error; + ctx->res.status = lnm_http_status_internal_server_error; return false; } @@ -61,36 +62,35 @@ bool lander_insert_entry(http_loop_ctx *ctx) { buf[0] = '/'; buf[key_len + 1] = '\0'; - http_res_add_header(&ctx->res, http_header_location, buf, true); - ctx->res.status = http_created; + lnm_http_res_add_header(&ctx->res, lnm_http_header_location, buf, true); + ctx->res.status = lnm_http_status_created; return true; } -bool lander_post_redirect(event_loop_conn *conn) { - http_loop_ctx *ctx = conn->ctx; +lnm_http_step_err lander_post_redirect(lnm_http_conn *conn) { + lnm_http_loop_ctx *ctx = conn->ctx; lander_ctx *c_ctx = ctx->c; if (!lander_insert_entry(ctx)) { - conn->state = event_loop_conn_state_res; - return true; + return lnm_http_step_err_res; } lsm_entry_attr_insert_uint8_t(c_ctx->entry, lander_attr_type_entry_type, lander_entry_type_redirect); - return true; + return lnm_http_step_err_done; } -bool lander_post_redirect_body_to_attr(event_loop_conn *conn) { - http_loop_ctx *ctx = conn->ctx; +lnm_http_step_err lander_post_redirect_body_to_attr(lnm_http_conn *conn) { + lnm_http_loop_ctx *ctx = conn->ctx; lander_ctx *c_ctx = ctx->c; lsm_str *attr_value; lsm_str_init_copy_n(&attr_value, ctx->req.body.buf, ctx->req.body.len); lsm_entry_attr_insert(c_ctx->entry, lander_attr_type_url, attr_value); - return true; + return lnm_http_step_err_done; } bool lander_post_paste(event_loop_conn *conn) { diff --git a/src/main.c b/src/main.c index 78ead69..21e8a82 100644 --- a/src/main.c +++ b/src/main.c @@ -19,6 +19,16 @@ lnm_http_loop *loop_init(lander_gctx *gctx) { lnm_http_route_init_literal(&route, lnm_http_method_get, "/", step); lnm_http_loop_route_add(hl, route); + lnm_http_step_init(&step, lander_get_entry); + lnm_http_route_init_regex(&route, lnm_http_method_get, "^/([^/]+)$", 1, step); + lnm_http_loop_route_add(hl, route); + + lnm_http_step_init(&step, lander_post_redirect); + lnm_http_route_init_regex(&route, lnm_http_method_post, "^/s(l?)/([^/]*)$", 2, step); + lnm_http_step_append(&step, step, lnm_http_loop_step_body_to_buf); + lnm_http_step_append(&step, step, lander_post_redirect_body_to_attr); + lnm_http_loop_route_add(hl, route); + return hl; }