diff options
| author | Xavier Del Campo Romero <xavi92@disroot.org> | 2025-09-23 22:03:57 +0200 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi92@disroot.org> | 2025-09-24 12:33:35 +0200 |
| commit | 5a6f30440b66fe6713acb9d979dc3e6624e4c36a (patch) | |
| tree | 9a4410f7f7ee7a37f4b28fc8963b3667c3a0a900 | |
| parent | f7864cb7d49a8ca5bddf8d1f68b71ecd5ed85adc (diff) | |
Implement async HTTP responses
Sometimes, library users cannot return a HTTP response as soon as the
request is received, or the operations that are required to generate it
can take a long time.
In order to solve this, libweb adds a new member to struct
http_response, namely step, which must be assigned to a function
whenever a HTTP response should be generated in a non-blocking manner.
Leaving the function pointer as null will fall back to the default
behaviour.
| -rw-r--r-- | doc/man7/libweb_http.7 | 19 | ||||
| -rw-r--r-- | handler.c | 15 | ||||
| -rw-r--r-- | http.c | 149 | ||||
| -rw-r--r-- | include/libweb/http.h | 1 |
4 files changed, 140 insertions, 44 deletions
diff --git a/doc/man7/libweb_http.7 b/doc/man7/libweb_http.7 index f11c1ef..c3bec8b 100644 --- a/doc/man7/libweb_http.7 +++ b/doc/man7/libweb_http.7 @@ -562,6 +562,7 @@ struct http_response unsigned long long \fIn\fP; size_t \fIn_headers\fP; void (*\fIfree\fP)(void *); + int (*\fIstep\fP)(const struct http_payload *, struct http_response *, void *); }; .EE .in @@ -669,6 +670,24 @@ is a valid pointer. Otherwise, .I free must be a null pointer. +.I step +allows implementations to generate a response in a non-blocking manner. +When a response is not immediately available when a payload is received, +.I step +must be assigned to a function that can generate it later. +.I libweb +shall then call this function immediately later, +without blocking other clients. +Assigning +.I step +to a null pointer falls back to the default behaviour +i.e., a response is returned immediately. +Note that a non-null +.I step +shall always take priority, thus ignoring any other information inside the +.I "struct http_response" +instance. + .SS Transport Layer Security (TLS) By design, .I libweb @@ -28,6 +28,7 @@ struct handler struct handler *h; struct server_client *c; struct http_ctx *http; + int (*fn)(const struct http_payload *, struct http_response *, void *); struct client *next; } *clients; @@ -59,7 +60,19 @@ static int on_payload(const struct http_payload *const p, 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); + { + int ret; + + if (c->fn) + ret = c->fn(p, r, e->user); + else + ret = e->f(p, r, e->user); + + if (!ret) + c->fn = r->step; + + return ret; + } } fprintf(stderr, "Not found: %s\n", p->resource); @@ -102,6 +102,7 @@ struct http_ctx size_t n_args, n_headers; struct http_header *headers; bool has_length, expect_continue; + int (*response)(struct http_ctx *); } ctx; struct write_ctx @@ -1211,15 +1212,22 @@ static int process_payload(struct http_ctx *const h) { struct ctx *const c = &h->ctx; const struct http_payload p = ctx_to_payload(c); + struct http_response *const r = &h->wctx.r; const int ret = h->cfg.payload(&p, &h->wctx.r, h->cfg.user); - h->wctx.op = c->op; - ctx_free(c); - if (ret) + { + ctx_free(c); return ret; + } + else if (!r->step) + { + h->wctx.op = c->op; + ctx_free(c); + return start_response(h); + } - return start_response(h); + return 0; } static int get_field_value(const char *const line, size_t *const n, @@ -1470,6 +1478,7 @@ static int check_length(struct http_ctx *const h) static int process_expect(struct http_ctx *const h) { struct ctx *const c = &h->ctx; + struct write_ctx *const w = &h->wctx; const int res = check_length(h); if (res) @@ -1480,7 +1489,7 @@ static int process_expect(struct http_ctx *const h) return res; } - h->wctx.close = true; + w->close = true; return start_response(h); } @@ -1488,36 +1497,51 @@ static int process_expect(struct http_ctx *const h) p.expect_continue = true; - const int ret = h->cfg.payload(&p, &h->wctx.r, h->cfg.user); - - h->wctx.op = c->op; + struct http_response *const r = &h->wctx.r; + const int ret = h->cfg.payload(&p, r, h->cfg.user); if (ret) + { + ctx_free(c); return ret; + } + else if (!r->step) + { + w->op = c->op; + c->state = BODY_LINE; + return start_response(h); + } - c->state = BODY_LINE; - return start_response(h); + return 0; } static int process_get(struct http_ctx *const h) { + int ret; struct ctx *const c = &h->ctx; struct http_payload p = ctx_to_payload(c); struct write_ctx *const w = &h->wctx; + struct http_response *const r = &w->r; p.u.get.range = w->gr = c->u2.get.range; - const int ret = h->cfg.payload(&p, &h->wctx.r, h->cfg.user); + if ((ret = h->cfg.payload(&p, &h->wctx.r, h->cfg.user))) + { + ctx_free(c); + return ret; + } + else if (!r->step) + { + w->op = c->op; + ctx_free(c); - w->op = c->op; - ctx_free(c); + if (w->gr.state) + w->r.status = HTTP_STATUS_PARTIAL_CONTENT; - if (ret) - return ret; - else if (w->gr.state) - w->r.status = HTTP_STATUS_PARTIAL_CONTENT; + return start_response(h); + } - return start_response(h); + return 0; } static int header_cr_line(struct http_ctx *const h) @@ -1530,17 +1554,25 @@ static int header_cr_line(struct http_ctx *const h) switch (c->op) { case HTTP_OP_GET: - return process_get(h); + c->response = process_get; + return 0; case HTTP_OP_HEAD: - return process_payload(h); + c->response = process_payload; + return 0; case HTTP_OP_POST: { if (!c->u2.payload.len) - return process_payload(h); + { + c->response = process_payload; + return 0; + } else if (c->expect_continue) - return process_expect(h); + { + c->response = process_expect; + return 0; + } else if (c->boundary) { const int res = check_length(h); @@ -1571,9 +1603,15 @@ static int header_cr_line(struct http_ctx *const h) return 1; } else if (!c->u2.payload.len) - return process_payload(h); + { + c->response = process_payload; + return 0; + } else if (c->expect_continue) - return process_expect(h); + { + c->response = process_expect; + return 0; + } c->state = BODY_LINE; return 0; @@ -1594,14 +1632,21 @@ static int send_payload(struct http_ctx *const h, const struct http_payload *const p) { struct ctx *const c = &h->ctx; - const int ret = h->cfg.payload(p, &h->wctx.r, h->cfg.user); - - ctx_free(c); + struct http_response *const r = &h->wctx.r; + const int ret = h->cfg.payload(p, r, h->cfg.user); if (ret) + { + ctx_free(c); return ret; + } + else if (!r->step) + { + ctx_free(c); + return start_response(h); + } - return start_response(h); + return 0; } static int update_lstate(struct http_ctx *const h, @@ -2216,7 +2261,7 @@ static int read_mf_end_boundary(struct http_ctx *const h, /* Found end boundary. */ if (!*n) - return end_boundary(h); + c->response = end_boundary; else m->state = MF_END_BOUNDARY_CR_LINE; } @@ -2243,7 +2288,8 @@ static int process_mf_end_crlf(struct http_ctx *const h) return 1; } - return end_boundary(h); + h->ctx.response = end_boundary; + return 0; } static int read_multiform(struct http_ctx *const h, const char *const buf, @@ -2284,6 +2330,15 @@ static int read_multiform(struct http_ctx *const h, const char *const buf, return 0; } +static int send_line_payload(struct http_ctx *const h) +{ + struct ctx *const c = &h->ctx; + struct http_payload pl = ctx_to_payload(c); + + pl.u.post.data = h->line; + return send_payload(h, &pl); +} + static int read_body_to_mem(struct http_ctx *const h, const char *const buf, size_t *const sz) { @@ -2302,11 +2357,8 @@ static int read_body_to_mem(struct http_ctx *const h, const char *const buf, if (p->read >= p->len) { - struct http_payload pl = ctx_to_payload(c); - h->line[p->len] = '\0'; - pl.u.post.data = h->line; - return send_payload(h, &pl); + c->response = send_line_payload; } return 0; @@ -2396,6 +2448,15 @@ static int read_put_body_to_file(struct http_ctx *const h, return 0; } +static int send_put_payload(struct http_ctx *const h) +{ + struct ctx *const c = &h->ctx; + struct http_payload pl = ctx_to_payload(c); + + pl.u.put.tmpname = c->u.put.tmpname; + return send_payload(h, &pl); +} + static int read_to_file(struct http_ctx *const h, const char *const buf, size_t *const sz) { @@ -2405,12 +2466,7 @@ static int read_to_file(struct http_ctx *const h, const char *const buf, if (read_put_body_to_file(h, buf, sz)) return -1; else if (p->read >= p->len) - { - struct http_payload pl = ctx_to_payload(c); - - pl.u.put.tmpname = c->u.put.tmpname; - return send_payload(h, &pl); - } + c->response = send_put_payload; return 0; } @@ -2547,12 +2603,19 @@ failure: int http_update(struct http_ctx *const h, bool *const write, bool *const close) { + int ret; + struct ctx *const c = &h->ctx; + struct write_ctx *const w = &h->wctx; + struct http_response *const r = &w->r; + *close = false; - struct write_ctx *const w = &h->wctx; - const int ret = w->pending ? http_write(h, close) : http_read(h, close); + if (c->response) + ret = c->response(h); + else + ret = w->pending ? http_write(h, close) : http_read(h, close); - *write = w->pending; + *write = c->response || w->pending; return ret; } diff --git a/include/libweb/http.h b/include/libweb/http.h index 736a561..f18b5c4 100644 --- a/include/libweb/http.h +++ b/include/libweb/http.h @@ -109,6 +109,7 @@ struct http_response unsigned long long n; size_t n_headers; void (*free)(void *); + int (*step)(const struct http_payload *, struct http_response *, void *); }; struct http_cfg |
