316 lines
6.4 KiB
C
316 lines
6.4 KiB
C
#define _POSIX_C_SOURCE 200809L
|
|
|
|
#include "handler.h"
|
|
#include "http.h"
|
|
#include "server.h"
|
|
#include "wildcard_cmp.h"
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
struct handler
|
|
{
|
|
struct handler_cfg cfg;
|
|
struct elem
|
|
{
|
|
char *url;
|
|
enum http_op op;
|
|
handler_fn f;
|
|
void *user;
|
|
} *elem;
|
|
|
|
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 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 elem *const e = &h->elem[i];
|
|
|
|
if (e->op == p->op && !wildcard_cmp(p->resource, e->url, true))
|
|
return e->f(p, r, e->user);
|
|
}
|
|
|
|
fprintf(stderr, "Not found: %s\n", p->resource);
|
|
|
|
*r = (const struct http_response)
|
|
{
|
|
.status = HTTP_STATUS_NOT_FOUND
|
|
};
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int on_length(const unsigned long long len,
|
|
const struct http_cookie *const c, struct http_response *const r,
|
|
void *const user)
|
|
{
|
|
struct client *const cl = user;
|
|
struct handler *const h = cl->h;
|
|
|
|
if (h->cfg.length)
|
|
return h->cfg.length(len, c, r, h->cfg.user);
|
|
|
|
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,
|
|
.length = on_length,
|
|
.user = ret,
|
|
.tmpdir = h->cfg.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_poll(h->server, &io, &exit);
|
|
|
|
if (exit)
|
|
{
|
|
printf("Exiting...\n");
|
|
break;
|
|
}
|
|
else if (!c)
|
|
{
|
|
fprintf(stderr, "%s: server_poll 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->elem[i].url);
|
|
|
|
free(h->elem);
|
|
free_clients(h);
|
|
server_close(h->server);
|
|
}
|
|
|
|
free(h);
|
|
}
|
|
|
|
struct handler *handler_alloc(const struct handler_cfg *const cfg)
|
|
{
|
|
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){.cfg = *cfg};
|
|
return h;
|
|
}
|
|
|
|
int handler_add(struct handler *const h, const char *url,
|
|
const enum http_op op, const handler_fn f, void *const user)
|
|
{
|
|
const size_t n = h->n_cfg + 1;
|
|
struct elem *const elem = realloc(h->elem, n * sizeof *h->elem);
|
|
|
|
if (!elem)
|
|
{
|
|
fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
struct elem *const e = &elem[h->n_cfg];
|
|
|
|
*e = (const struct elem)
|
|
{
|
|
.url = strdup(url),
|
|
.op = op,
|
|
.f = f,
|
|
.user = user
|
|
};
|
|
|
|
if (!e->url)
|
|
{
|
|
fprintf(stderr, "%s: strdup(3): %s\n", __func__, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
h->elem = elem;
|
|
h->n_cfg = n;
|
|
return 0;
|
|
}
|