2023-05-27 11:47:39 +02:00
|
|
|
#ifndef LANDER_HTTP_LOOP
|
|
|
|
#define LANDER_HTTP_LOOP
|
|
|
|
|
2023-05-27 15:38:06 +02:00
|
|
|
#include <regex.h>
|
|
|
|
|
2023-05-27 11:47:39 +02:00
|
|
|
#include "event_loop.h"
|
2023-05-30 16:00:08 +02:00
|
|
|
#include "http/req.h"
|
|
|
|
#include "http/res.h"
|
|
|
|
#include "http/types.h"
|
2023-05-27 11:47:39 +02:00
|
|
|
#include "trie.h"
|
|
|
|
|
2023-05-31 16:49:15 +02:00
|
|
|
// Max amount of steps a route can use
|
2023-05-30 19:36:58 +02:00
|
|
|
#define HTTP_LOOP_MAX_STEPS 17
|
|
|
|
|
2023-05-29 23:26:15 +02:00
|
|
|
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
|
|
|
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
|
|
|
|
2023-05-31 16:49:15 +02:00
|
|
|
/**
|
|
|
|
* Type of a route
|
|
|
|
*/
|
2023-05-27 15:38:06 +02:00
|
|
|
typedef enum http_route_type {
|
|
|
|
http_route_literal = 0,
|
|
|
|
http_route_regex = 1,
|
|
|
|
} http_route_type;
|
|
|
|
|
2023-05-31 16:49:15 +02:00
|
|
|
/**
|
|
|
|
* Function describing a step in a route's processing.
|
|
|
|
*
|
|
|
|
* @param conn connection to process
|
2023-11-02 10:27:34 +01:00
|
|
|
* @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.
|
2023-05-31 16:49:15 +02:00
|
|
|
*/
|
2023-11-03 15:02:03 +01:00
|
|
|
typedef bool (*http_step)(event_loop_conn *conn);
|
2023-05-31 16:49:15 +02:00
|
|
|
|
2023-11-03 15:02:03 +01:00
|
|
|
extern const http_step http_default_res_steps[HTTP_LOOP_MAX_STEPS];
|
2023-11-03 14:41:55 +01:00
|
|
|
|
2023-05-31 16:49:15 +02:00
|
|
|
/**
|
|
|
|
* Struct describing a route a request can take.
|
|
|
|
*/
|
2023-05-27 15:38:06 +02:00
|
|
|
typedef struct http_route {
|
|
|
|
http_route_type type;
|
2023-05-30 16:00:08 +02:00
|
|
|
http_method method;
|
2023-05-27 15:38:06 +02:00
|
|
|
char *path;
|
2023-05-31 16:49:15 +02:00
|
|
|
// Compiled regex for a regex route. This value gets set at runtime when
|
|
|
|
// starting the http loop
|
2023-05-27 15:38:06 +02:00
|
|
|
regex_t *regex;
|
2023-11-03 15:02:03 +01:00
|
|
|
const http_step steps[HTTP_LOOP_MAX_STEPS];
|
|
|
|
const http_step steps_res[HTTP_LOOP_MAX_STEPS];
|
2023-05-27 15:38:06 +02:00
|
|
|
} http_route;
|
|
|
|
|
2023-05-31 16:49:15 +02:00
|
|
|
/**
|
2023-05-27 11:47:39 +02:00
|
|
|
* Global context passed to every connection using the same pointer
|
|
|
|
*/
|
|
|
|
typedef struct http_loop_gctx {
|
2023-05-27 16:42:15 +02:00
|
|
|
http_route *routes;
|
|
|
|
size_t route_count;
|
2023-10-30 21:14:06 +01:00
|
|
|
void *(*custom_ctx_init)();
|
|
|
|
void (*custom_ctx_reset)(void *);
|
|
|
|
void (*custom_ctx_free)(void *);
|
2023-05-29 23:37:23 +02:00
|
|
|
const char *api_key;
|
2023-10-30 21:14:06 +01:00
|
|
|
// Custom global context
|
|
|
|
void *c;
|
2023-05-27 11:47:39 +02:00
|
|
|
} http_loop_gctx;
|
|
|
|
|
2023-05-31 16:49:15 +02:00
|
|
|
/**
|
2023-05-27 15:38:06 +02:00
|
|
|
* Initialize a new global context
|
2023-05-31 16:49:15 +02:00
|
|
|
*
|
|
|
|
* @return pointer to the newly allocated object.
|
2023-05-27 15:38:06 +02:00
|
|
|
*/
|
2023-05-27 11:47:39 +02:00
|
|
|
http_loop_gctx *http_loop_gctx_init();
|
|
|
|
|
2023-05-31 16:49:15 +02:00
|
|
|
/**
|
2023-05-27 11:47:39 +02:00
|
|
|
* Invidivual context initialized for every connection
|
|
|
|
*/
|
|
|
|
typedef struct http_loop_ctx {
|
|
|
|
http_request req;
|
2023-05-28 18:24:22 +02:00
|
|
|
http_response res;
|
2023-05-27 15:38:06 +02:00
|
|
|
http_route *route;
|
|
|
|
size_t current_step;
|
2023-05-27 11:47:39 +02:00
|
|
|
http_loop_gctx *g;
|
2023-10-30 21:14:06 +01:00
|
|
|
void *c;
|
2023-05-27 11:47:39 +02:00
|
|
|
} http_loop_ctx;
|
|
|
|
|
2023-05-31 16:49:15 +02:00
|
|
|
/**
|
2023-05-27 15:38:06 +02:00
|
|
|
* Initialize a context struct
|
2023-05-31 16:49:15 +02:00
|
|
|
*
|
|
|
|
* @param g global context
|
|
|
|
* @return pointer to the newly allocated context
|
2023-05-27 15:38:06 +02:00
|
|
|
*/
|
2023-05-27 11:47:39 +02:00
|
|
|
http_loop_ctx *http_loop_ctx_init(http_loop_gctx *g);
|
|
|
|
|
2023-05-31 16:49:15 +02:00
|
|
|
/**
|
2023-05-29 12:03:40 +02:00
|
|
|
* Resets an already allocated context so that it can be reused for a new
|
|
|
|
* request.
|
2023-05-31 16:49:15 +02:00
|
|
|
*
|
|
|
|
* @param ctx context to reset
|
2023-05-29 12:03:40 +02:00
|
|
|
*/
|
2023-05-28 18:24:22 +02:00
|
|
|
void http_loop_ctx_reset(http_loop_ctx *ctx);
|
|
|
|
|
2023-05-31 16:49:15 +02:00
|
|
|
/**
|
|
|
|
* Free a context struct. Internally this first calls http_loop_ctx_reset.
|
|
|
|
*
|
|
|
|
* @param ctx context to free
|
2023-05-27 15:38:06 +02:00
|
|
|
*/
|
2023-05-27 11:47:39 +02:00
|
|
|
void http_loop_ctx_free(http_loop_ctx *ctx);
|
|
|
|
|
2023-10-30 21:14:06 +01:00
|
|
|
/**
|
|
|
|
* Represents an HTTP loop
|
|
|
|
*/
|
|
|
|
typedef struct event_loop http_loop;
|
|
|
|
|
2023-05-31 16:49:15 +02:00
|
|
|
/**
|
2023-05-29 12:03:40 +02:00
|
|
|
* Process incoming data as an HTTP request. This is the "handle_data" function
|
|
|
|
* for the event loop.
|
2023-05-31 16:49:15 +02:00
|
|
|
*
|
|
|
|
* @param conn connection to process
|
|
|
|
* @return whether another request can be processed immediately.
|
2023-05-27 15:38:06 +02:00
|
|
|
*/
|
2023-05-27 11:47:39 +02:00
|
|
|
bool http_loop_handle_request(event_loop_conn *conn);
|
|
|
|
|
2023-05-31 16:49:15 +02:00
|
|
|
/**
|
2023-05-29 12:03:40 +02:00
|
|
|
* Try to parse the incoming data as an HTTP request.
|
2023-05-31 16:49:15 +02:00
|
|
|
*
|
|
|
|
* @param conn connection to process
|
|
|
|
* @return result of the parse
|
2023-05-29 12:03:40 +02:00
|
|
|
*/
|
2023-05-27 17:14:49 +02:00
|
|
|
http_parse_error http_loop_parse_request(event_loop_conn *conn);
|
|
|
|
|
2023-05-31 16:49:15 +02:00
|
|
|
/**
|
2023-05-29 12:03:40 +02:00
|
|
|
* Try to match the parsed request with one of the defined routes, aka route the
|
|
|
|
* request.
|
2023-05-31 16:49:15 +02:00
|
|
|
*
|
|
|
|
* @param conn connection to process
|
2023-05-29 12:03:40 +02:00
|
|
|
*/
|
2023-05-29 11:36:57 +02:00
|
|
|
void http_loop_route_request(event_loop_conn *conn);
|
2023-05-27 16:42:15 +02:00
|
|
|
|
2023-05-31 16:49:15 +02:00
|
|
|
/**
|
2023-05-29 12:03:40 +02:00
|
|
|
* Advance the processing of the routed request's processing by cycling through
|
|
|
|
* the request's various steps.
|
2023-05-31 16:49:15 +02:00
|
|
|
*
|
|
|
|
* @param conn connection to process
|
2023-05-29 12:03:40 +02:00
|
|
|
*/
|
2023-05-27 15:38:06 +02:00
|
|
|
void http_loop_process_request(event_loop_conn *conn);
|
|
|
|
|
2023-11-02 10:27:34 +01:00
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
|
2023-05-31 16:49:15 +02:00
|
|
|
/**
|
|
|
|
* 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
|
2023-05-29 23:26:15 +02:00
|
|
|
*/
|
|
|
|
bool http_loop_step_body_to_buf(event_loop_conn *conn);
|
|
|
|
|
2023-05-31 16:49:15 +02:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
2023-05-30 10:17:46 +02:00
|
|
|
bool http_loop_step_body_to_file(event_loop_conn *conn);
|
|
|
|
|
2023-11-03 13:22:25 +01:00
|
|
|
/**
|
|
|
|
* Try to parse the Content-Length header.
|
|
|
|
*
|
|
|
|
* @param conn connection to process
|
|
|
|
*/
|
|
|
|
bool http_loop_step_parse_content_length(event_loop_conn *conn);
|
|
|
|
|
2023-05-31 16:49:15 +02:00
|
|
|
/**
|
|
|
|
* Authenticate the request using the X-Api-Key header.
|
|
|
|
*
|
|
|
|
* @param conn connection to check
|
|
|
|
* @return always true
|
2023-05-29 23:37:23 +02:00
|
|
|
*/
|
|
|
|
bool http_loop_step_auth(event_loop_conn *conn);
|
|
|
|
|
2023-05-31 16:49:15 +02:00
|
|
|
/**
|
|
|
|
* A step that simply sets the connection's state to res.
|
|
|
|
*
|
|
|
|
* @param conn connection to process
|
|
|
|
* @return always true
|
|
|
|
*/
|
2023-05-30 10:17:46 +02:00
|
|
|
bool http_loop_step_switch_res(event_loop_conn *conn);
|
|
|
|
|
2023-11-02 10:27:34 +01:00
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
|
2023-05-27 15:38:06 +02:00
|
|
|
/**
|
2023-05-31 16:49:15 +02:00
|
|
|
* Initialize a new http loop.
|
|
|
|
*
|
2023-10-30 21:14:06 +01:00
|
|
|
* @param routes array of routes that should be served
|
2023-11-03 13:22:25 +01:00
|
|
|
* @param route_count how many elements are in `routes`
|
2023-10-30 21:14:06 +01:00
|
|
|
* @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
|
2023-05-31 16:49:15 +02:00
|
|
|
* @return pointer to the newly allocated object
|
2023-05-27 15:38:06 +02:00
|
|
|
*/
|
2023-10-30 21:14:06 +01:00
|
|
|
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);
|
2023-05-27 11:47:39 +02:00
|
|
|
|
2023-05-31 16:49:15 +02:00
|
|
|
/**
|
|
|
|
* Run the HTTP loop. This function never returns.
|
|
|
|
*
|
|
|
|
* @param el the event loop
|
|
|
|
* @param port on what port to listen
|
|
|
|
*/
|
2023-10-30 21:14:06 +01:00
|
|
|
void http_loop_run(http_loop *hl, int port);
|
2023-05-29 16:55:16 +02:00
|
|
|
|
2023-05-27 11:47:39 +02:00
|
|
|
#endif
|