From 2968c5f67daa1c571f5f9cf9445de907f9490636 Mon Sep 17 00:00:00 2001 From: Xavier Del Campo Romero Date: Mon, 9 Jan 2023 01:22:54 +0100 Subject: Initial commit --- handler.c | 336 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 handler.c (limited to 'handler.c') diff --git a/handler.c b/handler.c new file mode 100644 index 0000000..b152cf1 --- /dev/null +++ b/handler.c @@ -0,0 +1,336 @@ +#include "handler.h" +#include "http.h" +#include "server.h" +#include +#include +#include +#include +#include +#include + +struct handler +{ + const char *tmpdir; + + struct handler_cfg + { + char *url; + enum http_op op; + handler_fn f; + void *user; + } *cfg; + + struct server *server; + struct client + { + struct handler *h; + struct server_client *c; + struct http_ctx *http; + struct client *next; + } *clients; + + size_t n_cfg; +}; + +static int on_read(void *const buf, const size_t n, void *const user) +{ + struct client *const c = user; + + return server_read(buf, n, c->c); +} + +static int on_write(const void *const buf, const size_t n, void *const user) +{ + struct client *const c = user; + + return server_write(buf, n, c->c); +} + +static int wildcard_cmp(const char *s, const char *p) +{ + while (*p && *s) + { + const char *const wc = strchr(p, '*'); + + if (!wc) + return strcmp(s, p); + + const size_t n = wc - p; + + if (n) + { + const int r = strncmp(s, p, n); + + if (r) + return r; + + p += n; + s += n; + } + else if (*(wc + 1) == *s) + { + p = wc + 1; + s += n; + } + else if (*(wc + 1) == '*') + p++; + else + { + s++; + p += n; + } + } + + while (*p) + if (*p++ != '*') + return -1; + + return 0; +} + +static int on_payload(const struct http_payload *const p, + struct http_response *const r, void *const user) +{ + struct client *const c = user; + struct handler *const h = c->h; + + for (size_t i = 0; i < h->n_cfg; i++) + { + const struct handler_cfg *const cfg = &h->cfg[i]; + + if (cfg->op == p->op && !wildcard_cmp(p->resource, cfg->url)) + return cfg->f(p, r, cfg->user); + } + + fprintf(stderr, "Not found: %s\n", p->resource); + + *r = (const struct http_response) + { + .status = HTTP_STATUS_NOT_FOUND + }; + + return 0; +} + +static struct client *find_or_alloc_client(struct handler *const h, + struct server_client *const c) +{ + for (struct client *cl = h->clients; cl; cl = cl->next) + { + if (cl->c == c) + return cl; + } + + struct client *const ret = malloc(sizeof *ret); + + if (!ret) + { + fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno)); + return NULL; + } + + const struct http_cfg cfg = + { + .read = on_read, + .write = on_write, + .payload = on_payload, + .user = ret, + .tmpdir = h->tmpdir + }; + + *ret = (const struct client) + { + .c = c, + .h = h, + .http = http_alloc(&cfg) + }; + + if (!ret->http) + { + fprintf(stderr, "%s: http_alloc failed\n", __func__); + return NULL; + } + + if (!h->clients) + h->clients = ret; + else + { + for (struct client *c = h->clients; c; c = c->next) + if (!c->next) + { + c->next = ret; + break; + } + } + + return ret; +} + +static void client_free(struct client *const c) +{ + if (c) + http_free(c->http); + + free(c); +} + +static int remove_client_from_list(struct handler *const h, + struct client *const c) +{ + int ret = -1; + + if (server_client_close(h->server, c->c)) + { + fprintf(stderr, "%s: server_client_close failed\n", + __func__); + goto end; + } + + for (struct client *cl = h->clients, *prev = NULL; cl; + prev = cl, cl = cl->next) + { + if (cl == c) + { + if (!prev) + h->clients = c->next; + else + prev->next = cl->next; + + break; + } + } + + ret = 0; + +end: + client_free(c); + return ret; +} + +int handler_listen(struct handler *const h, const short port) +{ + if (!(h->server = server_init(port))) + { + fprintf(stderr, "%s: server_init failed\n", __func__); + return -1; + } + + for (;;) + { + bool exit, io; + struct server_client *const c = server_select(h->server, &io, &exit); + + if (exit) + { + printf("Exiting...\n"); + break; + } + else if (!c) + { + fprintf(stderr, "%s: server_select failed\n", __func__); + return -1; + } + + struct client *const cl = find_or_alloc_client(h, c); + + if (!cl) + { + fprintf(stderr, "%s: find_or_alloc_client failed\n", __func__); + return -1; + } + else if (io) + { + bool write, close; + const int res = http_update(cl->http, &write, &close); + + if (res || close) + { + if (res < 0) + { + fprintf(stderr, "%s: http_update failed\n", __func__); + return -1; + } + else if (remove_client_from_list(h, cl)) + { + fprintf(stderr, "%s: remove_client_from_list failed\n", + __func__); + return -1; + } + } + else + server_client_write_pending(cl->c, write); + } + } + + return 0; +} + +static void free_clients(struct handler *const h) +{ + for (struct client *c = h->clients; c;) + { + struct client *const next = c->next; + + server_client_close(h->server, c->c); + client_free(c); + c = next; + } +} + +void handler_free(struct handler *const h) +{ + if (h) + { + for (size_t i = 0; i < h->n_cfg; i++) + free(h->cfg[i].url); + + free(h->cfg); + free_clients(h); + server_close(h->server); + } + + free(h); +} + +struct handler *handler_alloc(const char *const tmpdir) +{ + struct handler *const h = malloc(sizeof *h); + + if (!h) + { + fprintf(stderr, "%s: malloc(3) handler: %s\n", + __func__, strerror(errno)); + return NULL; + } + + *h = (const struct handler){.tmpdir = tmpdir}; + return h; +} + +int handler_add(struct handler *const h, const char *url, + const enum http_op op, const handler_fn f, void *const user) +{ + if (!h || !(h->cfg = realloc(h->cfg, (h->n_cfg + 1) * sizeof *h->cfg))) + { + fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno)); + return -1; + } + + char *const new = strdup(url); + + if (!new) + { + fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno)); + return -1; + } + + h->cfg[h->n_cfg++] = (const struct handler_cfg) + { + .url = new, + .op = op, + .f = f, + .user = user + }; + + return 0; +} -- cgit v1.2.3