diff options
| author | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2023-03-06 05:09:56 +0100 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2023-07-20 23:52:52 +0200 |
| commit | 7b810b55ab058b71dc11ac9ebb86f2de7c3623da (patch) | |
| tree | 5c822be4b1f2f44e2dfe08687f981479911c560d | |
| parent | f5dfbf6e8be018199c4d57375935fd1f5f3c93bb (diff) | |
Implement user quota
This feature allows admins to set a specific quota for each user, in
MiB. This feature is particularly useful for shared instances, where
unlimited user storage might be unfeasible or even dangerous for the
server.
Also, a nice HTML5 <progress> element has been added to the site that
shows how much of the quota has been consumed.
If no quota is set, slcl falls back to the default behaviour i.e.,
assume unlimited storage.
Limitations:
- While HTTP does specify a Content-Length, which determines the length
of the whole request, it does not specify how many files are involved
or their individual sizes.
- Because of this, if multiple files are uploaded simultaneously, the
whole request would be dropped if user quota is exceeded, even if not
all files exceeded it.
- Also, Content-Length adds the length of some HTTP boilerplate
(e.g.: boundaries), but slcl must rely on this before accepting the
whole request. In other words, this means some requests might be
rejected by slcl because of the extra bytes caused by such boilerplate.
- When the quota is exceeded, slcl must close the connection so that
the rest of the transfer is cancelled. Unfortunately, this means no
HTML can be sent back to the customer to inform about the situation.
| -rw-r--r-- | Makefile | 3 | ||||
| -rw-r--r-- | handler.c | 48 | ||||
| -rw-r--r-- | handler.h | 10 | ||||
| -rw-r--r-- | http.c | 21 | ||||
| -rw-r--r-- | http.h | 2 |
5 files changed, 64 insertions, 20 deletions
@@ -4,7 +4,8 @@ CC = cc # c99 (default value) does not allow POSIX extensions. PROJECT = slcl O = -Og -CFLAGS = $(O) -g -Wall -Idynstr/include -MD -MF - +CDEFS = -D_FILE_OFFSET_BITS=64 # Required for large file support on 32-bit. +CFLAGS = $(O) $(CDEFS) -g -Wall -Idynstr/include -MD -MF - LIBS = -lcjson -lssl -lm -lcrypto LDFLAGS = $(LIBS) DEPS = $(OBJECTS:.o=.d) @@ -10,15 +10,14 @@ struct handler { - const char *tmpdir; - - struct handler_cfg + struct handler_cfg cfg; + struct elem { char *url; enum http_op op; handler_fn f; void *user; - } *cfg; + } *elem; struct server *server; struct client @@ -96,10 +95,10 @@ static int on_payload(const struct http_payload *const p, for (size_t i = 0; i < h->n_cfg; i++) { - const struct handler_cfg *const cfg = &h->cfg[i]; + const struct elem *const e = &h->elem[i]; - if (cfg->op == p->op && !wildcard_cmp(p->resource, cfg->url)) - return cfg->f(p, r, cfg->user); + if (e->op == p->op && !wildcard_cmp(p->resource, e->url)) + return e->f(p, r, e->user); } fprintf(stderr, "Not found: %s\n", p->resource); @@ -112,6 +111,18 @@ static int on_payload(const struct http_payload *const p, return 0; } +static int on_length(const unsigned long long len, + const struct http_cookie *const c, void *const user) +{ + struct client *const cl = user; + struct handler *const h = cl->h; + + if (h->cfg.length) + return h->cfg.length(len, c, h->cfg.user); + + return 0; +} + static struct client *find_or_alloc_client(struct handler *const h, struct server_client *const c) { @@ -134,8 +145,9 @@ static struct client *find_or_alloc_client(struct handler *const h, .read = on_read, .write = on_write, .payload = on_payload, + .length = on_length, .user = ret, - .tmpdir = h->tmpdir + .tmpdir = h->cfg.tmpdir }; *ret = (const struct client) @@ -282,9 +294,9 @@ 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->elem[i].url); - free(h->cfg); + free(h->elem); free_clients(h); server_close(h->server); } @@ -292,7 +304,7 @@ void handler_free(struct handler *const h) free(h); } -struct handler *handler_alloc(const char *const tmpdir) +struct handler *handler_alloc(const struct handler_cfg *const cfg) { struct handler *const h = malloc(sizeof *h); @@ -303,7 +315,7 @@ struct handler *handler_alloc(const char *const tmpdir) return NULL; } - *h = (const struct handler){.tmpdir = tmpdir}; + *h = (const struct handler){.cfg = *cfg}; return h; } @@ -311,17 +323,17 @@ 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 handler_cfg *const cfgs = realloc(h->cfg, n * sizeof *h->cfg); + struct elem *const elem = realloc(h->elem, n * sizeof *h->elem); - if (!cfgs) + if (!elem) { fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno)); return -1; } - struct handler_cfg *const c = &cfgs[h->n_cfg]; + struct elem *const e = &elem[h->n_cfg]; - *c = (const struct handler_cfg) + *e = (const struct elem) { .url = strdup(url), .op = op, @@ -329,13 +341,13 @@ int handler_add(struct handler *const h, const char *url, .user = user }; - if (!c->url) + if (!e->url) { fprintf(stderr, "%s: strdup(3): %s\n", __func__, strerror(errno)); return -1; } - h->cfg = cfgs; + h->elem = elem; h->n_cfg = n; return 0; } @@ -7,7 +7,15 @@ typedef int (*handler_fn)(const struct http_payload *p, struct http_response *r, void *user); -struct handler *handler_alloc(const char *tmpdir); +struct handler_cfg +{ + const char *tmpdir; + int (*length)(unsigned long long len, const struct http_cookie *c, + void *user); + void *user; +}; + +struct handler *handler_alloc(const struct handler_cfg *cfg); void handler_free(struct handler *h); int handler_add(struct handler *h, const char *url, enum http_op op, handler_fn f, void *user); @@ -797,6 +797,18 @@ static int process_header(struct http_ctx *const h, const char *const line, return 0; } +static int check_length(struct http_ctx *const h) +{ + struct ctx *const c = &h->ctx; + const struct http_cookie cookie = + { + .field = c->field, + .value = c->value + }; + + return h->cfg.length(c->post.len, &cookie, h->cfg.user); +} + static int header_cr_line(struct http_ctx *const h) { const char *const line = (const char *)h->line; @@ -810,11 +822,20 @@ static int header_cr_line(struct http_ctx *const h) return payload_get(h, line); case HTTP_OP_POST: + { if (!c->post.len) return payload_post(h, line); + else if (c->boundary) + { + const int res = check_length(h); + + if (res) + return res; + } c->state = BODY_LINE; return 0; + } } } @@ -79,6 +79,8 @@ struct http_cfg int (*write)(const void *buf, size_t n, void *user); int (*payload)(const struct http_payload *p, struct http_response *r, void *user); + int (*length)(unsigned long long len, const struct http_cookie *c, + void *user); const char *tmpdir; void *user; }; |
