feat(lnm): move request data to separate buffer if read buffer changes
parent
d53a949946
commit
8bdf52da0d
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue