feat(lnm): more request processing code
ci/woodpecker/push/build Pipeline was successful Details

lnm
Jef Roosens 2023-11-30 21:04:13 +01:00
parent 8c21ccf58b
commit 77b62825a6
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
5 changed files with 120 additions and 19 deletions

View File

@ -5,6 +5,7 @@
#include "lnm/common.h" #include "lnm/common.h"
#include "lnm/http/req.h" #include "lnm/http/req.h"
#include "lnm/http/res.h"
typedef struct lnm_loop lnm_http_loop; typedef struct lnm_loop lnm_http_loop;
@ -14,14 +15,14 @@ typedef struct lnm_http_step lnm_http_step;
typedef struct lnm_http_route lnm_http_route; typedef struct lnm_http_route lnm_http_route;
typedef enum lnm_step_err { typedef enum lnm_http_step_err {
lnm_step_err_done = 0, lnm_http_step_err_done = 0,
lnm_step_err_io_needed, lnm_http_step_err_io_needed,
lnm_step_err_close, lnm_http_step_err_close,
lnm_step_err_res, lnm_http_step_err_res,
} lnm_step_err; } lnm_http_step_err;
typedef lnm_step_err (*lnm_http_step_fn)(lnm_http_conn *conn); typedef lnm_http_step_err (*lnm_http_step_fn)(lnm_http_conn *conn);
typedef lnm_err (*lnm_http_ctx_init_fn)(void **c_ctx, void *gctx); typedef lnm_err (*lnm_http_ctx_init_fn)(void **c_ctx, void *gctx);
@ -84,9 +85,22 @@ void lnm_http_loop_route_add(lnm_http_loop *hl, lnm_http_route *route);
* Represents what state an HTTP loop request is currently in. * Represents what state an HTTP loop request is currently in.
*/ */
typedef enum lnm_http_loop_state { typedef enum lnm_http_loop_state {
// Parse the HTTP request
lnm_http_loop_state_parse_req = 0, lnm_http_loop_state_parse_req = 0,
// Route the request
lnm_http_loop_state_route, lnm_http_loop_state_route,
// Parse specific headers (e.g. Content-Length)
lnm_http_loop_state_parse_headers,
// Execute the various steps defined for the route
lnm_http_loop_state_steps, lnm_http_loop_state_steps,
// Write the response status line
lnm_http_loop_state_write_status_line,
// Write the various response headers
lnm_http_loop_state_write_headers,
// Write the request body
lnm_http_loop_state_write_body,
// Clean up the request and reset the state for a next request
lnm_http_loop_state_finish,
} lnm_http_loop_state; } lnm_http_loop_state;
typedef struct lnm_http_loop_gctx { typedef struct lnm_http_loop_gctx {
@ -103,7 +117,9 @@ typedef struct lnm_http_loop_gctx {
typedef struct lnm_http_loop_ctx { typedef struct lnm_http_loop_ctx {
lnm_http_loop_state state; lnm_http_loop_state state;
lnm_http_req req; lnm_http_req req;
lnm_http_res res;
lnm_http_route *route; lnm_http_route *route;
lnm_http_step *cur_step;
lnm_http_loop_gctx *g; lnm_http_loop_gctx *g;
void *c; void *c;
} lnm_http_loop_ctx; } lnm_http_loop_ctx;

View File

@ -0,0 +1,30 @@
#ifndef LNM_HTTP_RES
#define LNM_HTTP_RES
#include <stdbool.h>
#include "lnm/http/consts.h"
typedef struct lnm_http_res_header {
struct {
char *s;
size_t len;
bool owned;
} name;
struct {
char *s;
size_t len;
bool owned;
} value;
struct lnm_http_res_header *next;
} lnm_http_res_header;
typedef struct lnm_http_res {
lnm_http_status status;
struct {
lnm_http_res_header *head;
lnm_http_res_header *current;
} headers;
} lnm_http_res;
#endif

View File

@ -1,12 +1,18 @@
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#include "lnm/http/consts.h"
#include "lnm/http/loop.h" #include "lnm/http/loop.h"
#include "lnm/http/loop_internal.h" #include "lnm/http/loop_internal.h"
#include "lnm/http/req.h" #include "lnm/http/req.h"
#include "lnm/loop.h" #include "lnm/loop.h"
#include "lnm/loop_internal.h" #include "lnm/loop_internal.h"
/* static const lnm_http_loop_state lnm_http_loop_state_first_req =
* lnm_http_loop_state_parse_req; */
static const lnm_http_loop_state lnm_http_loop_state_first_res =
lnm_http_loop_state_write_headers;
void lnm_http_loop_process_parse_req(lnm_http_conn *conn) { void lnm_http_loop_process_parse_req(lnm_http_conn *conn) {
lnm_http_loop_ctx *ctx = conn->ctx; lnm_http_loop_ctx *ctx = conn->ctx;
@ -16,7 +22,7 @@ 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.len;
ctx->state = lnm_http_loop_state_steps; ctx->state = lnm_http_loop_state_route;
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
@ -29,8 +35,8 @@ void lnm_http_loop_process_parse_req(lnm_http_conn *conn) {
conn->state = lnm_loop_state_end; conn->state = lnm_loop_state_end;
break; break;
case lnm_http_parse_err_unknown_method: case lnm_http_parse_err_unknown_method:
// TODO set status code here ctx->res.status = lnm_http_status_method_not_implemented;
conn->state = lnm_loop_state_end; ctx->state = lnm_http_loop_state_first_res;
break; break;
} }
} }
@ -65,31 +71,80 @@ void lnm_http_loop_process_route(lnm_http_conn *conn) {
match_level = match_level < new_match_level ? new_match_level : match_level; match_level = match_level < new_match_level ? new_match_level : match_level;
} }
ctx->route = match_level == 2 ? route : NULL; switch (match_level) {
case 0:
ctx->res.status = lnm_http_status_not_found;
ctx->state = lnm_http_loop_state_first_res;
break;
case 1:
ctx->res.status = lnm_http_status_method_not_allowed;
ctx->state = lnm_http_loop_state_first_res;
break;
case 2:
ctx->route = route;
ctx->cur_step = route->step;
ctx->state = lnm_http_loop_state_parse_headers;
break;
}
}
void lnm_http_loop_process_parse_headers(lnm_http_conn *conn) {
lnm_http_loop_ctx *ctx = conn->ctx;
// TODO
ctx->state = lnm_http_loop_state_steps;
} }
void lnm_http_loop_process_steps(lnm_http_conn *conn) { void lnm_http_loop_process_steps(lnm_http_conn *conn) {
/* lnm_http_loop_ctx *ctx = conn->ctx; */ lnm_http_loop_ctx *ctx = conn->ctx;
lnm_http_step *step;
/* while () */ do {
step = ctx->cur_step;
switch (step->fn(conn)) {
case lnm_http_step_err_done:
ctx->cur_step = ctx->cur_step->next;
break;
case lnm_http_step_err_io_needed:
break;
case lnm_http_step_err_close:
conn->state = lnm_loop_state_end;
break;
case lnm_http_step_err_res:
ctx->state = lnm_http_loop_state_first_res;
break;
}
} while ((step != ctx->cur_step) && (ctx->cur_step != NULL));
if (ctx->cur_step == NULL) {
ctx->state = lnm_http_loop_state_write_headers;
}
} }
void (*process_fns[])(lnm_http_conn *conn) = { void (*process_fns[])(lnm_http_conn *conn) = {
lnm_http_loop_process_parse_req, lnm_http_loop_process_parse_req,
lnm_http_loop_process_route, lnm_http_loop_process_route,
lnm_http_loop_process_parse_headers,
lnm_http_loop_process_steps, lnm_http_loop_process_steps,
}; };
void lnm_http_loop_process(lnm_http_conn *conn) { void lnm_http_loop_process(lnm_http_conn *conn) {
lnm_http_loop_ctx *ctx = conn->ctx; lnm_http_loop_ctx *ctx = conn->ctx;
lnm_http_loop_state cur_state; lnm_http_loop_state http_loop_state;
lnm_loop_state loop_state;
// As long as the processing is able to advance to a next state, we keep // We stop processing if:
// progressing // - the event loop state has changed, as we need to switch to the other I/O
// loop
// - the process fn returned without changing the HTTP loop state, indicating
// it's waiting for I/O
do { do {
cur_state = ctx->state; http_loop_state = ctx->state;
loop_state = conn->state;
process_fns[cur_state](conn); process_fns[http_loop_state](conn);
} while (conn->state == lnm_loop_state_req && cur_state != ctx->state); } while ((conn->state == loop_state) && (http_loop_state != ctx->state));
} }