feat(lnm): move request data to separate buffer if read buffer changes
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details

new-lnm-integration
Jef Roosens 2023-12-22 20:06:06 +01:00
parent d53a949946
commit 8bdf52da0d
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
6 changed files with 88 additions and 24 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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