feat(lander): initial integration of lsm

lsm
Jef Roosens 2023-11-03 14:10:14 +01:00
parent 8b6d1f6e91
commit 711eaa2bde
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
5 changed files with 238 additions and 8 deletions

View File

@ -2,18 +2,27 @@
#define LANDER
#include "http_loop.h"
#include "lsm/store.h"
extern http_route lander_routes[4];
typedef struct lander_gctx {
const char *data_dir;
Trie *trie;
lsm_store *store;
} lander_gctx;
typedef struct lander_ctx {
char *key;
lsm_entry_handle *entry;
uint64_t remaining_data;
} lander_ctx;
typedef enum lander_entry_type {
lander_entry_type_redirect = 0,
lander_entry_type_paste = 1,
} lander_entry_type;
void *lander_gctx_init();
void *lander_ctx_init();
@ -30,4 +39,14 @@ bool lander_post_redirect(event_loop_conn *conn);
bool lander_post_paste(event_loop_conn *conn);
bool lander_post_paste_lsm(event_loop_conn *conn);
bool lander_post_redirect_lsm(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_get_entry_lsm(event_loop_conn *conn);
#endif

View File

@ -1,6 +1,8 @@
#include <stdio.h>
#include "http_loop.h"
#include "lander.h"
#include "lsm/store.h"
http_route lander_routes[] = {
{.type = http_route_literal,
@ -13,24 +15,24 @@ http_route lander_routes[] = {
.type = http_route_regex,
.method = http_get,
.path = "^/([^/]+)$",
.steps = {lander_get_entry, NULL},
.steps_res = {http_loop_step_write_header, http_loop_step_write_body,
.steps = {lander_get_entry_lsm, NULL},
.steps_res = {http_loop_step_write_header, lander_stream_body_to_client,
NULL},
},
{
.type = http_route_regex,
.method = http_post,
.path = "^/s(l?)/([^/]*)$",
.steps = {http_loop_step_auth, http_loop_step_body_to_buf,
lander_post_redirect, NULL},
.steps = {http_loop_step_auth, http_loop_step_parse_content_length,
lander_post_redirect_lsm, lander_stream_body_to_entry, NULL},
.steps_res = {http_loop_step_write_header, http_loop_step_write_body,
NULL},
},
{.type = http_route_regex,
.method = http_post,
.path = "^/p(l?)/([^/]*)$",
.steps = {http_loop_step_auth, lander_post_paste,
http_loop_step_body_to_file, http_loop_step_switch_res, NULL},
.steps = {http_loop_step_auth, http_loop_step_parse_content_length,
lander_post_paste_lsm, lander_stream_body_to_entry, NULL},
.steps_res = {http_loop_step_write_header, http_loop_step_write_body,
NULL}},
};
@ -39,6 +41,14 @@ void *lander_gctx_init() { return calloc(1, sizeof(lander_gctx)); }
void *lander_ctx_init() { return calloc(1, sizeof(lander_ctx)); }
void lander_ctx_reset(lander_ctx *ctx) {}
void lander_ctx_reset(lander_ctx *ctx) {
if (ctx->entry != NULL) {
lsm_entry_close(ctx->entry);
ctx->entry = NULL;
}
ctx->remaining_data = 0;
}
void lander_ctx_free(lander_ctx *ctx) { free(ctx); }

View File

@ -1,6 +1,9 @@
#include <stdio.h>
#include "event_loop.h"
#include "http/types.h"
#include "lander.h"
#include "lsm/store.h"
static const char index_page[] =
"<!DOCTYPE html>\n"
@ -50,3 +53,82 @@ bool lander_get_entry(event_loop_conn *conn) {
return true;
}
bool lander_get_entry_lsm(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx;
lander_ctx *c_ctx = ctx->c;
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;
lsm_str *key;
lsm_str_init_copy_n(&key, (char *)key_s, key_len);
switch (lsm_store_open_read(&c_ctx->entry, c_gctx->store, key)) {
case lsm_error_ok:
break;
case lsm_error_not_found:
ctx->res.status = http_not_found;
return true;
default:
ctx->res.status = http_internal_server_error;
return true;
}
lander_entry_type t;
lsm_entry_attr_get_num((uint64_t *)&t, c_ctx->entry,
lsm_attr_type_entry_type);
if (t == lander_entry_type_redirect) {
// Stream entire redirect data into buffer to set as header
uint64_t data_len = lsm_entry_data_len(c_ctx->entry);
char *buf = malloc(data_len + 1);
uint64_t read = 0;
uint64_t total_read = 0;
while (total_read < data_len) {
lsm_entry_data_read(&read, &buf[total_read], c_ctx->entry,
data_len - total_read);
total_read += read;
}
buf[data_len] = '\0';
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;
} else {
ctx->res.body.expected_len = lsm_entry_data_len(c_ctx->entry);
}
return true;
}
bool lander_stream_body_to_client(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx;
lander_ctx *c_ctx = ctx->c;
if ((c_ctx->entry == NULL) ||
(ctx->res.body.expected_len == ctx->res.body.len)) {
return true;
}
uint64_t to_write = MIN(EVENT_LOOP_BUFFER_SIZE - conn->wbuf_size,
ctx->res.body.expected_len - ctx->res.body.len);
uint64_t read = 0;
lsm_entry_data_read(&read, (char *)&conn->wbuf[conn->wbuf_size], c_ctx->entry,
to_write);
ctx->res.body.len += read;
conn->wbuf_size += read;
return false;
}

View File

@ -1,6 +1,16 @@
#include "http/res.h"
#include "http/types.h"
#include "lander.h"
#include "log.h"
#include "lsm/store.h"
static void randomize_key(char *key, int len) {
for (int i = 0; i < len; i++) {
key[i] = charset[rand() % charset_len];
}
key[len] = '\0';
}
// TODO entry leaks if key is already present
static bool add_entry(char **key_ptr, int *key_len_ptr, http_loop_ctx *ctx,
@ -60,6 +70,110 @@ static bool add_entry(char **key_ptr, int *key_len_ptr, http_loop_ctx *ctx,
return true;
}
/**
* Insert a new entry into the store.
*
* @return true on success, false otherwise
*/
bool lander_insert_entry(http_loop_ctx *ctx) {
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) {
// Generate a random key to insert
bool secure =
(ctx->req.regex_groups[1].rm_eo - ctx->req.regex_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 {
char *key_s = (char *)&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;
lsm_str_init_copy_n(&key, key_s, key_len);
}
// 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;
return false;
case lsm_error_ok:
break;
default:
ctx->res.status = http_internal_server_error;
return false;
}
// Add location header
char *buf = malloc(key_len + 2);
memcpy(&buf[1], lsm_str_ptr(key), key_len);
buf[0] = '/';
buf[key_len + 1] = '\0';
http_res_add_header(&ctx->res, http_header_location, buf, true);
ctx->res.status = http_created;
return true;
}
bool lander_post_redirect_lsm(event_loop_conn *conn) {
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;
}
lsm_entry_attr_insert_num(c_ctx->entry, lsm_attr_type_entry_type,
lander_entry_type_redirect);
return true;
}
bool lander_post_paste_lsm(event_loop_conn *conn) {
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;
}
lsm_entry_attr_insert_num(c_ctx->entry, lsm_attr_type_entry_type,
lander_entry_type_paste);
return true;
}
bool lander_stream_body_to_entry(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx;
lander_ctx *c_ctx = ctx->c;
http_loop_gctx *gctx = ctx->g;
lander_gctx *c_gctx = gctx->c;
uint64_t to_append =
MIN(conn->rbuf_size - conn->rbuf_read,
ctx->req.body.expected_len - lsm_entry_data_len(c_ctx->entry));
lsm_str *data;
lsm_str_init_copy_n(&data, (char *)&conn->rbuf[conn->rbuf_read], to_append);
lsm_entry_data_append(c_gctx->store, c_ctx->entry, data);
conn->rbuf_read += to_append;
lsm_str_free(data);
return lsm_entry_data_len(c_ctx->entry) == ctx->req.body.expected_len;
}
bool lander_post_redirect(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx;
bool random =

View File

@ -49,6 +49,11 @@ int main() {
c_gctx->data_dir = data_dir;
c_gctx->trie = trie;
lsm_str *db_path, *data_dir2;
lsm_str_init_copy(&db_path, "data/store.db");
lsm_str_init_copy(&data_dir2, "data");
lsm_store_load(&c_gctx->store, db_path, data_dir2);
http_loop *hl = http_loop_init(
lander_routes, sizeof(lander_routes) / sizeof(lander_routes[0]), c_gctx,
lander_ctx_init, (void (*)(void *))lander_ctx_reset,