diff --git a/lnm/include/lnm/http/consts.h b/lnm/include/lnm/http/consts.h index 4b6bc92..37f3239 100644 --- a/lnm/include/lnm/http/consts.h +++ b/lnm/include/lnm/http/consts.h @@ -12,7 +12,7 @@ typedef enum lnm_http_method { http_method_put, http_method_patch, http_method_delete -} http_method; +} lnm_http_method; extern const char *lnm_http_status_names[][32]; diff --git a/lnm/include/lnm/http/loop.h b/lnm/include/lnm/http/loop.h index 9b6f0d3..f84a1ea 100644 --- a/lnm/include/lnm/http/loop.h +++ b/lnm/include/lnm/http/loop.h @@ -3,9 +3,11 @@ #include "lnm/common.h" +#define LNM_HTTP_MAX_REQ_HEADERS 32 + typedef struct lnm_loop lnm_http_loop; -typedef struct lnm_http_conn lnm_http_conn; +typedef struct lnm_conn lnm_http_conn; typedef struct lnm_http_step lnm_http_step; @@ -20,14 +22,6 @@ typedef lnm_err (*lnm_http_step_fn)(lnm_http_conn *conn); */ lnm_err lnm_http_loop_init(lnm_http_loop **out); -/** - * Initialize a first step - * - * @param out where to store pointer to new `lnm_http_step` - * @param fn step function associated with the step - */ -lnm_err lnm_http_step_init(lnm_http_step **out, lnm_http_step_fn fn); - /** * Append the given step fn to the step. * @@ -67,4 +61,15 @@ lnm_err lnm_http_route_init_regex(lnm_http_route **out, const char *pattern, */ void lnm_http_loop_route_add(lnm_http_loop *hl, lnm_http_route *route); +/** + * Represents what state an HTTP loop request is currently in. + */ +typedef enum lnm_http_loop_state { + lnm_http_loop_state_parse_req = 0, +} lnm_http_loop_state; + +typedef struct lnm_http_loop_ctx { + lnm_http_loop_state state; +} lnm_http_loop_ctx; + #endif diff --git a/lnm/include/lnm/http/req.h b/lnm/include/lnm/http/req.h new file mode 100644 index 0000000..4741f4d --- /dev/null +++ b/lnm/include/lnm/http/req.h @@ -0,0 +1,48 @@ +#ifndef LNM_HTTP_REQ +#define LNM_HTTP_REQ + +#include + +#include "picohttpparser.h" + +#include "lnm/http/consts.h" +#include "lnm/http/loop.h" + +/** + * Represents the parsed HTTP request + */ +typedef struct lnm_http_req { + size_t len; + int minor_version; + lnm_http_method method; + struct { + const char *s; + size_t len; + } path; + struct { + const char *s; + size_t len; + } query; + struct { + struct phr_header arr[LNM_HTTP_MAX_REQ_HEADERS]; + size_t len; + } headers; +} lnm_http_req; + +typedef enum lnm_http_parse_err { + lnm_http_parse_err_ok = 0, + lnm_http_parse_err_incomplete, + lnm_http_parse_err_invalid, + lnm_http_parse_err_unknown_method, +} lnm_http_parse_err; + +/** + * Try to parse the given buffer into an HTTP request. + * + * @param req request to store parsed data in + * @param buf buffer to parse; might be modified + * @param len length of buf + */ +lnm_http_parse_err lnm_http_req_parse(lnm_http_req *req, char *buf, size_t len); + +#endif diff --git a/lnm/src/_include/lnm/http/loop_internal.h b/lnm/src/_include/lnm/http/loop_internal.h index 5d86a03..7410ade 100644 --- a/lnm/src/_include/lnm/http/loop_internal.h +++ b/lnm/src/_include/lnm/http/loop_internal.h @@ -5,10 +5,6 @@ #include "lnm/http/loop.h" -typedef struct lnm_http_conn { - -} lnm_http_conn; - typedef struct lnm_http_step { lnm_http_step_fn fn; struct lnm_http_step *next; @@ -29,6 +25,19 @@ typedef struct lnm_http_route { lnm_http_step *step; } lnm_http_route; +/** + * Initialize a new empty route. + * + * @param out where to store pointer to new `lnm_http_route` + */ lnm_err lnm_http_route_init(lnm_http_route **out); +/** + * Initialize a first step. + * + * @param out where to store pointer to new `lnm_http_step` + * @param fn step function associated with the step + */ +lnm_err lnm_http_step_init(lnm_http_step **out, lnm_http_step_fn fn); + #endif diff --git a/lnm/src/http/req.c b/lnm/src/http/req.c new file mode 100644 index 0000000..2327ce6 --- /dev/null +++ b/lnm/src/http/req.c @@ -0,0 +1,58 @@ +#include +#include + +#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; + + req->headers.len = 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, 0); + + if (req_len == -1) { + 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; + } + + 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.s = question_mark + 1; + 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'; + req->path.len = path_len; + req->path.s = path; + req->len = req_len; + + return lnm_http_parse_err_ok; +}