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