From 8bdf52da0d7a1d4ea5598adba8fdb1a55bdd62a7 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Fri, 22 Dec 2023 20:06:06 +0100 Subject: [PATCH] feat(lnm): move request data to separate buffer if read buffer changes --- lnm/include/lnm/http/req.h | 29 +++++++++++++++++--- lnm/src/http/lnm_http_loop_process.c | 34 ++++++++++++++++++----- lnm/src/http/lnm_http_req.c | 40 ++++++++++++++++++++-------- src/lander/lander_delete.c | 3 ++- src/lander/lander_get.c | 3 ++- src/lander/lander_post.c | 3 ++- 6 files changed, 88 insertions(+), 24 deletions(-) diff --git a/lnm/include/lnm/http/req.h b/lnm/include/lnm/http/req.h index e40d388..06f2743 100644 --- a/lnm/include/lnm/http/req.h +++ b/lnm/include/lnm/http/req.h @@ -13,24 +13,39 @@ #define LNM_HTTP_MAX_REQ_HEADERS 32 #define LNM_HTTP_MAX_REGEX_GROUPS 4 +typedef struct lnm_http_req_header { + struct { + size_t o; + size_t len; + } name; + struct { + size_t o; + size_t len; + } value; +} lnm_http_req_header; + /** * Represents the parsed HTTP request */ typedef struct lnm_http_req { - size_t len; + struct { + char *s; + size_t len; + bool owned; + } buf; int minor_version; lnm_http_method method; struct { - const char *s; + size_t o; size_t len; regmatch_t groups[LNM_HTTP_MAX_REGEX_GROUPS]; } path; struct { - const char *s; + size_t o; size_t len; } query; struct { - struct phr_header arr[LNM_HTTP_MAX_REQ_HEADERS]; + lnm_http_req_header arr[LNM_HTTP_MAX_REQ_HEADERS]; size_t len; } headers; struct { @@ -68,6 +83,9 @@ void lnm_http_req_reset(lnm_http_req *req); /** * Retrieve a specific header from the request. * + * Pointers retrieved from this function should never be used between step + * functions; simply request the header again if you need to. + * * @param out where to write pointer to header value * @param out_len where to store length of out value * @param req request to look for header in @@ -79,6 +97,9 @@ lnm_err lnm_http_req_header_get(const char **out, size_t *out_len, /** * Retrieve a specific header from the request by specifying its name. * + * Pointers retrieved from this function should never be used between step + * functions; simply request the header again if you need to. + * * @param out where to write pointer to header value * @param out_len where to store length of out value * @param req request to look for header in diff --git a/lnm/src/http/lnm_http_loop_process.c b/lnm/src/http/lnm_http_loop_process.c index 63017f1..e16e18c 100644 --- a/lnm/src/http/lnm_http_loop_process.c +++ b/lnm/src/http/lnm_http_loop_process.c @@ -25,12 +25,12 @@ void lnm_http_loop_process_parse_req(lnm_http_conn *conn) { switch (res) { case lnm_http_parse_err_ok: - conn->r.read += ctx->req.len; + conn->r.read += ctx->req.buf.len; ctx->state = lnm_http_loop_state_route; lnm_linfo(section, "%s %.*s HTTP/1.%i", lnm_http_method_names[ctx->req.method], (int)ctx->req.path.len, - ctx->req.path.s, ctx->req.minor_version); + ctx->req.buf.s + ctx->req.path.o, ctx->req.minor_version); break; case lnm_http_parse_err_incomplete: // If the request is already the size of the read buffer, we close the @@ -70,12 +70,12 @@ void lnm_http_loop_process_route(lnm_http_conn *conn) { switch (route->type) { case lnm_http_route_type_literal: - matched_path = - strncmp(route->route.s, ctx->req.path.s, ctx->req.path.len) == 0; + matched_path = strncmp(route->route.s, ctx->req.buf.s + ctx->req.path.o, + ctx->req.path.len) == 0; break; case lnm_http_route_type_regex: matched_path = - regexec(route->route.regex, ctx->req.path.s, + regexec(route->route.regex, ctx->req.buf.s + ctx->req.path.o, LNM_HTTP_MAX_REGEX_GROUPS, ctx->req.path.groups, 0) == 0; break; } @@ -345,7 +345,7 @@ lnm_loop_state state_map[] = { }; void lnm_http_loop_process(lnm_http_conn *conn) { - const lnm_http_loop_ctx *ctx = conn->ctx; + lnm_http_loop_ctx *ctx = conn->ctx; lnm_http_loop_state http_loop_state; lnm_loop_state loop_state = conn->state; @@ -366,4 +366,26 @@ void lnm_http_loop_process(lnm_http_conn *conn) { // Check required to prevent overwriting manually set event loop state conn->state = conn->state == loop_state ? state_map[ctx->state] : conn->state; + + // We move the request to a dedicated buffer if the read buffer needs to be + // reused + if ((conn->state == lnm_loop_state_req) && (conn->state == loop_state) && + (!ctx->req.buf.owned) && (ctx->req.buf.len > 0)) { + char *buf = malloc(ctx->req.buf.len); + + if (buf == NULL) { + lnm_lerror(section, + "Failed to allocate request buffer; closing connection %i", + conn->fd); + + conn->state = lnm_loop_state_end; + } else { + memcpy(buf, ctx->req.buf.s, ctx->req.buf.len); + ctx->req.buf.s = buf; + ctx->req.buf.owned = true; + + lnm_ldebug(section, "Allocated request buffer for connection %i", + conn->fd); + } + } } diff --git a/lnm/src/http/lnm_http_req.c b/lnm/src/http/lnm_http_req.c index 853a33e..50be3af 100644 --- a/lnm/src/http/lnm_http_req.c +++ b/lnm/src/http/lnm_http_req.c @@ -11,15 +11,15 @@ lnm_http_parse_err lnm_http_req_parse(lnm_http_req *req, char *buf, const char *method; char *path; size_t method_len, path_len; - - req->headers.len = LNM_HTTP_MAX_REQ_HEADERS; + size_t num_headers = LNM_HTTP_MAX_REQ_HEADERS; + struct phr_header headers[LNM_HTTP_MAX_REQ_HEADERS]; int req_len = phr_parse_request( buf, len, &method, &method_len, (const char **)&path, &path_len, - &req->minor_version, req->headers.arr, &req->headers.len, req->len); + &req->minor_version, headers, &num_headers, req->buf.len); if (req_len == -1) { - req->len = len; + req->buf.len = len; return lnm_http_parse_err_invalid; } else if (req_len == -2) { @@ -45,7 +45,7 @@ lnm_http_parse_err lnm_http_req_parse(lnm_http_req *req, char *buf, // Only store query if the path doesn't simply end with a question mark if ((question_mark != NULL) && (path_len - (question_mark + 1 - path) > 0)) { - req->query.s = question_mark + 1; + req->query.o = question_mark + 1 - buf; req->query.len = path_len - (question_mark + 1 - path); path_len = question_mark - path; @@ -56,9 +56,22 @@ lnm_http_parse_err lnm_http_req_parse(lnm_http_req *req, char *buf, path[path_len] = '\0'; } + // Also migrate headers to offset-based + for (size_t i = 0; i < num_headers; i++) { + req->headers.arr[i].name.o = headers[i].name - buf; + req->headers.arr[i].name.len = headers[i].name_len; + req->headers.arr[i].value.o = headers[i].value - buf; + req->headers.arr[i].value.len = headers[i].value_len; + } + + req->headers.len = num_headers; + req->path.len = path_len; - req->path.s = path; - req->len = req_len; + req->path.o = path - buf; + + req->buf.len = req_len; + req->buf.s = buf; + req->buf.owned = false; return lnm_http_parse_err_ok; } @@ -68,6 +81,10 @@ void lnm_http_req_reset(lnm_http_req *req) { free(req->body.buf); } + if (req->buf.owned) { + free(req->buf.s); + } + memset(req, 0, sizeof(lnm_http_req)); } @@ -82,11 +99,12 @@ lnm_err lnm_http_req_header_get_s(const char **out, size_t *out_len, size_t name_len = strlen(name); for (size_t i = 0; i < req->headers.len; i++) { - const struct phr_header *header = &req->headers.arr[i]; + const lnm_http_req_header *header = &req->headers.arr[i]; - if (lnm_strnieq(header->name, header->name_len, name, name_len)) { - *out = header->value; - *out_len = header->value_len; + if (lnm_strnieq(req->buf.s + header->name.o, header->name.len, name, + name_len)) { + *out = req->buf.s + header->value.o; + *out_len = header->value.len; return lnm_err_ok; } diff --git a/src/lander/lander_delete.c b/src/lander/lander_delete.c index 349fac6..f773ab5 100644 --- a/src/lander/lander_delete.c +++ b/src/lander/lander_delete.c @@ -8,7 +8,8 @@ lnm_http_step_err lander_remove_entry(lnm_http_conn *conn) { lnm_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]; + const char *key_s = + &ctx->req.buf.s[ctx->req.path.o + 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; diff --git a/src/lander/lander_get.c b/src/lander/lander_get.c index 0e90086..f9d085f 100644 --- a/src/lander/lander_get.c +++ b/src/lander/lander_get.c @@ -118,7 +118,8 @@ lnm_http_step_err lander_get_entry(lnm_http_conn *conn) { lnm_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]; + const char *key_s = + &ctx->req.buf.s[ctx->req.path.o + 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; diff --git a/src/lander/lander_post.c b/src/lander/lander_post.c index 1244cb7..8b14410 100644 --- a/src/lander/lander_post.c +++ b/src/lander/lander_post.c @@ -38,7 +38,8 @@ bool lander_insert_entry(lnm_http_loop_ctx *ctx) { 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]; + const char *key_s = + &ctx->req.buf.s[ctx->req.path.o + 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);