132 lines
3.6 KiB
C
132 lines
3.6 KiB
C
#include <stdbool.h>
|
|
#include <string.h>
|
|
|
|
#include "lnm/common.h"
|
|
#include "lnm/http/consts.h"
|
|
#include "lnm/http/loop.h"
|
|
#include "lnm/http/req.h"
|
|
|
|
lnm_http_parse_err lnm_http_req_parse(lnm_http_req *req, char *buf,
|
|
size_t len) {
|
|
const char *method;
|
|
char *path;
|
|
size_t method_len, path_len;
|
|
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, headers, &num_headers, req->buf.len);
|
|
|
|
if (req_len == -1) {
|
|
req->buf.len = len;
|
|
|
|
return lnm_http_parse_err_invalid;
|
|
} else if (req_len == -2) {
|
|
return lnm_http_parse_err_incomplete;
|
|
}
|
|
|
|
bool known_method = false;
|
|
|
|
for (size_t i = 0; i < lnm_http_method_names_len && !known_method; i++) {
|
|
if (strncmp(method, lnm_http_method_names[i], method_len) == 0) {
|
|
req->method = i;
|
|
known_method = true;
|
|
}
|
|
}
|
|
|
|
if (!known_method) {
|
|
return lnm_http_parse_err_unknown_method;
|
|
}
|
|
|
|
// Path will always end with a newline, which we can safely set to nul
|
|
path[path_len] = '\0';
|
|
char *question_mark = strchr(path, '?');
|
|
|
|
// 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.o = question_mark + 1 - buf;
|
|
req->query.len = path_len - (question_mark + 1 - path);
|
|
|
|
path_len = question_mark - path;
|
|
|
|
// All parsed strings should be null-terminated. This character is either a
|
|
// newline (if at the end of the path), or a question mark (if a query is
|
|
// present).
|
|
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.o = path - buf;
|
|
|
|
req->buf.len = req_len;
|
|
req->buf.s = buf;
|
|
req->buf.owned = false;
|
|
|
|
return lnm_http_parse_err_ok;
|
|
}
|
|
|
|
void lnm_http_req_reset(lnm_http_req *req) {
|
|
if (req->body.owned) {
|
|
free(req->body.buf);
|
|
}
|
|
|
|
if (req->buf.owned) {
|
|
free(req->buf.s);
|
|
}
|
|
|
|
memset(req, 0, sizeof(lnm_http_req));
|
|
}
|
|
|
|
lnm_err lnm_http_req_header_get(lnm_http_req_hdr *out, lnm_http_req *req,
|
|
lnm_http_header type) {
|
|
return lnm_http_req_header_get_s(out, req, lnm_http_header_names[type]);
|
|
}
|
|
|
|
lnm_err lnm_http_req_header_get_s(lnm_http_req_hdr *out, lnm_http_req *req,
|
|
const char *name) {
|
|
size_t name_len = strlen(name);
|
|
|
|
for (size_t i = 0; i < req->headers.len; i++) {
|
|
const lnm_http_req_ihhr *header = &req->headers.arr[i];
|
|
|
|
if (lnm_strnieq(req->buf.s + header->name.o, header->name.len, name,
|
|
name_len)) {
|
|
out->name.s = req->buf.s + header->name.o;
|
|
out->name.len = header->name.len;
|
|
out->value.s = req->buf.s + header->value.o;
|
|
out->value.len = header->value.len;
|
|
|
|
return lnm_err_ok;
|
|
}
|
|
}
|
|
|
|
return lnm_err_not_found;
|
|
}
|
|
|
|
size_t lnm_http_req_route_segment(const char **out, lnm_http_req *req,
|
|
const char *key) {
|
|
const lnm_http_route_match_segment *segment =
|
|
lnm_http_route_match_get(&req->route_match, key);
|
|
|
|
if (segment == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (out != NULL) {
|
|
*out = req->buf.s + req->path.o + segment->start;
|
|
}
|
|
|
|
return segment->len;
|
|
}
|