feat(lander): implement redirect posting using lnm
parent
ad243ea9f5
commit
f3da5c78ef
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
10
src/main.c
10
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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue