feat(http): fully decouple HTTP loop functionality
							parent
							
								
									fc4187e6ce
								
							
						
					
					
						commit
						6d74c8c550
					
				| 
						 | 
				
			
			@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 | 
			
		|||
 | 
			
		||||
## [Unreleased](https://git.rustybever.be/Chewing_Bever/lander/src/branch/dev)
 | 
			
		||||
 | 
			
		||||
* Fully decoupled HTTP loop functionnality
 | 
			
		||||
 | 
			
		||||
## [0.1.0](https://git.rustybever.be/Chewing_Bever/lander/src/tag/0.1.0)
 | 
			
		||||
 | 
			
		||||
### Added
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,9 +52,12 @@ typedef struct http_route {
 | 
			
		|||
typedef struct http_loop_gctx {
 | 
			
		||||
  http_route *routes;
 | 
			
		||||
  size_t route_count;
 | 
			
		||||
  Trie *trie;
 | 
			
		||||
  void *(*custom_ctx_init)();
 | 
			
		||||
  void (*custom_ctx_reset)(void *);
 | 
			
		||||
  void (*custom_ctx_free)(void *);
 | 
			
		||||
  const char *api_key;
 | 
			
		||||
  const char *data_dir;
 | 
			
		||||
  // Custom global context
 | 
			
		||||
  void *c;
 | 
			
		||||
} http_loop_gctx;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -73,6 +76,7 @@ typedef struct http_loop_ctx {
 | 
			
		|||
  http_route *route;
 | 
			
		||||
  size_t current_step;
 | 
			
		||||
  http_loop_gctx *g;
 | 
			
		||||
  void *c;
 | 
			
		||||
} http_loop_ctx;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -98,6 +102,11 @@ void http_loop_ctx_reset(http_loop_ctx *ctx);
 | 
			
		|||
 */
 | 
			
		||||
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.
 | 
			
		||||
| 
						 | 
				
			
			@ -174,10 +183,27 @@ bool http_loop_step_switch_res(event_loop_conn *conn);
 | 
			
		|||
/**
 | 
			
		||||
 * Initialize a new http loop.
 | 
			
		||||
 *
 | 
			
		||||
 * @param gctx global context for the event loop
 | 
			
		||||
 * @param routes array of routes that should be served
 | 
			
		||||
 * @parma 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
 | 
			
		||||
 */
 | 
			
		||||
event_loop *http_loop_init(http_loop_gctx *gctx);
 | 
			
		||||
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.
 | 
			
		||||
| 
						 | 
				
			
			@ -185,6 +211,6 @@ event_loop *http_loop_init(http_loop_gctx *gctx);
 | 
			
		|||
 * @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(http_loop *hl, int port);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,6 +5,23 @@
 | 
			
		|||
 | 
			
		||||
extern http_route lander_routes[4];
 | 
			
		||||
 | 
			
		||||
typedef struct lander_gctx {
 | 
			
		||||
  const char *data_dir;
 | 
			
		||||
  Trie *trie;
 | 
			
		||||
} lander_gctx;
 | 
			
		||||
 | 
			
		||||
typedef struct lander_ctx {
 | 
			
		||||
  char *key;
 | 
			
		||||
} lander_ctx;
 | 
			
		||||
 | 
			
		||||
void *lander_gctx_init();
 | 
			
		||||
 | 
			
		||||
void *lander_ctx_init();
 | 
			
		||||
 | 
			
		||||
void lander_ctx_reset(lander_ctx *ctx);
 | 
			
		||||
 | 
			
		||||
void lander_ctx_free(lander_ctx *ctx);
 | 
			
		||||
 | 
			
		||||
bool lander_get_index(event_loop_conn *conn);
 | 
			
		||||
 | 
			
		||||
bool lander_get_entry(event_loop_conn *conn);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,18 +46,32 @@ bool http_loop_handle_request(event_loop_conn *conn) {
 | 
			
		|||
  return conn->state == event_loop_conn_state_req;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
event_loop *http_loop_init(http_loop_gctx *gctx) {
 | 
			
		||||
event_loop *http_loop_init(http_route *routes, size_t route_count,
 | 
			
		||||
                           void *custom_gctx, void *(*custom_ctx_init)(),
 | 
			
		||||
                           void(custom_ctx_reset)(), void(custom_ctx_free)()) {
 | 
			
		||||
  event_loop *el = event_loop_init();
 | 
			
		||||
 | 
			
		||||
  el->ctx_init = (void *(*)(void *))http_loop_ctx_init;
 | 
			
		||||
  el->ctx_free = (void (*)(void *))http_loop_ctx_free;
 | 
			
		||||
  el->handle_data = http_loop_handle_request;
 | 
			
		||||
  el->write_data = http_loop_write_response;
 | 
			
		||||
 | 
			
		||||
  http_loop_gctx *gctx = http_loop_gctx_init();
 | 
			
		||||
  gctx->c = custom_gctx;
 | 
			
		||||
  gctx->routes = routes;
 | 
			
		||||
  gctx->route_count = route_count;
 | 
			
		||||
  gctx->custom_ctx_init = custom_ctx_init;
 | 
			
		||||
  gctx->custom_ctx_reset = custom_ctx_reset;
 | 
			
		||||
  gctx->custom_ctx_free = custom_ctx_free;
 | 
			
		||||
  el->gctx = gctx;
 | 
			
		||||
 | 
			
		||||
  return el;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void http_loop_set_api_key(http_loop *hl, const char *api_key) {
 | 
			
		||||
  ((http_loop_gctx *)hl->gctx)->api_key = api_key;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void http_loop_run(event_loop *el, int port) {
 | 
			
		||||
  debug("Compiling RegEx routes");
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,12 +12,14 @@ http_loop_gctx *http_loop_gctx_init() {
 | 
			
		|||
http_loop_ctx *http_loop_ctx_init(http_loop_gctx *g) {
 | 
			
		||||
  http_loop_ctx *ctx = calloc(sizeof(http_loop_ctx), 1);
 | 
			
		||||
  ctx->g = g;
 | 
			
		||||
  ctx->c = g->custom_ctx_init();
 | 
			
		||||
 | 
			
		||||
  return ctx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void http_loop_ctx_free(http_loop_ctx *ctx) {
 | 
			
		||||
  http_loop_ctx_reset(ctx);
 | 
			
		||||
  ctx->g->custom_ctx_free(ctx->c);
 | 
			
		||||
 | 
			
		||||
  free(ctx);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -45,4 +47,6 @@ void http_loop_ctx_reset(http_loop_ctx *ctx) {
 | 
			
		|||
  ctx->res.status = 0;
 | 
			
		||||
  ctx->res.head_len = 0;
 | 
			
		||||
  ctx->res.head_written = 0;
 | 
			
		||||
 | 
			
		||||
  ctx->g->custom_ctx_reset(ctx->c);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,3 +22,11 @@ http_route lander_routes[] = {
 | 
			
		|||
     .steps = {http_loop_step_auth, lander_post_paste,
 | 
			
		||||
               http_loop_step_body_to_file, http_loop_step_switch_res, NULL}},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void *lander_gctx_init() { return calloc(1, sizeof(lander_gctx)); }
 | 
			
		||||
 | 
			
		||||
void *lander_ctx_init() { return calloc(1, sizeof(lander_ctx)); }
 | 
			
		||||
 | 
			
		||||
void lander_ctx_reset(lander_ctx *ctx) {}
 | 
			
		||||
 | 
			
		||||
void lander_ctx_free(lander_ctx *ctx) { free(ctx); }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,12 +24,13 @@ bool lander_get_index(event_loop_conn *conn) {
 | 
			
		|||
 | 
			
		||||
bool lander_get_entry(event_loop_conn *conn) {
 | 
			
		||||
  http_loop_ctx *ctx = conn->ctx;
 | 
			
		||||
  lander_gctx *c_gctx = ctx->g->c;
 | 
			
		||||
 | 
			
		||||
  const char *key = &ctx->req.path[ctx->req.regex_groups[1].rm_so];
 | 
			
		||||
  int key_len = ctx->req.regex_groups[1].rm_eo - ctx->req.regex_groups[1].rm_so;
 | 
			
		||||
 | 
			
		||||
  Entry *entry;
 | 
			
		||||
  TrieExitCode res = trie_search_len(ctx->g->trie, &entry, key, key_len);
 | 
			
		||||
  TrieExitCode res = trie_search_len(c_gctx->trie, &entry, key, key_len);
 | 
			
		||||
 | 
			
		||||
  if (res == NotFound) {
 | 
			
		||||
    ctx->res.status = http_not_found;
 | 
			
		||||
| 
						 | 
				
			
			@ -37,8 +38,8 @@ bool lander_get_entry(event_loop_conn *conn) {
 | 
			
		|||
    ctx->res.status = http_moved_permanently;
 | 
			
		||||
    http_res_add_header(&ctx->res, http_header_location, entry->string, false);
 | 
			
		||||
  } else if (entry->type == Paste) {
 | 
			
		||||
    char fname[strlen(ctx->g->data_dir) + 8 + key_len + 1];
 | 
			
		||||
    sprintf(fname, "%s/pastes/%.*s", ctx->g->data_dir, key_len, key);
 | 
			
		||||
    char fname[strlen(c_gctx->data_dir) + 8 + key_len + 1];
 | 
			
		||||
    sprintf(fname, "%s/pastes/%.*s", c_gctx->data_dir, key_len, key);
 | 
			
		||||
 | 
			
		||||
    http_res_set_body_file(&ctx->res, fname);
 | 
			
		||||
    // TODO don't call everything a text file
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,6 +5,8 @@
 | 
			
		|||
// TODO entry leaks if key is already present
 | 
			
		||||
static bool add_entry(char **key_ptr, int *key_len_ptr, http_loop_ctx *ctx,
 | 
			
		||||
                      Entry *entry, bool random) {
 | 
			
		||||
  lander_gctx *c_gctx = ctx->g->c;
 | 
			
		||||
 | 
			
		||||
  // The first match group matches the "long" path
 | 
			
		||||
  bool secure =
 | 
			
		||||
      (ctx->req.regex_groups[1].rm_eo - ctx->req.regex_groups[1].rm_so) == 1;
 | 
			
		||||
| 
						 | 
				
			
			@ -14,7 +16,7 @@ static bool add_entry(char **key_ptr, int *key_len_ptr, http_loop_ctx *ctx,
 | 
			
		|||
  TrieExitCode res;
 | 
			
		||||
 | 
			
		||||
  if (random) {
 | 
			
		||||
    res = trie_add_random(ctx->g->trie, &key, entry, secure);
 | 
			
		||||
    res = trie_add_random(c_gctx->trie, &key, entry, secure);
 | 
			
		||||
 | 
			
		||||
    if (res == Ok) {
 | 
			
		||||
      key_len = strlen(key);
 | 
			
		||||
| 
						 | 
				
			
			@ -23,7 +25,7 @@ static bool add_entry(char **key_ptr, int *key_len_ptr, http_loop_ctx *ctx,
 | 
			
		|||
    key = (char *)&ctx->req.path[ctx->req.regex_groups[2].rm_so];
 | 
			
		||||
    key_len = ctx->req.regex_groups[2].rm_eo - ctx->req.regex_groups[2].rm_so;
 | 
			
		||||
 | 
			
		||||
    res = trie_add_len(ctx->g->trie, key, key_len, entry);
 | 
			
		||||
    res = trie_add_len(c_gctx->trie, key, key_len, entry);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  switch (res) {
 | 
			
		||||
| 
						 | 
				
			
			@ -89,6 +91,8 @@ bool lander_post_redirect(event_loop_conn *conn) {
 | 
			
		|||
 | 
			
		||||
bool lander_post_paste(event_loop_conn *conn) {
 | 
			
		||||
  http_loop_ctx *ctx = conn->ctx;
 | 
			
		||||
  lander_gctx *c_gctx = ctx->g->c;
 | 
			
		||||
 | 
			
		||||
  bool random =
 | 
			
		||||
      ctx->req.regex_groups[2].rm_eo == ctx->req.regex_groups[2].rm_so;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -102,8 +106,8 @@ bool lander_post_paste(event_loop_conn *conn) {
 | 
			
		|||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  char *fname = malloc(strlen(ctx->g->data_dir) + 8 + key_len + 1);
 | 
			
		||||
  sprintf(fname, "%s/pastes/%.*s", ctx->g->data_dir, key_len, key);
 | 
			
		||||
  char *fname = malloc(strlen(c_gctx->data_dir) + 8 + key_len + 1);
 | 
			
		||||
  sprintf(fname, "%s/pastes/%.*s", c_gctx->data_dir, key_len, key);
 | 
			
		||||
 | 
			
		||||
  ctx->req.body.fname = fname;
 | 
			
		||||
  ctx->req.body.fname_owned = true;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										18
									
								
								src/main.c
								
								
								
								
							
							
						
						
									
										18
									
								
								src/main.c
								
								
								
								
							| 
						 | 
				
			
			@ -45,13 +45,15 @@ int main() {
 | 
			
		|||
 | 
			
		||||
  info("Trie initialized and populated with %i entries", trie_size(trie));
 | 
			
		||||
 | 
			
		||||
  http_loop_gctx *gctx = http_loop_gctx_init();
 | 
			
		||||
  gctx->trie = trie;
 | 
			
		||||
  gctx->routes = lander_routes;
 | 
			
		||||
  gctx->route_count = sizeof(lander_routes) / sizeof(lander_routes[0]);
 | 
			
		||||
  gctx->api_key = api_key;
 | 
			
		||||
  gctx->data_dir = data_dir;
 | 
			
		||||
  event_loop *el = http_loop_init(gctx);
 | 
			
		||||
  lander_gctx *c_gctx = lander_gctx_init();
 | 
			
		||||
  c_gctx->data_dir = data_dir;
 | 
			
		||||
  c_gctx->trie = trie;
 | 
			
		||||
 | 
			
		||||
  http_loop_run(el, port);
 | 
			
		||||
  http_loop *hl = http_loop_init(
 | 
			
		||||
      lander_routes, sizeof(lander_routes) / sizeof(lander_routes[0]), c_gctx,
 | 
			
		||||
      lander_ctx_init, (void (*)(void *))lander_ctx_reset,
 | 
			
		||||
      (void (*)(void *))lander_ctx_free);
 | 
			
		||||
  http_loop_set_api_key(hl, api_key);
 | 
			
		||||
 | 
			
		||||
  http_loop_run(hl, port);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue