feat(lander): initial lnm integration test
All checks were successful
ci/woodpecker/push/build Pipeline was successful
All checks were successful
ci/woodpecker/push/build Pipeline was successful
This commit is contained in:
parent
799821d9fc
commit
8ec667af3b
15 changed files with 187 additions and 44 deletions
|
|
@ -79,4 +79,11 @@ uint64_t lnm_ipow(uint64_t base, uint64_t power);
|
|||
*/
|
||||
uint64_t lnm_atoi(const char *s, size_t len);
|
||||
|
||||
/**
|
||||
* Calculate how many base 10 digits the given number consists of.
|
||||
*
|
||||
* @param num number to use
|
||||
*/
|
||||
uint64_t lnm_digits(uint64_t num);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@ extern const char *lnm_http_method_names[];
|
|||
extern const size_t lnm_http_method_names_len;
|
||||
|
||||
typedef enum lnm_http_method {
|
||||
http_method_get = 0,
|
||||
http_method_post,
|
||||
http_method_put,
|
||||
http_method_patch,
|
||||
http_method_delete
|
||||
lnm_http_method_get = 0,
|
||||
lnm_http_method_post,
|
||||
lnm_http_method_put,
|
||||
lnm_http_method_patch,
|
||||
lnm_http_method_delete
|
||||
} lnm_http_method;
|
||||
|
||||
extern const char *lnm_http_status_names[][32];
|
||||
|
|
|
|||
|
|
@ -32,12 +32,20 @@ lnm_err lnm_http_loop_init(lnm_http_loop **out, void *c_gctx,
|
|||
lnm_http_ctx_reset_fn ctx_reset,
|
||||
lnm_http_ctx_free_fn ctx_free);
|
||||
|
||||
/**
|
||||
* Initialize a new step.
|
||||
*
|
||||
* @param out where to store pointer to new `lnm_http_step`
|
||||
* @param fn step function
|
||||
*/
|
||||
lnm_err lnm_http_step_init(lnm_http_step **out, lnm_http_step_fn fn);
|
||||
|
||||
/**
|
||||
* Append the given step fn to the step.
|
||||
*
|
||||
* @param out where to store pointer to new `lnm_http_step`
|
||||
* @param step step to append new step to
|
||||
* @param fn step funcitonn
|
||||
* @param fn step function
|
||||
*/
|
||||
lnm_err lnm_http_step_append(lnm_http_step **out, lnm_http_step *step,
|
||||
lnm_http_step_fn fn);
|
||||
|
|
@ -71,7 +79,9 @@ lnm_err lnm_http_route_init_regex(lnm_http_route **out, lnm_http_method method,
|
|||
* @param hl HTTP loop to modify
|
||||
* @param route route to add
|
||||
*/
|
||||
void lnm_http_loop_route_add(lnm_http_loop *hl, lnm_http_route *route);
|
||||
lnm_err lnm_http_loop_route_add(lnm_http_loop *hl, lnm_http_route *route);
|
||||
|
||||
lnm_err lnm_http_loop_run(lnm_http_loop *hl, uint16_t port);
|
||||
|
||||
/**
|
||||
* Represents what state an HTTP loop request is currently in.
|
||||
|
|
@ -85,6 +95,8 @@ typedef enum lnm_http_loop_state {
|
|||
lnm_http_loop_state_parse_headers,
|
||||
// Execute the various steps defined for the route
|
||||
lnm_http_loop_state_steps,
|
||||
// Add certain automatically added headers
|
||||
lnm_http_loop_state_add_headers,
|
||||
// Write the response status line
|
||||
lnm_http_loop_state_write_status_line,
|
||||
// Write the various response headers
|
||||
|
|
|
|||
|
|
@ -45,12 +45,12 @@ typedef struct lnm_http_res {
|
|||
FILE *f;
|
||||
data_fn fn;
|
||||
} data;
|
||||
size_t len;
|
||||
uint64_t len;
|
||||
bool owned;
|
||||
lnm_http_res_body_type type;
|
||||
} body;
|
||||
// General-purpose; meaning depends on the current state
|
||||
size_t written;
|
||||
uint64_t written;
|
||||
} lnm_http_res;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ lnm_err lnm_http_loop_init(lnm_http_loop **out, void *c_gctx,
|
|||
free(hl));
|
||||
|
||||
hl->data_read = lnm_http_loop_process;
|
||||
hl->data_write = lnm_http_loop_process;
|
||||
hl->ctx_init = (lnm_err(*)(void **, void *))lnm_http_loop_ctx_init;
|
||||
hl->ctx_free = (void (*)(void *))lnm_http_loop_ctx_free;
|
||||
*out = hl;
|
||||
|
||||
return lnm_err_ok;
|
||||
|
|
@ -42,7 +45,9 @@ lnm_err lnm_http_step_append(lnm_http_step **out, lnm_http_step *step,
|
|||
lnm_http_step_fn fn) {
|
||||
LNM_RES(lnm_http_step_init(out, fn));
|
||||
|
||||
step->next = *out;
|
||||
if (step != NULL) {
|
||||
step->next = *out;
|
||||
}
|
||||
|
||||
return lnm_err_ok;
|
||||
}
|
||||
|
|
@ -96,3 +101,28 @@ lnm_err lnm_http_route_init_regex(lnm_http_route **out, lnm_http_method method,
|
|||
|
||||
return lnm_err_ok;
|
||||
}
|
||||
|
||||
lnm_err lnm_http_loop_route_add(lnm_http_loop *hl, lnm_http_route *route) {
|
||||
lnm_http_loop_gctx *gctx = hl->gctx;
|
||||
|
||||
lnm_http_route **new_routes =
|
||||
gctx->routes.len > 0
|
||||
? realloc(gctx->routes.arr,
|
||||
(gctx->routes.len + 1) * sizeof(lnm_http_route *))
|
||||
: malloc(sizeof(lnm_http_route *));
|
||||
|
||||
if (new_routes == NULL) {
|
||||
return lnm_err_failed_alloc;
|
||||
}
|
||||
|
||||
new_routes[gctx->routes.len] = route;
|
||||
gctx->routes.arr = new_routes;
|
||||
gctx->routes.len++;
|
||||
|
||||
return lnm_err_ok;
|
||||
}
|
||||
|
||||
lnm_err lnm_http_loop_run(lnm_http_loop *hl, uint16_t port) {
|
||||
LNM_RES(lnm_loop_setup(hl, port));
|
||||
return lnm_loop_run(hl);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
/* 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;
|
||||
lnm_http_loop_state_write_status_line;
|
||||
|
||||
void lnm_http_loop_process_parse_req(lnm_http_conn *conn) {
|
||||
lnm_http_loop_ctx *ctx = conn->ctx;
|
||||
|
|
@ -65,6 +65,7 @@ void lnm_http_loop_process_route(lnm_http_conn *conn) {
|
|||
matched_path =
|
||||
regexec(route->route.regex, ctx->req.path.s,
|
||||
LNM_HTTP_MAX_REGEX_GROUPS, ctx->req.path.groups, 0) == 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Remember the previous match levels
|
||||
|
|
@ -105,9 +106,12 @@ void lnm_http_loop_process_parse_headers(lnm_http_conn *conn) {
|
|||
|
||||
void lnm_http_loop_process_steps(lnm_http_conn *conn) {
|
||||
lnm_http_loop_ctx *ctx = conn->ctx;
|
||||
lnm_http_step *step;
|
||||
lnm_http_step *step = NULL;
|
||||
|
||||
do {
|
||||
// Loop until we either:
|
||||
// - reach the end of the chain of steps, indicated by NULL
|
||||
// - have a step that's waiting for I/O
|
||||
while ((ctx->cur_step != NULL) && (step != ctx->cur_step)) {
|
||||
step = ctx->cur_step;
|
||||
|
||||
switch (step->fn(conn)) {
|
||||
|
|
@ -124,16 +128,34 @@ void lnm_http_loop_process_steps(lnm_http_conn *conn) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
// Loop until we either:
|
||||
// - reach the end of the chain of steps, indicated by NULL
|
||||
// - have a step that's waiting for I/O
|
||||
while ((ctx->cur_step != NULL) && (step != ctx->cur_step));
|
||||
|
||||
if (ctx->cur_step == NULL) {
|
||||
ctx->state = lnm_http_loop_state_write_headers;
|
||||
ctx->state = lnm_http_loop_state_add_headers;
|
||||
}
|
||||
}
|
||||
|
||||
void lnm_http_loop_state_process_add_headers(lnm_http_conn *conn) {
|
||||
lnm_http_loop_ctx *ctx = conn->ctx;
|
||||
lnm_http_res *res = &ctx->res;
|
||||
|
||||
if (res->body.len > 0) {
|
||||
uint64_t digits = lnm_digits(res->body.len);
|
||||
char *buf = malloc(digits + 1);
|
||||
|
||||
if (buf == NULL) {
|
||||
conn->state = lnm_loop_state_end;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sprintf(buf, "%lu", res->body.len);
|
||||
lnm_http_res_add_header_len(res, lnm_http_header_content_length, buf,
|
||||
digits, true);
|
||||
}
|
||||
|
||||
ctx->state = lnm_http_loop_state_write_status_line;
|
||||
}
|
||||
|
||||
// This function is intentionally written inefficiently for now, as it will most
|
||||
// likely only have to run once for each response
|
||||
void lnm_http_loop_process_write_status_line(lnm_http_conn *conn) {
|
||||
|
|
@ -199,7 +221,13 @@ void lnm_http_loop_process_write_headers(lnm_http_conn *conn) {
|
|||
}
|
||||
}
|
||||
|
||||
if (res->headers.current == NULL) {
|
||||
// The headers should end with an additional newline. If there's no space left
|
||||
// in the write buffer, we don't switch states so we can re-try this write
|
||||
// later
|
||||
if ((res->headers.current == NULL) && (conn->w.size < LNM_LOOP_BUF_SIZE)) {
|
||||
conn->w.buf[conn->w.size] = '\n';
|
||||
conn->w.size++;
|
||||
|
||||
ctx->state = ctx->res.body.len > 0 ? lnm_http_loop_state_write_body
|
||||
: lnm_http_loop_state_finish;
|
||||
}
|
||||
|
|
@ -258,6 +286,7 @@ void (*process_fns[])(lnm_http_conn *conn) = {
|
|||
lnm_http_loop_process_route,
|
||||
lnm_http_loop_process_parse_headers,
|
||||
lnm_http_loop_process_steps,
|
||||
lnm_http_loop_state_process_add_headers,
|
||||
lnm_http_loop_process_write_status_line,
|
||||
lnm_http_loop_process_write_headers,
|
||||
lnm_http_loop_process_write_body,
|
||||
|
|
@ -273,6 +302,8 @@ lnm_loop_state state_map[] = {
|
|||
lnm_loop_state_req,
|
||||
// steps
|
||||
lnm_loop_state_req,
|
||||
// add_headers
|
||||
lnm_loop_state_req,
|
||||
// write_status_line
|
||||
lnm_loop_state_res,
|
||||
// write_headers
|
||||
|
|
@ -301,9 +332,10 @@ void lnm_http_loop_process(lnm_http_conn *conn) {
|
|||
|
||||
process_fns[http_loop_state](conn);
|
||||
} while ((conn->state == loop_state) &&
|
||||
(conn->state == state_map[loop_state]) &&
|
||||
(conn->state == state_map[http_loop_state]) &&
|
||||
(http_loop_state != ctx->state));
|
||||
|
||||
// Check required to prevent overwriting manually set event loop state
|
||||
conn->state = conn->state == loop_state ? state_map[loop_state] : conn->state;
|
||||
conn->state =
|
||||
conn->state == loop_state ? state_map[http_loop_state] : conn->state;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ lnm_http_parse_err lnm_http_req_parse(lnm_http_req *req, char *buf,
|
|||
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
|
||||
|
|
|
|||
|
|
@ -43,3 +43,15 @@ uint64_t lnm_atoi(const char *s, size_t len) {
|
|||
|
||||
return res;
|
||||
}
|
||||
|
||||
uint64_t lnm_digits(uint64_t num) {
|
||||
int digits = 1;
|
||||
|
||||
while (num > 9) {
|
||||
digits++;
|
||||
|
||||
num /= 10;
|
||||
}
|
||||
|
||||
return digits;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lnm/common.h"
|
||||
|
|
@ -43,7 +44,9 @@ lnm_err lnm_loop_accept(lnm_loop *l) {
|
|||
// Append connection to list of connections
|
||||
if ((size_t)conn_fd >= l->conns.len) {
|
||||
lnm_loop_conn **new =
|
||||
realloc(l->conns.arr, sizeof(lnm_loop_conn *) * (conn_fd + 1));
|
||||
l->conns.len == 0
|
||||
? calloc(sizeof(lnm_loop_conn *), conn_fd + 1)
|
||||
: realloc(l->conns.arr, sizeof(lnm_loop_conn *) * (conn_fd + 1));
|
||||
|
||||
if (new == NULL) {
|
||||
close(conn_fd);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue