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_REQ_HEADERS 32
|
||||||
#define LNM_HTTP_MAX_REGEX_GROUPS 4
|
#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
|
* Represents the parsed HTTP request
|
||||||
*/
|
*/
|
||||||
typedef struct lnm_http_req {
|
typedef struct lnm_http_req {
|
||||||
size_t len;
|
struct {
|
||||||
|
char *s;
|
||||||
|
size_t len;
|
||||||
|
bool owned;
|
||||||
|
} buf;
|
||||||
int minor_version;
|
int minor_version;
|
||||||
lnm_http_method method;
|
lnm_http_method method;
|
||||||
struct {
|
struct {
|
||||||
const char *s;
|
size_t o;
|
||||||
size_t len;
|
size_t len;
|
||||||
regmatch_t groups[LNM_HTTP_MAX_REGEX_GROUPS];
|
regmatch_t groups[LNM_HTTP_MAX_REGEX_GROUPS];
|
||||||
} path;
|
} path;
|
||||||
struct {
|
struct {
|
||||||
const char *s;
|
size_t o;
|
||||||
size_t len;
|
size_t len;
|
||||||
} query;
|
} query;
|
||||||
struct {
|
struct {
|
||||||
struct phr_header arr[LNM_HTTP_MAX_REQ_HEADERS];
|
lnm_http_req_header arr[LNM_HTTP_MAX_REQ_HEADERS];
|
||||||
size_t len;
|
size_t len;
|
||||||
} headers;
|
} headers;
|
||||||
struct {
|
struct {
|
||||||
|
@ -68,6 +83,9 @@ void lnm_http_req_reset(lnm_http_req *req);
|
||||||
/**
|
/**
|
||||||
* Retrieve a specific header from the request.
|
* 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 where to write pointer to header value
|
||||||
* @param out_len where to store length of out value
|
* @param out_len where to store length of out value
|
||||||
* @param req request to look for header in
|
* @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.
|
* 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 where to write pointer to header value
|
||||||
* @param out_len where to store length of out value
|
* @param out_len where to store length of out value
|
||||||
* @param req request to look for header in
|
* @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) {
|
switch (res) {
|
||||||
case lnm_http_parse_err_ok:
|
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;
|
ctx->state = lnm_http_loop_state_route;
|
||||||
|
|
||||||
lnm_linfo(section, "%s %.*s HTTP/1.%i",
|
lnm_linfo(section, "%s %.*s HTTP/1.%i",
|
||||||
lnm_http_method_names[ctx->req.method], (int)ctx->req.path.len,
|
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;
|
break;
|
||||||
case lnm_http_parse_err_incomplete:
|
case lnm_http_parse_err_incomplete:
|
||||||
// If the request is already the size of the read buffer, we close the
|
// 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) {
|
switch (route->type) {
|
||||||
case lnm_http_route_type_literal:
|
case lnm_http_route_type_literal:
|
||||||
matched_path =
|
matched_path = strncmp(route->route.s, ctx->req.buf.s + ctx->req.path.o,
|
||||||
strncmp(route->route.s, ctx->req.path.s, ctx->req.path.len) == 0;
|
ctx->req.path.len) == 0;
|
||||||
break;
|
break;
|
||||||
case lnm_http_route_type_regex:
|
case lnm_http_route_type_regex:
|
||||||
matched_path =
|
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;
|
LNM_HTTP_MAX_REGEX_GROUPS, ctx->req.path.groups, 0) == 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -345,7 +345,7 @@ lnm_loop_state state_map[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
void lnm_http_loop_process(lnm_http_conn *conn) {
|
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_http_loop_state http_loop_state;
|
||||||
lnm_loop_state loop_state = conn->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
|
// Check required to prevent overwriting manually set event loop state
|
||||||
conn->state = conn->state == loop_state ? state_map[ctx->state] : conn->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;
|
const char *method;
|
||||||
char *path;
|
char *path;
|
||||||
size_t method_len, path_len;
|
size_t method_len, path_len;
|
||||||
|
size_t num_headers = LNM_HTTP_MAX_REQ_HEADERS;
|
||||||
req->headers.len = LNM_HTTP_MAX_REQ_HEADERS;
|
struct phr_header headers[LNM_HTTP_MAX_REQ_HEADERS];
|
||||||
|
|
||||||
int req_len = phr_parse_request(
|
int req_len = phr_parse_request(
|
||||||
buf, len, &method, &method_len, (const char **)&path, &path_len,
|
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) {
|
if (req_len == -1) {
|
||||||
req->len = len;
|
req->buf.len = len;
|
||||||
|
|
||||||
return lnm_http_parse_err_invalid;
|
return lnm_http_parse_err_invalid;
|
||||||
} else if (req_len == -2) {
|
} 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
|
// 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)) {
|
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);
|
req->query.len = path_len - (question_mark + 1 - path);
|
||||||
|
|
||||||
path_len = question_mark - 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';
|
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.len = path_len;
|
||||||
req->path.s = path;
|
req->path.o = path - buf;
|
||||||
req->len = req_len;
|
|
||||||
|
req->buf.len = req_len;
|
||||||
|
req->buf.s = buf;
|
||||||
|
req->buf.owned = false;
|
||||||
|
|
||||||
return lnm_http_parse_err_ok;
|
return lnm_http_parse_err_ok;
|
||||||
}
|
}
|
||||||
|
@ -68,6 +81,10 @@ void lnm_http_req_reset(lnm_http_req *req) {
|
||||||
free(req->body.buf);
|
free(req->body.buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (req->buf.owned) {
|
||||||
|
free(req->buf.s);
|
||||||
|
}
|
||||||
|
|
||||||
memset(req, 0, sizeof(lnm_http_req));
|
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);
|
size_t name_len = strlen(name);
|
||||||
|
|
||||||
for (size_t i = 0; i < req->headers.len; i++) {
|
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)) {
|
if (lnm_strnieq(req->buf.s + header->name.o, header->name.len, name,
|
||||||
*out = header->value;
|
name_len)) {
|
||||||
*out_len = header->value_len;
|
*out = req->buf.s + header->value.o;
|
||||||
|
*out_len = header->value.len;
|
||||||
|
|
||||||
return lnm_err_ok;
|
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;
|
lnm_http_loop_gctx *gctx = ctx->g;
|
||||||
lander_gctx *c_gctx = gctx->c;
|
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;
|
int key_len = ctx->req.path.groups[1].rm_eo - ctx->req.path.groups[1].rm_so;
|
||||||
|
|
||||||
lsm_str *key;
|
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;
|
lnm_http_loop_gctx *gctx = ctx->g;
|
||||||
lander_gctx *c_gctx = gctx->c;
|
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;
|
int key_len = ctx->req.path.groups[1].rm_eo - ctx->req.path.groups[1].rm_so;
|
||||||
|
|
||||||
lsm_str *key;
|
lsm_str *key;
|
||||||
|
|
|
@ -38,7 +38,8 @@ bool lander_insert_entry(lnm_http_loop_ctx *ctx) {
|
||||||
randomize_key(key_s, key_len);
|
randomize_key(key_s, key_len);
|
||||||
lsm_str_init(&key, key_s);
|
lsm_str_init(&key, key_s);
|
||||||
} else {
|
} 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;
|
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);
|
lsm_str_init_copy_n(&key, key_s, key_len);
|
||||||
|
|
Loading…
Reference in New Issue