diff options
| author | Xavier Del Campo Romero <xavi92@disroot.org> | 2025-10-06 23:01:42 +0200 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi92@disroot.org> | 2025-10-07 12:43:25 +0200 |
| commit | b9d9328956c656f0e9a007b8810cadafbe531c48 (patch) | |
| tree | 028ad88fd37551fcd6ad0bf29a3ce9af745e687c | |
| parent | db9cb051c4ee05c07ab32dfed5bae8b7dc0916bd (diff) | |
WIP chunk encodingchunk
| -rw-r--r-- | examples/async/main.c | 6 | ||||
| -rw-r--r-- | handler.c | 10 | ||||
| -rw-r--r-- | http.c | 181 | ||||
| -rw-r--r-- | include/libweb/http.h | 8 |
4 files changed, 190 insertions, 15 deletions
diff --git a/examples/async/main.c b/examples/async/main.c index dbf69a8..fbd766e 100644 --- a/examples/async/main.c +++ b/examples/async/main.c @@ -86,9 +86,9 @@ end: } static int step(const struct http_payload *const pl, - struct http_response *const r, void *const user, void *step_args) + struct http_response *const r, void *const user, void *args) { - unsigned *const cnt = step_args; + unsigned *const cnt = args; const unsigned max = 10; fprintf(stderr, "%s: step %u\n", __func__, *cnt); @@ -113,7 +113,7 @@ static int hello(const struct http_payload *const pl, *r = (const struct http_response) { .step.payload = step, - .step_args = cnt + .args = cnt }; *cnt = 0; @@ -29,7 +29,7 @@ struct handler struct server_client *c; struct http_ctx *http; union http_step step; - void *step_args; + void *args; struct client *next; } *clients; @@ -66,14 +66,14 @@ static int on_payload(const struct http_payload *const p, int ret; if (s->payload) - ret = s->payload(p, r, e->user, c->step_args); + ret = s->payload(p, r, e->user, c->args); else ret = e->f(p, r, e->user); if (!ret) { s->payload = r->step.payload; - c->step_args = r->step_args; + c->args = r->args; } return ret; @@ -104,14 +104,14 @@ static int on_length(const unsigned long long len, int ret; if (s->length) - ret = s->length(len, c, r, h->cfg.user, cl->step_args); + ret = s->length(len, c, r, h->cfg.user, cl->args); else ret = h->cfg.length(len, c, r, h->cfg.user); if (!ret) { s->length = r->step.length; - cl->step_args = r->step_args; + cl->args = r->args; } return ret; @@ -15,6 +15,7 @@ #include <time.h> #define HTTP_VERSION "HTTP/1.1" +#define MAX_CHUNKSZ 1024 struct http_ctx { @@ -109,13 +110,16 @@ struct http_ctx struct write_ctx { - bool pending, close; + bool pending, close, chunk_done; enum state state; struct http_response r; struct http_get_range gr; off_t n; struct dynstr d; enum http_op op; + void *buf; + int (*next)(struct http_ctx *, bool *close); + size_t written, chunklen; } wctx; /* From RFC9112, section 3 (Request line): @@ -125,6 +129,8 @@ struct http_ctx struct http_cfg cfg; }; +static int setup_chunk(struct http_ctx *, bool *); + static int adjust_partial(struct write_ctx *const w) { int ret = -1; @@ -226,6 +232,14 @@ static int send_length(struct write_ctx *const w) return res; } } + else if (r->chunk) + { + if (http_response_add_header(r, "Tansfer-Encoding", "chunked")) + { + fprintf(stderr, "%s: http_response_add_header failed\n", __func__); + return -1; + } + } else { char len[sizeof "18446744073709551615"]; @@ -782,6 +796,7 @@ static int write_ctx_free(struct write_ctx *const w) if (r->f && (ret = fclose(r->f))) fprintf(stderr, "%s: fclose(3): %s\n", __func__, strerror(errno)); + free(w->buf); dynstr_free(&w->d); free_response_headers(&w->r); *w = (const struct write_ctx){0}; @@ -808,7 +823,7 @@ static int write_header_cr_line(struct http_ctx *const h, bool *const close) dynstr_free(d); - if (w->r.n && must_write_body(w)) + if (w->r.chunk || (w->r.n && must_write_body(w))) { w->state = BODY_LINE; w->n = 0; @@ -825,6 +840,162 @@ static int write_header_cr_line(struct http_ctx *const h, bool *const close) return 0; } +static int write_lastchunk(struct http_ctx *const h, bool *const close) +{ + static const char end[] = "0\r\n"; + struct write_ctx *const w = &h->wctx; + const struct http_cfg *const cfg = &h->cfg; + const void *const src = end + w->written; + const int wr = cfg->write(src, strlen(end) - w->written, cfg->user); + + if (wr < 0) + { + fprintf(stderr, "%s: write failed\n", __func__); + return -1; + } + else if ((w->written += wr) >= strlen(end)) + { + w->next = setup_chunk; + } + + return 0; +} + +static int write_chunkend(struct http_ctx *const h, bool *const close) +{ + static const char end[] = "\r\n"; + struct write_ctx *const w = &h->wctx; + const struct http_cfg *const cfg = &h->cfg; + const void *const src = end + w->written; + const int wr = cfg->write(src, strlen(end) - w->written, cfg->user); + + if (wr < 0) + { + fprintf(stderr, "%s: write failed\n", __func__); + return -1; + } + else if ((w->written += wr) >= strlen(end)) + { + if (w->chunk_done) + { + w->written = 0; + w->next = write_lastchunk; + } + else + w->next = setup_chunk; + } + + return 0; +} + +static int write_chunkdata(struct http_ctx *const h, bool *const close) +{ + struct write_ctx *const w = &h->wctx; + const struct http_cfg *const cfg = &h->cfg; + const void *const src = (const char *)w->buf + w->written; + const int wr = cfg->write(src, w->chunklen - w->written, cfg->user); + + if (wr < 0) + { + fprintf(stderr, "%s: write failed\n", __func__); + return -1; + } + else if ((w->written += wr) >= w->chunklen) + { + free(w->buf); + w->buf = NULL; + w->written = 0; + w->next = write_chunkend; + } + + return 0; +} + +static int write_chunklen(struct http_ctx *const h, bool *const close) +{ + struct write_ctx *const w = &h->wctx; + struct dynstr *const d = &w->d; + const void *const src = d->str + w->written; + const struct http_cfg *const cfg = &h->cfg; + const int wr = cfg->write(src, w->chunklen - w->written, cfg->user); + + if (wr < 0) + { + fprintf(stderr, "%s: write failed\n", __func__); + return -1; + } + else if ((w->written += wr) >= w->n) + { + dynstr_free(d); + w->written = 0; + w->next = write_chunkdata; + } + + return 0; +} + +static int read_chunk(struct http_ctx *const h, bool *const close) +{ + bool done = false; + struct write_ctx *const w = &h->wctx; + struct dynstr *const d = &w->d; + const struct http_response *const r = &w->r; + const int n = r->chunk(w->buf, MAX_CHUNKSZ, &done, h->cfg.user, r->args); + + if (n < 0) + { + fprintf(stderr, "%s: user callback failed\n", __func__); + return -1; + } + else if (!n) + return 0; + + dynstr_init(d); + + if (dynstr_append(d, "%x\r\n", n)) + { + fprintf(stderr, "%s: dynstr_append failed\n", __func__); + return -1; + } + + w->n = d->len; + w->written = 0; + w->chunklen = n; + w->chunk_done = done; + w->next = write_chunklen; + return 0; +} + +static int setup_chunk(struct http_ctx *const h, bool *const close) +{ + struct write_ctx *const w = &h->wctx; + void *const buf = malloc(MAX_CHUNKSZ); + + if (!buf) + { + fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno)); + goto failure; + } + + w->buf = buf; + w->next = read_chunk; + return 0; + +failure: + free(buf); + return -1; +} + +static int write_chunk(struct http_ctx *const h, bool *const close) +{ + struct write_ctx *const w = &h->wctx; + + if (!w->next) + w->next = setup_chunk; + + return w->next(h, close); +} + static int write_body_mem(struct http_ctx *const h, bool *const close) { struct write_ctx *const w = &h->wctx; @@ -892,12 +1063,14 @@ static int write_body_line(struct http_ctx *const h, bool *const close) { const struct http_response *const r = &h->wctx.r; - if (r->buf.ro) + if (r->chunk) + return write_chunk(h, close); + else if (r->buf.ro) return write_body_mem(h, close); else if (r->f) return write_body_file(h, close); - fprintf(stderr, "%s: expected either buffer or file path\n", __func__); + fprintf(stderr, "%s: expected chunk, buffer or file path\n", __func__); return -1; } diff --git a/include/libweb/http.h b/include/libweb/http.h index 87edf8a..c5dff9d 100644 --- a/include/libweb/http.h +++ b/include/libweb/http.h @@ -109,15 +109,17 @@ struct http_response unsigned long long n; size_t n_headers; void (*free)(void *); - void *step_args; + void *args; union http_step { int (*length)(unsigned long long len, const struct http_cookie *c, - struct http_response *r, void *user, void *step_args); + struct http_response *r, void *user, void *args); int (*payload)(const struct http_payload *p, struct http_response *r, - void *user, void *step_args); + void *user, void *args); } step; + + int (*chunk)(void *buf, size_t n, bool *done, void *user, void *args); }; struct http_cfg |
