aboutsummaryrefslogtreecommitdiff
path: root/main.c
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi92@disroot.org>2025-09-24 11:01:31 +0200
committerXavier Del Campo Romero <xavi92@disroot.org>2025-09-24 12:39:09 +0200
commit173528aef50a4b452acdd8ec9aff13f25c3e092c (patch)
treef3b79ae0f4eb067b97997b4c91a859157987c3bc /main.c
parentebb825d3c622f74f0c47a84e1e388b709dd06c7d (diff)
downloadslcl-173528aef50a4b452acdd8ec9aff13f25c3e092c.tar.gz
Make search non-blocking
Thanks to a new feature in libweb, it is now possible to generate HTTP responses asynchronously i.e., without blocking other clients if the response takes a long time to generate. This now allow users to search for files or directories without blocking other users, regardless how much time the search operation takes. This required cftw to deviate from the POSIX-like, blocking interface it had so far, and has been replaced now with a non-blocking interface, so that directories are inspected one entry at a time.
Diffstat (limited to 'main.c')
-rw-r--r--main.c114
1 files changed, 77 insertions, 37 deletions
diff --git a/main.c b/main.c
index 3702706..e42236d 100644
--- a/main.c
+++ b/main.c
@@ -46,6 +46,14 @@ struct user_args
struct dynstr d;
int fd;
bool fifo;
+ void *state;
+};
+
+struct search
+{
+ struct cftw *cftw;
+ struct dynstr root, res;
+ struct page_search search;
};
static struct handler *handler;
@@ -647,24 +655,18 @@ static void search_results_free(struct page_search *const s)
}
}
-struct search_args
-{
- const char *root, *res;
- struct page_search *s;
-};
-
static int search_fn(const char *const fpath, const struct stat *const sb,
bool *const done, void *const user)
{
static const size_t limit = 200;
- const struct search_args *const sa = user;
- const char *rel = fpath + strlen(sa->root);
- struct page_search *const res = sa->s;
+ struct search *const s = user;
+ const char *rel = fpath + strlen(s->root.str);
+ struct page_search *const res = &s->search;
struct page_search_result *results = NULL, *r = NULL;
rel += strspn(rel, "/");
- if (wildcard_cmp(rel, sa->res, false))
+ if (wildcard_cmp(rel, s->res.str, false))
return 0;
else if (!(results = realloc(res->results,
(res->n + 1) * sizeof *res->results)))
@@ -689,7 +691,7 @@ static int search_fn(const char *const fpath, const struct stat *const sb,
if (++res->n >= limit)
{
- sa->s->limit_exceeded = true;
+ s->search.limit_exceeded = true;
*done = true;
}
@@ -701,42 +703,62 @@ failure:
return -1;
}
-static int do_search(const char *const abs, const char *const root,
- const char *const res, struct page_search *const s)
+static void free_search(struct search *const s)
{
- struct search_args sa =
- {
- .root = root,
- .res = res,
- .s = s
- };
+ if (!s)
+ return;
+
+ search_results_free(&s->search);
+ dynstr_free(&s->root);
+ dynstr_free(&s->res);
+ cftw_free(s->cftw);
+ free(s);
+}
- s->root = root;
+static int search_step(const struct http_payload *const p,
+ struct http_response *const r, void *const user)
+{
+ const struct user_args *const ua = user;
+ struct search *const s = ua->state;
- if (cftw(abs, search_fn, &sa))
+ switch (cftw_step(s->cftw))
{
- fprintf(stderr, "%s: cftw failed\n", __func__);
- return -1;
+ case CFTW_OK:
+
+ if (page_search(r, &s->search))
+ {
+ fprintf(stderr, "%s: page_search failed\n", __func__);
+ goto failure;
+ }
+
+ free_search(s);
+ break;
+
+ case CFTW_FATAL:
+ goto failure;
+
+ case CFTW_AGAIN:
+ break;
}
return 0;
+
+failure:
+ free_search(s);
+ return -1;
}
static int search(const struct http_payload *const p,
struct http_response *const r, void *const user)
{
int ret = -1;
- const struct user_args *const args = user;
+ struct search *s = NULL;
+ struct user_args *const args = user;
const struct auth *const a = args->a;
const char *const username = p->cookie.field, *const root = auth_dir(a);
int (*f)(struct http_response *);
char *dir = NULL;
struct dynstr userd, d, res;
- struct page_search s =
- {
- .username = username,
- .adir = root
- };
dynstr_init(&userd);
dynstr_init(&d);
@@ -767,25 +789,42 @@ static int search(const struct http_payload *const p,
fprintf(stderr, "%s: dynstr_append d failed\n", __func__);
goto end;
}
- else if ((ret = do_search(d.str, userd.str, res.str, &s)))
+ else if ((!(s = malloc(sizeof *s))))
{
- if (ret < 0)
- fprintf(stderr, "%s: do_search failed\n", __func__);
+ fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno));
+ goto end;
+ }
+
+ *s = (const struct search){0};
+ dynstr_init(&s->root);
+ dynstr_init(&s->res);
+ if (!(s->cftw = cftw(d.str, search_fn, s)))
+ {
+ fprintf(stderr, "%s: cftw failed\n", __func__);
goto end;
}
- else if ((ret = page_search(r, &s)))
+ else if (dynstr_dup(&s->root, &userd)
+ || dynstr_dup(&s->res, &res))
{
- fprintf(stderr, "%s: page_search failed\n", __func__);
+ fprintf(stderr, "%s: dynstr_dup failed\n", __func__);
goto end;
}
+ *r = (const struct http_response){.step = search_step};
+ s->search.root = s->root.str;
+ args->state = s;
+ ret = 0;
+
end:
free(dir);
- dynstr_free(&userd);
dynstr_free(&d);
+ dynstr_free(&userd);
dynstr_free(&res);
- search_results_free(&s);
+
+ if (ret)
+ free_search(s);
+
return ret;
}
@@ -947,7 +986,8 @@ static int check_length(const unsigned long long len,
const struct http_cookie *const c, struct http_response *const r,
void *const user)
{
- struct auth *const a = user;
+ const struct user_args *const ua = user;
+ const struct auth *const a = ua->a;
const char *const username = c->field;
bool has_quota;
unsigned long long quota;