aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/async/main.c6
-rw-r--r--handler.c10
-rw-r--r--http.c181
-rw-r--r--include/libweb/http.h8
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;
diff --git a/handler.c b/handler.c
index 63ef726..f30cfc3 100644
--- a/handler.c
+++ b/handler.c
@@ -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;
diff --git a/http.c b/http.c
index c173c3d..1ae233b 100644
--- a/http.c
+++ b/http.c
@@ -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