feat(lander): implement redirect posting using lnm

lnm
Jef Roosens 2023-12-05 19:12:19 +01:00
parent ad243ea9f5
commit f3da5c78ef
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
10 changed files with 123 additions and 81 deletions

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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;
}

View File

@ -0,0 +1,24 @@
#include <string.h>
#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;
}

View File

@ -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));
}

View File

@ -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);
}
}

View File

@ -1,5 +1,7 @@
#include <stdio.h>
#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) {

View File

@ -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) {

View File

@ -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;
}