docs: document event & http loop
parent
323fa65921
commit
62c42331d4
|
@ -47,50 +47,90 @@ typedef struct event_loop {
|
||||||
size_t connection_count;
|
size_t connection_count;
|
||||||
// Global context passed to every connection
|
// Global context passed to every connection
|
||||||
void *gctx;
|
void *gctx;
|
||||||
// Function to initialize a context for a connection
|
/**
|
||||||
|
* Function to initialize a connection context.
|
||||||
|
*
|
||||||
|
* @param gctx global context of the event loop
|
||||||
|
* @return pointer to the allocated object.
|
||||||
|
*/
|
||||||
void *(*ctx_init)(void *gctx);
|
void *(*ctx_init)(void *gctx);
|
||||||
// Function to free a context for a connection
|
/**
|
||||||
|
* Function to free a connection context object.
|
||||||
|
*
|
||||||
|
* @param ctx context to free
|
||||||
|
*/
|
||||||
void (*ctx_free)(void *ctx);
|
void (*ctx_free)(void *ctx);
|
||||||
// Function that processes incoming data
|
/**
|
||||||
|
* Function to process incoming data while in the req state.
|
||||||
|
*
|
||||||
|
* @param conn connection to process
|
||||||
|
* @return whether the function can be called again immediately in the same
|
||||||
|
* event loop cycle. This allows quicly processing multiple requests without
|
||||||
|
* waiting for I/O.
|
||||||
|
*/
|
||||||
bool (*handle_data)(event_loop_conn *conn);
|
bool (*handle_data)(event_loop_conn *conn);
|
||||||
// Function that writes outgoing data
|
/**
|
||||||
|
* Function to process outgoing data while in the res state.
|
||||||
|
*
|
||||||
|
* @param conn connection to proces
|
||||||
|
*/
|
||||||
void (*write_data)(event_loop_conn *conn);
|
void (*write_data)(event_loop_conn *conn);
|
||||||
} event_loop;
|
} event_loop;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize a new connection struct
|
* Initialize a new connection struct
|
||||||
|
*
|
||||||
|
* @param el the event loop
|
||||||
|
* @return pointer to the newly allocated connection struct
|
||||||
*/
|
*/
|
||||||
event_loop_conn *event_loop_conn_init(event_loop *el);
|
event_loop_conn *event_loop_conn_init(event_loop *el);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Free a connection struct
|
* Free a connection struct
|
||||||
|
*
|
||||||
|
* @param el the event loop
|
||||||
|
* @param conn connection struct to free
|
||||||
*/
|
*/
|
||||||
void event_loop_conn_free(event_loop *el, event_loop_conn *conn);
|
void event_loop_conn_free(event_loop *el, event_loop_conn *conn);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle I/O for a connection, be it reading input or writing output.
|
* Handle I/O for a connection, be it reading input or writing output.
|
||||||
|
*
|
||||||
|
* @param el the event loop
|
||||||
|
* @param conn the connection to process
|
||||||
*/
|
*/
|
||||||
void event_loop_conn_io(event_loop *el, event_loop_conn *conn);
|
void event_loop_conn_io(event_loop *el, event_loop_conn *conn);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize a new event loop struct
|
* Initialize a new event loop struct
|
||||||
|
*
|
||||||
|
* @return pointer to the newly allocated event loop struct
|
||||||
*/
|
*/
|
||||||
event_loop *event_loop_init();
|
event_loop *event_loop_init();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Place a new connection into the event loop's internal array.
|
* Place a new connection into the event loop's internal array.
|
||||||
*
|
*
|
||||||
* Returns -1 if the internal realloc failed
|
* @param el the event loop
|
||||||
|
* @param conn connection to insert
|
||||||
|
* @return 0 on success, -1 if the internal realloc failed.
|
||||||
*/
|
*/
|
||||||
int event_loop_put(event_loop *el, event_loop_conn *conn);
|
int event_loop_put(event_loop *el, event_loop_conn *conn);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accept a new connection for the given file descriptor.
|
* Accept a new connection for the given file descriptor.
|
||||||
|
*
|
||||||
|
* @param el the event loop
|
||||||
|
* @param fd file descriptor for the connection
|
||||||
|
* @return 0 if successful, negative value otherwise
|
||||||
*/
|
*/
|
||||||
int event_loop_accept(event_loop *el, int fd);
|
int event_loop_accept(event_loop *el, int fd);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Run the event loop. This function never returns.
|
* Run the event loop. This function never returns.
|
||||||
|
*
|
||||||
|
* @param el the event loop
|
||||||
|
* @param port on what port to listen
|
||||||
*/
|
*/
|
||||||
void event_loop_run(event_loop *el, int port);
|
void event_loop_run(event_loop *el, int port);
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#define HTTP_MAX_ALLOWED_HEADERS 16
|
#define HTTP_MAX_ALLOWED_HEADERS 16
|
||||||
#define HTTP_MAX_REGEX_GROUPS 4
|
#define HTTP_MAX_REGEX_GROUPS 4
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Struct representing the specific type of request
|
* Struct representing the specific type of request
|
||||||
*/
|
*/
|
||||||
typedef struct http_request {
|
typedef struct http_request {
|
||||||
|
@ -29,6 +29,9 @@ typedef struct http_request {
|
||||||
size_t num_headers;
|
size_t num_headers;
|
||||||
} http_request;
|
} http_request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of the HTTP parse function
|
||||||
|
*/
|
||||||
typedef enum http_parse_error {
|
typedef enum http_parse_error {
|
||||||
http_parse_error_ok = 0,
|
http_parse_error_ok = 0,
|
||||||
http_parse_error_incomplete = 1,
|
http_parse_error_incomplete = 1,
|
||||||
|
|
|
@ -6,12 +6,18 @@
|
||||||
|
|
||||||
#include "http/types.h"
|
#include "http/types.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Struct describing a header for the response.
|
||||||
|
*/
|
||||||
typedef struct http_response_header {
|
typedef struct http_response_header {
|
||||||
http_header type;
|
http_header type;
|
||||||
const char *value;
|
const char *value;
|
||||||
bool owned;
|
bool owned;
|
||||||
} http_response_header;
|
} http_response_header;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Struct representing an HTTP response.
|
||||||
|
*/
|
||||||
typedef struct http_response {
|
typedef struct http_response {
|
||||||
http_status status;
|
http_status status;
|
||||||
const char *head;
|
const char *head;
|
||||||
|
@ -22,26 +28,41 @@ typedef struct http_response {
|
||||||
size_t header_count;
|
size_t header_count;
|
||||||
} http_response;
|
} http_response;
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Set the request body to the given buffer. If owned is set to true, the body
|
* Set the request body to the given buffer.
|
||||||
* buffer will be free'd after the request has finished.
|
*
|
||||||
|
* @param res response to modify
|
||||||
|
* @param body pointer to the buf containing the body
|
||||||
|
* @param body_len length of the body
|
||||||
|
* @owned whether the body should be freed after processing the request
|
||||||
*/
|
*/
|
||||||
void http_res_set_body_buf(http_response *res, const char *body,
|
void http_res_set_body_buf(http_response *res, const char *body,
|
||||||
size_t body_len, bool owned);
|
size_t body_len, bool owned);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Set the request body to the given filename.
|
* Set the request body to the given filename.
|
||||||
|
*
|
||||||
|
* @param res response to modify
|
||||||
|
* @param filename path to the file to return
|
||||||
*/
|
*/
|
||||||
void http_res_set_body_file(http_response *res, const char *filename);
|
void http_res_set_body_file(http_response *res, const char *filename);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Add a header to the response.
|
* Add a header to the response.
|
||||||
|
*
|
||||||
|
* @param res response to modify
|
||||||
|
* @param type type of the header
|
||||||
|
* @param value value of the header
|
||||||
|
* @param owned whether the value should be freed after processing the request
|
||||||
*/
|
*/
|
||||||
void http_res_add_header(http_response *res, http_header type,
|
void http_res_add_header(http_response *res, http_header type,
|
||||||
const char *value, bool owned);
|
const char *value, bool owned);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Add a Content-Type header corresponding to the mime type.
|
* Add a Content-Type header corresponding to the mime type.
|
||||||
|
*
|
||||||
|
* @param res response to modiy
|
||||||
|
* @param mime_type mime type of the response
|
||||||
*/
|
*/
|
||||||
void http_res_set_mime_type(http_response *res, http_mime_type mime_type);
|
void http_res_set_mime_type(http_response *res, http_mime_type mime_type);
|
||||||
|
|
||||||
|
|
|
@ -147,10 +147,25 @@ typedef struct http_body {
|
||||||
size_t len;
|
size_t len;
|
||||||
} http_body;
|
} http_body;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a new body struct.
|
||||||
|
*
|
||||||
|
* @return pointer to the newly allocated object.
|
||||||
|
*/
|
||||||
http_body *http_body_init();
|
http_body *http_body_init();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset a body, allowing it to be reused as if newly allocated.
|
||||||
|
*
|
||||||
|
* @param body body to reset
|
||||||
|
*/
|
||||||
void http_body_reset(http_body *body);
|
void http_body_reset(http_body *body);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free a body. Internally, this calls http_body_reset.
|
||||||
|
*
|
||||||
|
* @param body body to free
|
||||||
|
*/
|
||||||
void http_body_free(http_body *body);
|
void http_body_free(http_body *body);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -9,25 +9,44 @@
|
||||||
#include "http/types.h"
|
#include "http/types.h"
|
||||||
#include "trie.h"
|
#include "trie.h"
|
||||||
|
|
||||||
|
// Max amount of steps a route can use
|
||||||
#define HTTP_LOOP_MAX_STEPS 17
|
#define HTTP_LOOP_MAX_STEPS 17
|
||||||
|
|
||||||
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||||
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of a route
|
||||||
|
*/
|
||||||
typedef enum http_route_type {
|
typedef enum http_route_type {
|
||||||
http_route_literal = 0,
|
http_route_literal = 0,
|
||||||
http_route_regex = 1,
|
http_route_regex = 1,
|
||||||
} http_route_type;
|
} http_route_type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function describing a step in a route's processing.
|
||||||
|
*
|
||||||
|
* @param conn connection to process
|
||||||
|
* @return whether the processing can immediately advance to the next step. A
|
||||||
|
* step should return false if it's e.g. waiting for I/O, and can therefore not
|
||||||
|
* finish its task in the current cycle of the event loop.
|
||||||
|
*/
|
||||||
|
typedef bool (*step)(event_loop_conn *conn);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Struct describing a route a request can take.
|
||||||
|
*/
|
||||||
typedef struct http_route {
|
typedef struct http_route {
|
||||||
http_route_type type;
|
http_route_type type;
|
||||||
http_method method;
|
http_method method;
|
||||||
char *path;
|
char *path;
|
||||||
|
// Compiled regex for a regex route. This value gets set at runtime when
|
||||||
|
// starting the http loop
|
||||||
regex_t *regex;
|
regex_t *regex;
|
||||||
bool (*steps[HTTP_LOOP_MAX_STEPS])(event_loop_conn *);
|
step steps[HTTP_LOOP_MAX_STEPS];
|
||||||
} 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 {
|
||||||
|
@ -37,12 +56,14 @@ typedef struct http_loop_gctx {
|
||||||
const char *api_key;
|
const char *api_key;
|
||||||
} http_loop_gctx;
|
} http_loop_gctx;
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Initialize a new global context
|
* Initialize a new global context
|
||||||
|
*
|
||||||
|
* @return pointer to the newly allocated object.
|
||||||
*/
|
*/
|
||||||
http_loop_gctx *http_loop_gctx_init();
|
http_loop_gctx *http_loop_gctx_init();
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Invidivual context initialized for every connection
|
* Invidivual context initialized for every connection
|
||||||
*/
|
*/
|
||||||
typedef struct http_loop_ctx {
|
typedef struct http_loop_ctx {
|
||||||
|
@ -53,70 +74,116 @@ typedef struct http_loop_ctx {
|
||||||
http_loop_gctx *g;
|
http_loop_gctx *g;
|
||||||
} http_loop_ctx;
|
} http_loop_ctx;
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Initialize a context struct
|
* 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);
|
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
|
* Resets an already allocated context so that it can be reused for a new
|
||||||
* request.
|
* request.
|
||||||
|
*
|
||||||
|
* @param ctx context to reset
|
||||||
*/
|
*/
|
||||||
void http_loop_ctx_reset(http_loop_ctx *ctx);
|
void http_loop_ctx_reset(http_loop_ctx *ctx);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Free a context struct
|
* 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);
|
void http_loop_ctx_free(http_loop_ctx *ctx);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Process incoming data as an HTTP request. This is the "handle_data" function
|
* Process incoming data as an HTTP request. This is the "handle_data" function
|
||||||
* for the event loop.
|
* 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);
|
bool http_loop_handle_request(event_loop_conn *conn);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Write the HTTP response to the file descriptor. This is the "write_data"
|
* Write the HTTP response to the file descriptor. This is the "write_data"
|
||||||
* function for the event loop.
|
* function for the event loop.
|
||||||
|
*
|
||||||
|
* @param conn connection to process
|
||||||
*/
|
*/
|
||||||
void http_loop_write_response(event_loop_conn *conn);
|
void http_loop_write_response(event_loop_conn *conn);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Try to parse the incoming data as an HTTP request.
|
* 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);
|
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
|
* Try to match the parsed request with one of the defined routes, aka route the
|
||||||
* request.
|
* request.
|
||||||
|
*
|
||||||
|
* @param conn connection to process
|
||||||
*/
|
*/
|
||||||
void http_loop_route_request(event_loop_conn *conn);
|
void http_loop_route_request(event_loop_conn *conn);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Advance the processing of the routed request's processing by cycling through
|
* Advance the processing of the routed request's processing by cycling through
|
||||||
* the request's various steps.
|
* the request's various steps.
|
||||||
|
*
|
||||||
|
* @param conn connection to process
|
||||||
*/
|
*/
|
||||||
void http_loop_process_request(event_loop_conn *conn);
|
void http_loop_process_request(event_loop_conn *conn);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Request step that consumes the request body and stores it in a buffer
|
* 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);
|
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);
|
bool http_loop_step_body_to_file(event_loop_conn *conn);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Authenticate the request using the X-Api-Key header
|
* 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);
|
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);
|
bool http_loop_step_switch_res(event_loop_conn *conn);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize a new http loop
|
* Initialize a new http loop.
|
||||||
|
*
|
||||||
|
* @param gctx global context for the event loop
|
||||||
|
* @return pointer to the newly allocated object
|
||||||
*/
|
*/
|
||||||
event_loop *http_loop_init(http_loop_gctx *gctx);
|
event_loop *http_loop_init(http_loop_gctx *gctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the HTTP loop. This function never returns.
|
||||||
|
*
|
||||||
|
* @param el the event loop
|
||||||
|
* @param port on what port to listen
|
||||||
|
*/
|
||||||
void http_loop_run(event_loop *el, int port);
|
void http_loop_run(event_loop *el, int port);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -115,7 +115,7 @@ bool http_loop_step_auth(event_loop_conn *conn) {
|
||||||
|
|
||||||
if ((strncmp("X-Api-Key", header->name, header->name_len) == 0) &&
|
if ((strncmp("X-Api-Key", header->name, header->name_len) == 0) &&
|
||||||
(strncmp(header->value, ctx->g->api_key, header->value_len) == 0) &&
|
(strncmp(header->value, ctx->g->api_key, header->value_len) == 0) &&
|
||||||
strlen(ctx->g->api_key) == header->value_len) {
|
(strlen(ctx->g->api_key) == header->value_len)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue