#ifndef LANDER_HTTP_LOOP #define LANDER_HTTP_LOOP #include #include "event_loop.h" #include "http/req.h" #include "http/res.h" #include "http/types.h" #include "trie.h" // Max amount of steps a route can use #define HTTP_LOOP_MAX_STEPS 17 #define MIN(x, y) (((x) < (y)) ? (x) : (y)) #define MAX(x, y) (((x) > (y)) ? (x) : (y)) /** * Type of a route */ typedef enum http_route_type { http_route_literal = 0, http_route_regex = 1, } http_route_type; /** * Function describing a step in a route's processing. * * @param conn connection to process * @return whether processing can proceed to the next step without performing * I/O first. For a request step, `false` means more data needs to be read * before the step can finish its processing. For response steps, `false` means * there's new data in the write buffer that needs to be written. */ typedef bool (*step)(event_loop_conn *conn); /** * Struct describing a route a request can take. */ typedef struct http_route { http_route_type type; http_method method; char *path; // Compiled regex for a regex route. This value gets set at runtime when // starting the http loop regex_t *regex; step steps[HTTP_LOOP_MAX_STEPS]; step steps_res[HTTP_LOOP_MAX_STEPS]; } http_route; /** * Global context passed to every connection using the same pointer */ typedef struct http_loop_gctx { http_route *routes; size_t route_count; void *(*custom_ctx_init)(); void (*custom_ctx_reset)(void *); void (*custom_ctx_free)(void *); const char *api_key; // Custom global context void *c; } http_loop_gctx; /** * Initialize a new global context * * @return pointer to the newly allocated object. */ http_loop_gctx *http_loop_gctx_init(); /** * Invidivual context initialized for every connection */ typedef struct http_loop_ctx { http_request req; http_response res; http_route *route; size_t current_step; http_loop_gctx *g; void *c; } http_loop_ctx; /** * Initialize a context struct * * @param g global context * @return pointer to the newly allocated context */ http_loop_ctx *http_loop_ctx_init(http_loop_gctx *g); /** * Resets an already allocated context so that it can be reused for a new * request. * * @param ctx context to reset */ void http_loop_ctx_reset(http_loop_ctx *ctx); /** * Free a context struct. Internally this first calls http_loop_ctx_reset. * * @param ctx context to free */ void http_loop_ctx_free(http_loop_ctx *ctx); /** * Represents an HTTP loop */ typedef struct event_loop http_loop; /** * Process incoming data as an HTTP request. This is the "handle_data" function * for the event loop. * * @param conn connection to process * @return whether another request can be processed immediately. */ bool http_loop_handle_request(event_loop_conn *conn); /** * Try to parse the incoming data as an HTTP request. * * @param conn connection to process * @return result of the parse */ http_parse_error http_loop_parse_request(event_loop_conn *conn); /** * Try to match the parsed request with one of the defined routes, aka route the * request. * * @param conn connection to process */ void http_loop_route_request(event_loop_conn *conn); /** * Advance the processing of the routed request's processing by cycling through * the request's various steps. * * @param conn connection to process */ void http_loop_process_request(event_loop_conn *conn); /** * Handles the response processing. This is the `write_data` function for the * event loop. * * @param conn connection to process */ void http_loop_handle_response(event_loop_conn *conn); /** * Request step that consumes the request body and stores it in a buffer. * * @param conn connection to process * @return true if the body has been fully received, false otherwise */ bool http_loop_step_body_to_buf(event_loop_conn *conn); /** * Request step that consumes the request body and stores it in a file. * * @param conn connection to process * @return true if the body has been fully received, false otherwise */ bool http_loop_step_body_to_file(event_loop_conn *conn); /** * Try to parse the Content-Length header. * * @param conn connection to process */ bool http_loop_step_parse_content_length(event_loop_conn *conn); /** * Authenticate the request using the X-Api-Key header. * * @param conn connection to check * @return always true */ bool http_loop_step_auth(event_loop_conn *conn); /** * A step that simply sets the connection's state to res. * * @param conn connection to process * @return always true */ bool http_loop_step_switch_res(event_loop_conn *conn); /** * Write the HTTP header back to the connection. If `res->head` is not set, a * header will be generated for you. * * @param conn connection to process */ bool http_loop_step_write_header(event_loop_conn *conn); /** * Write the HTTP body back to the connection. * * @param conn connection to process */ bool http_loop_step_write_body(event_loop_conn *conn); /** * Initialize a new http loop. * * @param routes array of routes that should be served * @param route_count how many elements are in `routes` * @param custom_gctx the application's custom global context; can be NULL * @param custom_ctx_init function to initialize a new custom context * @param custom_ctx_reset function to reset a custom context * @param custom_ctx_free function to free a custom context; will always be run * after a reset * @return pointer to the newly allocated object */ http_loop *http_loop_init(http_route *routes, size_t route_count, void *custom_gctx, void *(*custom_ctx_init)(), void(custom_ctx_reset)(void *), void(custom_ctx_free)(void *)); /** * Set the API key the authentication steps should use. * * @param hl HTTP loop to set key in * @param api_key API key to use */ void http_loop_set_api_key(http_loop *hl, const char *api_key); /** * Run the HTTP loop. This function never returns. * * @param el the event loop * @param port on what port to listen */ void http_loop_run(http_loop *hl, int port); #endif