diff --git a/include/lander.h b/include/lander.h index c61b7b6..7220559 100644 --- a/include/lander.h +++ b/include/lander.h @@ -42,32 +42,36 @@ void lander_ctx_free(lander_ctx *ctx); lnm_http_step_err lander_get_index(lnm_http_conn *conn); -lnm_http_step_err lander_get_entry(lnm_http_conn *conn); +bool lander_get_entry(event_loop_conn *conn); -lnm_http_step_err lander_post_redirect(lnm_http_conn *conn); +bool lander_post_redirect(event_loop_conn *conn); -lnm_http_step_err lander_post_paste(lnm_http_conn *conn); +bool lander_post_paste(event_loop_conn *conn); -lnm_http_step_err lander_stream_body_to_entry(lnm_http_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); -lnm_http_step_err lander_post_redirect_body_to_attr(lnm_http_conn *conn); +bool lander_post_redirect_body_to_attr(event_loop_conn *conn); -lnm_http_step_err lander_remove_entry(lnm_http_conn *conn); +bool lander_remove_entry(event_loop_conn *conn); -lnm_http_step_err lander_post_file(lnm_http_conn *conn); +bool lander_post_file(event_loop_conn *conn); /** * Store the requested header as an attribute, if it's present. */ -void lander_header_to_attr(lnm_http_loop_ctx *ctx, const char *header, +void lander_header_to_attr(http_loop_ctx *ctx, const char *header, lander_attr_type attr_type); /** * Store the attribute's value as the provided header, if present. */ -void lander_attr_to_header(lnm_http_loop_ctx *ctx, lander_attr_type attr_type, - lnm_http_header header_type); +void lander_attr_to_header(http_loop_ctx *ctx, lander_attr_type attr_type, + http_header header_type); #endif diff --git a/lnm/include/lnm/http/loop.h b/lnm/include/lnm/http/loop.h index 867a93e..482560d 100644 --- a/lnm/include/lnm/http/loop.h +++ b/lnm/include/lnm/http/loop.h @@ -128,6 +128,4 @@ 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 e40d388..5d8f46a 100644 --- a/lnm/include/lnm/http/req.h +++ b/lnm/include/lnm/http/req.h @@ -35,10 +35,8 @@ 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 8122058..2a6649a 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->body.expected_len = lnm_atoi(value, value_len); + req->content_length = 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 deleted file mode 100644 index fde3d70..0000000 --- a/lnm/src/http/lnm_http_loop_steps.c +++ /dev/null @@ -1,24 +0,0 @@ -#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 a601e32..fa6e1da 100644 --- a/lnm/src/http/lnm_http_req.c +++ b/lnm/src/http/lnm_http_req.c @@ -63,10 +63,6 @@ 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 36fd6cd..6d316d2 100644 --- a/src/lander/lander.c +++ b/src/lander/lander.c @@ -84,30 +84,36 @@ void lander_ctx_reset(lander_ctx *ctx) { void lander_ctx_free(lander_ctx *ctx) { free(ctx); } -void lander_header_to_attr(lnm_http_loop_ctx *ctx, const char *header_name, +void lander_header_to_attr(http_loop_ctx *ctx, const char *header_name, lander_attr_type attr_type) { lander_ctx *c_ctx = ctx->c; - const char *header_value; - size_t header_value_len; + for (size_t i = 0; i < ctx->req.num_headers; i++) { + const struct phr_header *header = &ctx->req.headers[i]; - if (lnm_http_req_header_get_s(&header_value, &header_value_len, &ctx->req, - header_name) == lnm_err_ok) { - lsm_str *value; - lsm_str_init_copy_n(&value, (char *)header_value, header_value_len); + if (strncmp(header->name, header_name, header->name_len) == 0) { + if (header->value_len > 0) { + lsm_str *value; + lsm_str_init_copy_n(&value, (char *)header->value, header->value_len); - lsm_entry_attr_insert(c_ctx->entry, attr_type, value); + lsm_entry_attr_insert(c_ctx->entry, attr_type, value); + } + + return; + } } } -void lander_attr_to_header(lnm_http_loop_ctx *ctx, lander_attr_type attr_type, - lnm_http_header header_type) { +void lander_attr_to_header(http_loop_ctx *ctx, lander_attr_type attr_type, + 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) { - lnm_http_res_add_header_len(&ctx->res, header_type, - (char *)lsm_str_ptr(value), lsm_str_len(value), - false); + 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); } } diff --git a/src/lander/lander_delete.c b/src/lander/lander_delete.c index 349fac6..e91b6c9 100644 --- a/src/lander/lander_delete.c +++ b/src/lander/lander_delete.c @@ -1,30 +1,29 @@ -#include "lnm/loop.h" - #include "lander.h" -lnm_http_step_err lander_remove_entry(lnm_http_conn *conn) { - lnm_http_loop_ctx *ctx = conn->ctx; +bool lander_remove_entry(event_loop_conn *conn) { + http_loop_ctx *ctx = conn->ctx; lander_ctx *c_ctx = ctx->c; - lnm_http_loop_gctx *gctx = ctx->g; + http_loop_gctx *gctx = ctx->g; lander_gctx *c_gctx = gctx->c; - 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; + 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_write(&c_ctx->entry, c_gctx->store, key)) { case lsm_error_ok: - lsm_entry_remove(c_ctx->entry); break; case lsm_error_not_found: - ctx->res.status = lnm_http_status_not_found; - break; + ctx->res.status = http_not_found; + return true; default: - ctx->res.status = lnm_http_status_internal_server_error; - break; + ctx->res.status = http_internal_server_error; + return true; } - return lnm_http_step_err_done; + lsm_entry_remove(c_ctx->entry); + + return true; } diff --git a/src/lander/lander_get.c b/src/lander/lander_get.c index 1bf4da0..3d05c4d 100644 --- a/src/lander/lander_get.c +++ b/src/lander/lander_get.c @@ -1,7 +1,5 @@ #include -#include "lnm/http/consts.h" -#include "lnm/http/loop.h" #include "lnm/loop.h" #include "event_loop.h" @@ -26,14 +24,17 @@ 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; } -lnm_http_step_err lander_get_redirect(lnm_http_conn *conn) { - lnm_http_loop_ctx *ctx = conn->ctx; +void lander_get_redirect(event_loop_conn *conn) { + http_loop_ctx *ctx = conn->ctx; lander_ctx *c_ctx = ctx->c; // For redirects, the URL is stored as an in-memory attribute @@ -44,55 +45,43 @@ lnm_http_step_err lander_get_redirect(lnm_http_conn *conn) { lsm_error_ok) { error("Entry of type redirect detected without URL attribute"); - ctx->res.status = lnm_http_status_internal_server_error; + ctx->res.status = http_internal_server_error; lsm_entry_close(c_ctx->entry); c_ctx->entry = NULL; - return lnm_http_step_err_res; + return; } - 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); + char *buf = malloc(lsm_str_len(url_attr_val) + 1); + memcpy(buf, lsm_str_ptr(url_attr_val), lsm_str_len(url_attr_val)); - ctx->res.status = lnm_http_status_moved_permanently; + buf[lsm_str_len(url_attr_val)] = '\0'; - return lnm_http_step_err_done; + 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; } -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; +void lander_get_paste(event_loop_conn *conn) { + http_loop_ctx *ctx = conn->ctx; lander_ctx *c_ctx = ctx->c; - lsm_entry_data_read(written, buf, c_ctx->entry, len); - - return lnm_err_ok; + ctx->res.body.expected_len = lsm_entry_data_len(c_ctx->entry); + http_res_set_mime_type(&ctx->res, http_mime_txt); } -lnm_http_step_err lander_get_paste(lnm_http_conn *conn) { - lnm_http_loop_ctx *ctx = conn->ctx; +void lander_get_file(event_loop_conn *conn) { + 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)); - 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)); + ctx->res.body.expected_len = lsm_entry_data_len(c_ctx->entry); lander_attr_to_header(ctx, lander_attr_type_content_type, - lnm_http_header_content_type); + http_header_content_type); lsm_str *value; char *buf; @@ -101,26 +90,23 @@ lnm_http_step_err lander_get_file(lnm_http_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"); } - lnm_http_res_add_header(&ctx->res, lnm_http_header_content_disposition, buf, - true); - - return lnm_http_step_err_done; + http_res_add_header(&ctx->res, http_header_content_disposition, buf, true); } -lnm_http_step_err lander_get_entry(lnm_http_conn *conn) { - lnm_http_loop_ctx *ctx = conn->ctx; +bool lander_get_entry(event_loop_conn *conn) { + http_loop_ctx *ctx = conn->ctx; lander_ctx *c_ctx = ctx->c; - lnm_http_loop_gctx *gctx = ctx->g; + http_loop_gctx *gctx = ctx->g; lander_gctx *c_gctx = gctx->c; - 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; + 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); @@ -129,32 +115,32 @@ lnm_http_step_err lander_get_entry(lnm_http_conn *conn) { case lsm_error_ok: break; case lsm_error_not_found: - ctx->res.status = lnm_http_status_not_found; - return lnm_http_step_err_res; + ctx->res.status = http_not_found; + conn->state = event_loop_conn_state_res; + return true; default: - ctx->res.status = lnm_http_status_internal_server_error; - return lnm_http_step_err_res; + ctx->res.status = http_internal_server_error; + conn->state = event_loop_conn_state_res; + return true; } 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: - res = lander_get_redirect(conn); + lander_get_redirect(conn); break; case lander_entry_type_paste: - res = lander_get_paste(conn); + lander_get_paste(conn); break; case lander_entry_type_file: - res = lander_get_file(conn); + lander_get_file(conn); break; } - return res; + return true; } 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 882cb04..9711d03 100644 --- a/src/lander/lander_post.c +++ b/src/lander/lander_post.c @@ -1,7 +1,6 @@ #include "http/res.h" #include "http/types.h" #include "lander.h" -#include "lnm/loop.h" #include "log.h" #include "lsm/store.h" @@ -20,26 +19,26 @@ static void randomize_key(char *key, int len) { * * @return true on success, false otherwise */ -bool lander_insert_entry(lnm_http_loop_ctx *ctx) { - lnm_http_loop_gctx *gctx = ctx->g; +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.path.groups[2].rm_eo == ctx->req.path.groups[2].rm_so) { + 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.path.groups[1].rm_eo - ctx->req.path.groups[1].rm_so) == 1; + (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 { - 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; + 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; lsm_str_init_copy_n(&key, key_s, key_len); } @@ -47,12 +46,12 @@ bool lander_insert_entry(lnm_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 = lnm_http_status_conflict; + ctx->res.status = http_conflict; return false; case lsm_error_ok: break; default: - ctx->res.status = lnm_http_status_internal_server_error; + ctx->res.status = http_internal_server_error; return false; } @@ -62,58 +61,61 @@ bool lander_insert_entry(lnm_http_loop_ctx *ctx) { buf[0] = '/'; buf[key_len + 1] = '\0'; - lnm_http_res_add_header(&ctx->res, lnm_http_header_location, buf, true); - ctx->res.status = lnm_http_status_created; + http_res_add_header(&ctx->res, http_header_location, buf, true); + ctx->res.status = http_created; return true; } -lnm_http_step_err lander_post_redirect(lnm_http_conn *conn) { - lnm_http_loop_ctx *ctx = conn->ctx; +bool lander_post_redirect(event_loop_conn *conn) { + http_loop_ctx *ctx = conn->ctx; lander_ctx *c_ctx = ctx->c; if (!lander_insert_entry(ctx)) { - return lnm_http_step_err_res; + conn->state = event_loop_conn_state_res; + return true; } lsm_entry_attr_insert_uint8_t(c_ctx->entry, lander_attr_type_entry_type, lander_entry_type_redirect); - return lnm_http_step_err_done; + return true; } -lnm_http_step_err lander_post_redirect_body_to_attr(lnm_http_conn *conn) { - lnm_http_loop_ctx *ctx = conn->ctx; +bool lander_post_redirect_body_to_attr(event_loop_conn *conn) { + 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 lnm_http_step_err_done; + return true; } -lnm_http_step_err lander_post_paste(lnm_http_conn *conn) { - lnm_http_loop_ctx *ctx = conn->ctx; +bool lander_post_paste(event_loop_conn *conn) { + http_loop_ctx *ctx = conn->ctx; lander_ctx *c_ctx = ctx->c; if (!lander_insert_entry(ctx)) { - return lnm_http_step_err_res; + conn->state = event_loop_conn_state_res; + return true; } lsm_entry_attr_insert_uint8_t(c_ctx->entry, lander_attr_type_entry_type, lander_entry_type_paste); lander_header_to_attr(ctx, "X-Lander-Filename", lander_attr_type_file_name); - return lnm_http_step_err_done; + return true; } -lnm_http_step_err lander_post_file(lnm_http_conn *conn) { - lnm_http_loop_ctx *ctx = conn->ctx; +bool lander_post_file(event_loop_conn *conn) { + http_loop_ctx *ctx = conn->ctx; lander_ctx *c_ctx = ctx->c; if (!lander_insert_entry(ctx)) { - return lnm_http_step_err_res; + conn->state = event_loop_conn_state_res; + return true; } lsm_entry_attr_insert_uint8_t(c_ctx->entry, lander_attr_type_entry_type, @@ -122,5 +124,5 @@ lnm_http_step_err lander_post_file(lnm_http_conn *conn) { lander_attr_type_content_type); lander_header_to_attr(ctx, "X-Lander-Filename", lander_attr_type_file_name); - return lnm_http_step_err_done; + return true; } diff --git a/src/lander/lander_steps.c b/src/lander/lander_steps.c index 6021dbd..7804df5 100644 --- a/src/lander/lander_steps.c +++ b/src/lander/lander_steps.c @@ -1,26 +1,22 @@ #include #include "lander.h" -#include "lnm/http/loop.h" -#include "lnm/loop.h" -lnm_http_step_err lander_stream_body_to_entry(lnm_http_conn *conn) { - lnm_http_loop_ctx *ctx = conn->ctx; +bool lander_stream_body_to_entry(event_loop_conn *conn) { + http_loop_ctx *ctx = conn->ctx; lander_ctx *c_ctx = ctx->c; uint64_t to_append = - MIN(conn->r.size - conn->r.read, + 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->r.buf[conn->r.read], to_append); + lsm_str_init_copy_n(&data, (char *)&conn->rbuf[conn->rbuf_read], to_append); lsm_entry_data_append(c_ctx->entry, data); - conn->r.read += to_append; + conn->rbuf_read += to_append; lsm_str_free(data); - return lsm_entry_data_len(c_ctx->entry) == ctx->req.body.expected_len - ? lnm_http_step_err_done - : lnm_http_step_err_io_needed; + return lsm_entry_data_len(c_ctx->entry) == ctx->req.body.expected_len; } diff --git a/src/main.c b/src/main.c index 3af1d2f..78ead69 100644 --- a/src/main.c +++ b/src/main.c @@ -19,34 +19,6 @@ 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); - - lnm_http_step_init(&step, lander_post_paste); - lnm_http_route_init_regex(&route, lnm_http_method_post, "^/p(l?)/([^/]*)$", 2, - step); - lnm_http_step_append(&step, step, lander_stream_body_to_entry); - lnm_http_loop_route_add(hl, route); - - lnm_http_step_init(&step, lander_post_file); - lnm_http_route_init_regex(&route, lnm_http_method_post, "^/f(l?)/([^/]*)$", 2, - step); - lnm_http_step_append(&step, step, lander_stream_body_to_entry); - lnm_http_loop_route_add(hl, route); - - lnm_http_step_init(&step, lander_remove_entry); - lnm_http_route_init_regex(&route, lnm_http_method_delete, "^/([^/]+)$", 1, - step); - lnm_http_loop_route_add(hl, route); - return hl; }