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 /http.c | |
| parent | db9cb051c4ee05c07ab32dfed5bae8b7dc0916bd (diff) | |
| download | libweb-chunk.tar.gz | |
WIP chunk encodingchunk
Diffstat (limited to 'http.c')
| -rw-r--r-- | http.c | 181 |
1 files changed, 177 insertions, 4 deletions
@@ -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; } |
