diff options
| author | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2024-01-20 01:05:05 +0100 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2024-01-20 01:19:57 +0100 |
| commit | 43be8d95f681c8240847b4f69a524a0ab20d5c83 (patch) | |
| tree | a4e45ad7784219fe5a43a2feae572827735a619e /http.c | |
| parent | b0accd099fa8c5110d4c3c68830ad6fd810ca3ec (diff) | |
http.c: Solve performance issues on POST uploads
Profiling showed that reading multipart/form POST uploads byte-by-byte
was too slow and typically led to maximum CPU usage. Therefore, the
older approach (as done up to commit 7efc2b3a) was more efficient, even
if the resulting code was a bit uglier.
Diffstat (limited to 'http.c')
| -rw-r--r-- | http.c | 138 |
1 files changed, 91 insertions, 47 deletions
@@ -1313,10 +1313,12 @@ static int send_payload(struct http_ctx *const h, } static int update_lstate(struct http_ctx *const h, bool *const close, - int (*const f)(struct http_ctx *), const char b) + int (*const f)(struct http_ctx *), const char *const buf, + size_t *const sz) { int ret = 1; struct ctx *const c = &h->ctx; + const char b = *buf; switch (c->lstate) { @@ -1358,6 +1360,7 @@ static int update_lstate(struct http_ctx *const h, bool *const close, break; } + (*sz)--; return 0; failure: @@ -1642,24 +1645,27 @@ end: return ret; } -static int read_mf_body_to_mem(struct http_ctx *const h, const char b) +static int read_mf_body_to_mem(struct http_ctx *const h, + const char *const buf, const size_t n) { struct ctx *const c = &h->ctx; struct multiform *const m = &c->u.mf; - if (m->written + 1 > sizeof h->line - 1) + if (m->written + n > sizeof h->line - 1) { fprintf(stderr, "%s: maximum length exceeded\n", __func__); return 1; } - h->line[m->written++] = b; - m->len++; - c->payload.read++; + memcpy(h->line, buf, n); + m->written += n; + m->len += n; + c->payload.read += n; return 0; } -static int read_mf_body_to_file(struct http_ctx *const h, const char b) +static int read_mf_body_to_file(struct http_ctx *const h, + const char *const buf, const size_t n) { struct ctx *const c = &h->ctx; struct multiform *const m = &c->u.mf; @@ -1669,16 +1675,16 @@ static int read_mf_body_to_file(struct http_ctx *const h, const char b) fprintf(stderr, "%s: generate_mf_file failed\n", __func__); return -1; } - else if (!fwrite(&b, sizeof b, 1, m->f)) + else if (n && !fwrite(buf, n, 1, m->f)) { fprintf(stderr, "%s: fwrite(3) failed, feof=%d, ferror=%d\n", __func__, feof(m->f), ferror(m->f)); return -1; } - m->written++; - m->len++; - c->payload.read++; + m->written += n; + m->len += n; + c->payload.read += n; return 0; } @@ -1686,31 +1692,28 @@ static int reset_boundary(struct http_ctx *const h) { struct multiform *const m = &h->ctx.u.mf; struct form *const f = &m->forms[m->nforms - 1]; - int (*const read_mf)(struct http_ctx *, char) = + int (*const read_mf)(struct http_ctx *, const char *, size_t) = f->filename ? read_mf_body_to_file : read_mf_body_to_mem; const size_t len = strlen(m->boundary); + const int res = read_mf(h, m->boundary, len); - for (size_t i = 0; i < len; i++) - { - const int res = read_mf(h, m->boundary[i]); - - if (res) - return res; - } + if (res) + return res; memset(m->boundary, '\0', len); m->blen = 0; return 0; } -static int dump_body(struct http_ctx *const h, const char b) +static int dump_body(struct http_ctx *const h, const char *const buf, + const size_t n) { struct multiform *const m = &h->ctx.u.mf; struct form *const f = &m->forms[m->nforms - 1]; - int (*const read_mf)(struct http_ctx *, char) = + int (*const read_mf)(struct http_ctx *, const char *, size_t) = f->filename ? read_mf_body_to_file : read_mf_body_to_mem; - return read_mf(h, b); + return read_mf(h, buf, n); } static int apply_from_file(struct http_ctx *const h, struct form *const f) @@ -1798,7 +1801,8 @@ static int apply_from_mem(struct http_ctx *const h, struct form *const f) return 0; } -static int read_mf_body_boundary_byte(struct http_ctx *const h, const char b) +static int read_mf_body_boundary_byte(struct http_ctx *const h, const char b, + const size_t len) { struct ctx *const c = &h->ctx; struct multiform *const m = &c->u.mf; @@ -1865,26 +1869,58 @@ static const char *http_memmem(const char *const a, const void *const b, return st; } -static int read_mf_body_boundary(struct http_ctx *const h, const char b) +static int read_mf_body_boundary(struct http_ctx *const h, + const char *buf, size_t *const n) { struct ctx *const c = &h->ctx; struct multiform *const m = &c->u.mf; int res; - if (b != c->boundary[m->blen]) + if (m->blen + && *buf != c->boundary[m->blen] + && (res = reset_boundary(h))) + return res; + + const char *const boundary = http_memmem(&c->boundary[m->blen], buf, *n); + + if (!boundary) { if ((res = reset_boundary(h)) - || (res = dump_body(h, b))) + || (res = dump_body(h, buf, *n))) return res; + *n = 0; return 0; } - return read_mf_body_boundary_byte(h, b); + const size_t prev = boundary - buf; + + if (prev && (res = reset_boundary(h))) + return res; + else if ((res = dump_body(h, buf, prev))) + return res; + + buf += prev; + *n -= prev; + + const size_t len = strlen(m->boundary), + rem = strlen(c->boundary) - len, + r = rem > *n ? *n : rem; + + for (size_t i = 0; i < r; i++) + { + const char *const b = buf; + + if ((res = read_mf_body_boundary_byte(h, b[i], len))) + return res; + } + + *n -= r; + return 0; } static int read_multiform(struct http_ctx *const h, bool *const close, - const char b) + const char *const buf, size_t *const sz) { struct multiform *const m = &h->ctx.u.mf; int res; @@ -1896,26 +1932,27 @@ static int read_multiform(struct http_ctx *const h, bool *const close, case MF_HEADER_CR_LINE: /* Fall through. */ case MF_END_BOUNDARY_CR_LINE: - { - if ((res = update_lstate(h, close, process_mf_line, b))) + if ((res = update_lstate(h, close, process_mf_line, buf, sz))) return res; - } break; case MF_BODY_BOUNDARY_LINE: - if ((res = read_mf_body_boundary(h, b))) + if ((res = read_mf_body_boundary(h, buf, sz))) return res; + + break; } return 0; } static int read_body_to_mem(struct http_ctx *const h, bool *const close, - const char b) + const char *const buf, size_t *const sz) { struct ctx *const c = &h->ctx; struct payload *const p = &c->payload; + const char b = *buf; if (p->read >= sizeof h->line - 1) { @@ -1924,6 +1961,7 @@ static int read_body_to_mem(struct http_ctx *const h, bool *const close, } h->line[p->read++] = b; + (*sz)--; if (p->read >= p->len) { @@ -2006,7 +2044,8 @@ end: return ret; } -static int read_put_body_to_file(struct http_ctx *const h, const char b) +static int read_put_body_to_file(struct http_ctx *const h, + const void *const buf, size_t *const sz) { struct ctx *const c = &h->ctx; struct put *const put = &c->u.put; @@ -2021,24 +2060,25 @@ static int read_put_body_to_file(struct http_ctx *const h, const char b) fprintf(stderr, "%s: ensure_put_file failed\n", __func__); return -1; } - else if (!fwrite(&b, sizeof b, 1, put->f)) + else if (!fwrite(buf, *sz, 1, put->f)) { fprintf(stderr, "%s: fwrite(3) failed, feof=%d ferror=%d\n", __func__, feof(put->f), ferror(put->f)); return -1; } - c->payload.read++; + c->payload.read += *sz; + *sz = 0; return 0; } static int read_to_file(struct http_ctx *const h, bool *const close, - const char b) + const char *const buf, size_t *const sz) { struct ctx *const c = &h->ctx; struct payload *const p = &c->payload; - if (read_put_body_to_file(h, b)) + if (read_put_body_to_file(h, buf, sz)) return -1; else if (p->read >= p->len) { @@ -2064,18 +2104,19 @@ static int read_to_file(struct http_ctx *const h, bool *const close, return 0; } -static int read_body(struct http_ctx *const h, bool *const close, const char b) +static int read_body(struct http_ctx *const h, bool *const close, + const char *const buf, size_t *const sz) { const struct ctx *const c = &h->ctx; switch (c->op) { case HTTP_OP_POST: - return c->boundary ? read_multiform(h, close, b) - : read_body_to_mem(h, close, b); + return c->boundary ? read_multiform(h, close, buf, sz) + : read_body_to_mem(h, close, buf, sz); case HTTP_OP_PUT: - return read_to_file(h, close, b); + return read_to_file(h, close, buf, sz); case HTTP_OP_GET: /* Fall through. */ @@ -2098,17 +2139,18 @@ static int process_line(struct http_ctx *const h) return state[h->ctx.state](h); } -static int read_byte(struct http_ctx *const h, bool *const close, const char b) +static int read_buf(struct http_ctx *const h, bool *const close, + const char *const buf, size_t *const sz) { switch (h->ctx.state) { case START_LINE: /* Fall through. */ case HEADER_CR_LINE: - return update_lstate(h, close, process_line, b); + return update_lstate(h, close, process_line, buf, sz); case BODY_LINE: - return read_body(h, close, b); + return read_body(h, close, buf, sz); } fprintf(stderr, "%s: unexpected state %d\n", __func__, h->ctx.state); @@ -2123,9 +2165,11 @@ int http_read(struct http_ctx *const h, bool *const close) if (r <= 0) return rw_error(r, close); - for (int i = 0; i < r; i++) + size_t rem = r; + + while (rem) { - const int ret = read_byte(h, close, buf[i]); + const int ret = read_buf(h, close, &buf[r - rem], &rem); if (ret) return ret; |
