git: 2c3377a3292d - main - devel/got: got-fetch-http: improve handling of HTTP chunked responses

From: Christian Weisgerber <naddy_at_FreeBSD.org>
Date: Thu, 17 Apr 2025 20:11:43 UTC
The branch main has been updated by naddy:

URL: https://cgit.FreeBSD.org/ports/commit/?id=2c3377a3292dcc94d8dd620faf037fe65044ecd9

commit 2c3377a3292dcc94d8dd620faf037fe65044ecd9
Author:     Christian Weisgerber <naddy@FreeBSD.org>
AuthorDate: 2025-04-17 20:09:52 +0000
Commit:     Christian Weisgerber <naddy@FreeBSD.org>
CommitDate: 2025-04-17 20:09:52 +0000

    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
---
 devel/got/Makefile                                 |   1 +
 .../patch-libexec_got-fetch-http_got-fetch-http.c  | 198 +++++++++++++++++++++
 2 files changed, 199 insertions(+)

diff --git a/devel/got/Makefile b/devel/got/Makefile
index 8b3e54514769..53147cb60b24 100644
--- a/devel/got/Makefile
+++ b/devel/got/Makefile
@@ -1,5 +1,6 @@
 PORTNAME=	got
 DISTVERSION=	0.110
+PORTREVISION=	1
 CATEGORIES=	devel
 MASTER_SITES=	https://gameoftrees.org/releases/portable/
 DISTNAME=	got-portable-${DISTVERSION}
diff --git a/devel/got/files/patch-libexec_got-fetch-http_got-fetch-http.c b/devel/got/files/patch-libexec_got-fetch-http_got-fetch-http.c
new file mode 100644
index 000000000000..ed132de57475
--- /dev/null
+++ b/devel/got/files/patch-libexec_got-fetch-http_got-fetch-http.c
@@ -0,0 +1,198 @@
+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: