ports/devel/got/files/patch-libexec_got-fetch-http_got-fetch-http.c
Christian Weisgerber 2c3377a329 devel/got: got-fetch-http: improve handling of HTTP chunked responses
Cherry-picked from upstream to fix a performance problem when cloning
FreeBSD src.git over HTTP(S).

Reported by:	cperciva
2025-04-17 20:09:52 +00:00

198 lines
4.8 KiB
C

https://got.gameoftrees.org/?action=diff&commit=539ba663896a5eb0dd33d8f4027755974431d528&headref=HEAD&path=got.git
--- libexec/got-fetch-http/got-fetch-http.c.orig 2025-03-21 16:37:10 UTC
+++ libexec/got-fetch-http/got-fetch-http.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2024 Tobias Heider <me@tobhe.de>
- * Copyright (c) 2022 Omar Polo <op@openbsd.org>
+ * Copyright (c) 2022, 2025 Omar Polo <op@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -65,19 +65,6 @@ bufio_getdelim_sync(struct bufio *bio, const char *nl,
return buf_getdelim(&bio->rbuf, nl, len);
}
-static size_t
-bufio_drain_sync(struct bufio *bio, void *d, size_t len)
-{
- int r;
-
- do {
- r = bufio_read(bio);
- if (r == -1 && errno != EAGAIN)
- errx(1, "bufio_read: %s", bufio_io_err(bio));
- } while (r == -1 && errno == EAGAIN);
- return bufio_drain(bio, d, len);
-}
-
static void
bufio_close_sync(struct bufio *bio)
{
@@ -269,53 +256,81 @@ static ssize_t
}
static ssize_t
-http_read(struct bufio *bio, int chunked, size_t *chunksz, char *buf, size_t bufsz)
+http_read(struct bufio *bio, int chunked, size_t *chunksz, char *buf, size_t bufsz,
+ FILE *out)
{
+ struct buf *rbuf = &bio->rbuf;
const char *errstr;
- char *line = NULL;
- size_t r;
- ssize_t ret = 0, linelen;
+ char *chunk, *endln;
+ size_t avail, w;
+ ssize_t r, ret = 0;
- if (!chunked)
- return bufio_drain_sync(bio, buf, bufsz);
+ while (out != NULL || bufsz > 0) {
+ if (rbuf->cur == rbuf->len) {
+ rbuf->cur = 0;
+ rbuf->len = 0;
+ r = bufio_read(bio);
+ if (r == -1) {
+ warnx("bufio_read: %s", bufio_io_err(bio));
+ return (-1);
+ }
+ if (r == 0)
+ return ret;
+ }
- while (bufsz > 0) {
- if (*chunksz == 0) {
- again:
- line = bufio_getdelim_sync(bio, "\r\n", &linelen);
- if (line == NULL) {
- buf_drain(&bio->rbuf, linelen);
+ if (chunked && *chunksz == 0) {
+ for (;;) {
+ chunk = rbuf->buf + rbuf->cur;
+ avail = rbuf->len - rbuf->cur;
+ endln = memmem(chunk, avail, "\r\n", 2);
+ if (endln == NULL) {
+ r = bufio_read(bio);
+ if (r == -1) {
+ warnx("bufio_read: %s",
+ bufio_io_err(bio));
+ return (-1);
+ }
+ if (r == 0)
+ return ret;
+ continue;
+ }
+ rbuf->cur += (endln - chunk) + 2;
+ *endln = '\0';
+ /* was the CRLF after the chunk? */
+ if (chunk == endln)
+ continue;
break;
}
- if (*line == '\0') {
- buf_drain(&bio->rbuf, linelen);
- goto again; /* was the CRLF after the chunk */
- }
- *chunksz = hexstrtonum(line, 0, INT_MAX, &errstr);
+ *chunksz = hexstrtonum(chunk, 0, INT_MAX, &errstr);
if (errstr != NULL) {
warnx("invalid HTTP chunk: size is %s (%s)",
- errstr, line);
- ret = -1;
- break;
+ errstr, chunk);
+ return (-1);
}
- if (*chunksz == 0) {
- buf_drain(&bio->rbuf, linelen);
+ if (*chunksz == 0)
break;
- }
- buf_drain(&bio->rbuf, linelen);
}
- r = bufio_drain_sync(bio, buf, MINIMUM(*chunksz, bufsz));
- if (r == 0) {
- break;
+ avail = rbuf->len - rbuf->cur;
+ if (chunked && avail > *chunksz)
+ avail = *chunksz;
+
+ if (out != NULL) {
+ w = fwrite(rbuf->buf + rbuf->cur, 1, avail, out);
+ if (w != avail)
+ return (-1);
+ } else {
+ avail = MINIMUM(avail, bufsz);
+ memcpy(buf, rbuf->buf + rbuf->cur, avail);
}
- ret += r;
- buf += r;
- bufsz -= r;
- *chunksz -= r;
+ rbuf->cur += avail;
+ ret += avail;
+ buf += avail;
+ bufsz -= avail;
+ *chunksz -= avail;
}
return ret;
@@ -375,7 +390,7 @@ get_refs(int https, const char *host, const char *port
goto err;
/* skip first pack; why git over http is like this? */
- r = http_read(&bio, chunked, &chunksz, buf, 4);
+ r = http_read(&bio, chunked, &chunksz, buf, 4, NULL);
if (r <= 0)
goto err;
@@ -388,23 +403,13 @@ get_refs(int https, const char *host, const char *port
/* TODO: validate it's # service=git-upload-pack\n */
while (skip > 0) {
r = http_read(&bio, chunked, &chunksz, buf,
- MINIMUM(skip, sizeof(buf)));
+ MINIMUM(skip, sizeof(buf)), NULL);
if (r <= 0)
goto err;
skip -= r;
}
- for (;;) {
- r = http_read(&bio, chunked, &chunksz, buf, sizeof(buf));
- if (r == -1)
- goto err;
-
- if (r == 0)
- break;
-
- fwrite(buf, 1, r, stdout);
- }
-
+ http_read(&bio, chunked, &chunksz, NULL, 0, stdout);
fflush(stdout);
ret = 0;
err:
@@ -497,16 +502,7 @@ upload_request(int https, const char *host, const char
goto err;
/* Fetch pack file data from server. */
- for (;;) {
- r = http_read(&bio, chunked, &chunksz, buf, sizeof(buf));
- if (r == -1)
- goto err;
-
- if (r == 0)
- break;
-
- fwrite(buf, 1, r, stdout);
- }
+ http_read(&bio, chunked, &chunksz, NULL, 0, stdout);
ret = 0;
err: