feat: basic http routing
							parent
							
								
									8250a5b8b0
								
							
						
					
					
						commit
						7ece0eb4e5
					
				| 
						 | 
				
			
			@ -16,15 +16,15 @@ typedef struct http_route {
 | 
			
		|||
  http_route_type type;
 | 
			
		||||
  char *path;
 | 
			
		||||
  regex_t *regex;
 | 
			
		||||
  size_t step_count;
 | 
			
		||||
  void (**steps)(event_loop_conn *);
 | 
			
		||||
  bool (*steps[5])(event_loop_conn *);
 | 
			
		||||
} http_route;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Global context passed to every connection using the same pointer
 | 
			
		||||
 */
 | 
			
		||||
typedef struct http_loop_gctx {
 | 
			
		||||
  http_route **routes;
 | 
			
		||||
  http_route *routes;
 | 
			
		||||
  size_t route_count;
 | 
			
		||||
  Trie *trie;
 | 
			
		||||
} 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_route_request(event_loop_conn *conn);
 | 
			
		||||
 | 
			
		||||
void http_loop_process_request(event_loop_conn *conn);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,24 +25,28 @@ bool http_loop_handle_request(event_loop_conn *conn) {
 | 
			
		|||
 | 
			
		||||
  http_loop_ctx *ctx = conn->ctx;
 | 
			
		||||
 | 
			
		||||
  http_parse_error res =
 | 
			
		||||
      http_parse_request(&ctx->req, (const char *)&conn->rbuf[conn->rbuf_read],
 | 
			
		||||
                         conn->rbuf_size - conn->rbuf_read);
 | 
			
		||||
  // If route is defined, we're currently processing a request
 | 
			
		||||
  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);
 | 
			
		||||
 | 
			
		||||
  if (res == http_parse_error_ok) {
 | 
			
		||||
    // 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 &&
 | 
			
		||||
            conn->rbuf_size == EVENT_LOOP_BUFFER_SIZE)) {
 | 
			
		||||
    conn->state = event_loop_conn_state_end;
 | 
			
		||||
    if (res == http_parse_error_invalid ||
 | 
			
		||||
        (res == http_parse_error_incomplete &&
 | 
			
		||||
         conn->rbuf_size == EVENT_LOOP_BUFFER_SIZE)) {
 | 
			
		||||
      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
 | 
			
		||||
  // return false here, as this allows cycling better through all connections
 | 
			
		||||
  return conn->state == event_loop_conn_state_req;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,13 +1,48 @@
 | 
			
		|||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "event_loop.h"
 | 
			
		||||
#include "http.h"
 | 
			
		||||
#include "http_loop.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) { */
 | 
			
		||||
/*   // TODO routing */
 | 
			
		||||
  // The end of the list of steps is marked with a NULL. When this is reached,
 | 
			
		||||
  // 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 */
 | 
			
		||||
/*   http_write_standard_response(conn, http_not_found); */
 | 
			
		||||
/* } */
 | 
			
		||||
  // We execute the next step
 | 
			
		||||
  // 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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										13
									
								
								src/main.c
								
								
								
								
							
							
						
						
									
										13
									
								
								src/main.c
								
								
								
								
							| 
						 | 
				
			
			@ -4,17 +4,18 @@
 | 
			
		|||
#include "http_loop.h"
 | 
			
		||||
#include "log.h"
 | 
			
		||||
 | 
			
		||||
void http_write_404(event_loop_conn *conn) {
 | 
			
		||||
  memcpy(conn->wbuf, http_404, http_404_len);
 | 
			
		||||
bool http_write_404(event_loop_conn *conn) {
 | 
			
		||||
  memcpy(conn->wbuf, http_405, http_405_len);
 | 
			
		||||
 | 
			
		||||
  conn->state = event_loop_conn_state_res;
 | 
			
		||||
  conn->wbuf_size = http_404_len;
 | 
			
		||||
  conn->wbuf_size = http_405_len;
 | 
			
		||||
  conn->wbuf_sent = 0;
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void (*steps[])(event_loop_conn *) = {http_write_404, NULL};
 | 
			
		||||
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() {
 | 
			
		||||
  setvbuf(stdout, NULL, _IONBF, 0);
 | 
			
		||||
| 
						 | 
				
			
			@ -32,6 +33,8 @@ int main() {
 | 
			
		|||
 | 
			
		||||
  http_loop_gctx *gctx = http_loop_gctx_init();
 | 
			
		||||
  gctx->trie = trie;
 | 
			
		||||
  gctx->routes = routes;
 | 
			
		||||
  gctx->route_count = sizeof(routes) / sizeof(routes[0]);
 | 
			
		||||
  event_loop *el = http_loop_init(gctx);
 | 
			
		||||
 | 
			
		||||
  event_loop_run(el, 8000);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue