aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi.dcr@tutanota.com>2023-03-06 05:09:56 +0100
committerXavier Del Campo Romero <xavi.dcr@tutanota.com>2023-07-20 23:52:52 +0200
commit7b810b55ab058b71dc11ac9ebb86f2de7c3623da (patch)
tree5c822be4b1f2f44e2dfe08687f981479911c560d
parentf5dfbf6e8be018199c4d57375935fd1f5f3c93bb (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--Makefile3
-rw-r--r--handler.c48
-rw-r--r--handler.h10
-rw-r--r--http.c21
-rw-r--r--http.h2
5 files changed, 64 insertions, 20 deletions
diff --git a/Makefile b/Makefile
index e29c8ac..fc00b3a 100644
--- a/Makefile
+++ b/Makefile
@@ -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)
diff --git a/handler.c b/handler.c
index 6a3d626..05650ca 100644
--- a/handler.c
+++ b/handler.c
@@ -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;
}
diff --git a/handler.h b/handler.h
index 69f8cbf..d851f65 100644
--- a/handler.h
+++ b/handler.h
@@ -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);
diff --git a/http.c b/http.c
index 5c4626b..a06f29d 100644
--- a/http.c
+++ b/http.c
@@ -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;
+ }
}
}
diff --git a/http.h b/http.h
index 3390e49..33f42c6 100644
--- a/http.h
+++ b/http.h
@@ -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;
};