feat: basic http routing

c-web-server
Jef Roosens 2023-05-27 16:42:15 +02:00
parent 8250a5b8b0
commit 7ece0eb4e5
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
4 changed files with 75 additions and 31 deletions

View File

@ -16,15 +16,15 @@ typedef struct http_route {
http_route_type type; http_route_type type;
char *path; char *path;
regex_t *regex; regex_t *regex;
size_t step_count; bool (*steps[5])(event_loop_conn *);
void (**steps)(event_loop_conn *);
} http_route; } http_route;
/* /*
* Global context passed to every connection using the same pointer * Global context passed to every connection using the same pointer
*/ */
typedef struct http_loop_gctx { typedef struct http_loop_gctx {
http_route **routes; http_route *routes;
size_t route_count;
Trie *trie; Trie *trie;
} http_loop_gctx; } http_loop_gctx;
@ -58,6 +58,8 @@ void http_loop_ctx_free(http_loop_ctx *ctx);
*/ */
bool http_loop_handle_request(event_loop_conn *conn); bool http_loop_handle_request(event_loop_conn *conn);
bool http_loop_route_request(event_loop_conn *conn);
void http_loop_process_request(event_loop_conn *conn); void http_loop_process_request(event_loop_conn *conn);
/** /**

View File

@ -25,24 +25,28 @@ bool http_loop_handle_request(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx; http_loop_ctx *ctx = conn->ctx;
http_parse_error res = // If route is defined, we're currently processing a request
http_parse_request(&ctx->req, (const char *)&conn->rbuf[conn->rbuf_read], if (ctx->route == NULL) {
http_parse_error res = http_parse_request(
&ctx->req, (const char *)&conn->rbuf[conn->rbuf_read],
conn->rbuf_size - conn->rbuf_read); conn->rbuf_size - conn->rbuf_read);
if (res == http_parse_error_ok) { if (res == http_parse_error_invalid ||
// Perform the request
/* http_route(conn); */
}
// Both in the case of an invalid HTTP request or one that's larger than the
// read buffer, we cannot determine when the next, possibly valid, HTTP
// request begins in the data stream. Therefore, we close the connection,
// even if additional pipelined requests are incoming.
else if (res == http_parse_error_invalid ||
(res == http_parse_error_incomplete && (res == http_parse_error_incomplete &&
conn->rbuf_size == EVENT_LOOP_BUFFER_SIZE)) { conn->rbuf_size == EVENT_LOOP_BUFFER_SIZE)) {
conn->state = event_loop_conn_state_end; conn->state = event_loop_conn_state_end;
return false;
} }
// If the routing fails, we exit the handler
if (!http_loop_route_request(conn)) {
return false;
}
}
http_loop_process_request(conn);
// TODO in highly concurrent situations, it might actually be better to always // TODO in highly concurrent situations, it might actually be better to always
// return false here, as this allows cycling better through all connections // return false here, as this allows cycling better through all connections
return conn->state == event_loop_conn_state_req; return conn->state == event_loop_conn_state_req;

View File

@ -1,13 +1,48 @@
#include <string.h> #include <string.h>
#include "event_loop.h" #include "http_loop.h"
#include "http.h"
void http_loop_process_request(event_loop_conn *conn) {} void http_loop_process_request(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx;
/* void http_route(event_loop_conn *conn) { */ // The end of the list of steps is marked with a NULL. When this is reached,
/* // TODO routing */ // we simply set the route to NULL, letting the http loop know a new request
// can be handled.
if (ctx->route->steps[ctx->current_step] == NULL) {
ctx->route = NULL;
ctx->current_step = 0;
return;
}
/* // Fallthrough is to return a 404 */ // We execute the next step
/* http_write_standard_response(conn, http_not_found); */ // NOTE can/should we execute more than one step at once here?
/* } */ if (ctx->route->steps[ctx->current_step](conn)) {
ctx->current_step++;
}
}
bool http_loop_route_request(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx;
http_loop_gctx *gctx = ctx->g;
http_route *route;
for (size_t i = 0; i < gctx->route_count; i++) {
route = &gctx->routes[i];
switch (route->type) {
case http_route_literal:
if (strncmp(route->path, ctx->req.method, ctx->req.method_len)) {
ctx->route = route;
return true;
}
// TODO
case http_route_regex:;
}
}
// Fallthrough is to write a 404
http_write_standard_response(conn, http_not_found);
return false;
}

View File

@ -4,17 +4,18 @@
#include "http_loop.h" #include "http_loop.h"
#include "log.h" #include "log.h"
void http_write_404(event_loop_conn *conn) { bool http_write_404(event_loop_conn *conn) {
memcpy(conn->wbuf, http_404, http_404_len); memcpy(conn->wbuf, http_405, http_405_len);
conn->state = event_loop_conn_state_res; conn->state = event_loop_conn_state_res;
conn->wbuf_size = http_404_len; conn->wbuf_size = http_405_len;
conn->wbuf_sent = 0; conn->wbuf_sent = 0;
return true;
} }
void (*steps[])(event_loop_conn *) = {http_write_404, NULL};
http_route routes[] = { http_route routes[] = {
{.type = http_route_literal, .path = "/", .step_count = 1, .steps = steps}}; {.type = http_route_literal, .path = "/", .steps = {http_write_404, NULL}}};
int main() { int main() {
setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0);
@ -32,6 +33,8 @@ int main() {
http_loop_gctx *gctx = http_loop_gctx_init(); http_loop_gctx *gctx = http_loop_gctx_init();
gctx->trie = trie; gctx->trie = trie;
gctx->routes = routes;
gctx->route_count = sizeof(routes) / sizeof(routes[0]);
event_loop *el = http_loop_init(gctx); event_loop *el = http_loop_init(gctx);
event_loop_run(el, 8000); event_loop_run(el, 8000);