curl: 8.12.1 -> 8.13.0 (#396200)

This commit is contained in:
Sefa Eyeoglu 2025-04-27 20:32:39 +02:00 committed by GitHub
commit 262d9972ef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 177 additions and 2 deletions

View file

@ -0,0 +1,13 @@
diff --git a/Source/cmCurl.cxx b/Source/cmCurl.cxx
index b9133ed7d47b9023de66a94ed5ff15a0f1ba440c..0cf8a71a72daaa07cb42b3f5eef81d2d04300cd6 100644
--- a/Source/cmCurl.cxx
+++ b/Source/cmCurl.cxx
@@ -170,7 +170,7 @@ std::string cmCurlSetNETRCOption(::CURL* curl, const std::string& netrc_level,
const std::string& netrc_file)
{
std::string e;
- CURL_NETRC_OPTION curl_netrc_level = CURL_NETRC_LAST;
+ long curl_netrc_level = CURL_NETRC_LAST;
::CURLcode res;
if (!netrc_level.empty()) {

View file

@ -76,6 +76,8 @@ stdenv.mkDerivation (finalAttrs: {
# Backport of https://gitlab.kitware.com/cmake/cmake/-/merge_requests/9900
# Needed to correctly link curl in pkgsStatic.
./008-FindCURL-Add-more-target-properties-from-pkg-config.diff
# Backport of https://gitlab.kitware.com/cmake/cmake/-/commit/1b0c92a3a1b782ff3e1c4499b6ab8db614d45bcd
./009-cmCurl-Avoid-using-undocumented-type-for-CURLOPT_NETRC-values.diff
];
outputs =

View file

@ -0,0 +1,155 @@
From 5fbd78eb2dc4afbd8884e8eed27147fc3d4318f6 Mon Sep 17 00:00:00 2001
From: Stefan Eissing <stefan@eissing.org>
Date: Fri, 4 Apr 2025 10:43:13 +0200
Subject: [PATCH] http2: fix stream window size after unpausing
When pausing a HTTP/2 transfer, the stream's local window size
is reduced to 0 to prevent the server from sending further data
which curl cannot write out to the application.
When unpausing again, the stream's window size was not correctly
increased again. The attempt to trigger a window update was
ignored by nghttp2, the server never received it and the transfer
stalled.
Add a debug feature to allow use of small window sizes which
reproduces this bug in test_02_21.
Fixes #16955
Closes #16960
---
docs/libcurl/libcurl-env-dbg.md | 5 +++++
lib/http2.c | 31 +++++++++++++++++++++++++++++++
tests/http/test_02_download.py | 27 +++++++++++++++++++++++++--
3 files changed, 61 insertions(+), 2 deletions(-)
diff --git a/docs/libcurl/libcurl-env-dbg.md b/docs/libcurl/libcurl-env-dbg.md
index 471533625f6b..60c887bfd5a9 100644
--- a/docs/libcurl/libcurl-env-dbg.md
+++ b/docs/libcurl/libcurl-env-dbg.md
@@ -147,3 +147,8 @@ Make a blocking, graceful shutdown of all remaining connections when
a multi handle is destroyed. This implicitly triggers for easy handles
that are run via easy_perform. The value of the environment variable
gives the shutdown timeout in milliseconds.
+
+## `CURL_H2_STREAM_WIN_MAX`
+
+Set to a positive 32-bit number to override the HTTP/2 stream window's
+default of 10MB. Used in testing to verify correct window update handling.
diff --git a/lib/http2.c b/lib/http2.c
index 88fbcceb7135..a1221dcc51de 100644
--- a/lib/http2.c
+++ b/lib/http2.c
@@ -44,6 +44,7 @@
#include "connect.h"
#include "rand.h"
#include "strdup.h"
+#include "strparse.h"
#include "transfer.h"
#include "dynbuf.h"
#include "headers.h"
@@ -141,6 +142,9 @@ struct cf_h2_ctx {
uint32_t goaway_error; /* goaway error code from server */
int32_t remote_max_sid; /* max id processed by server */
int32_t local_max_sid; /* max id processed by us */
+#ifdef DEBUGBUILD
+ int32_t stream_win_max; /* max h2 stream window size */
+#endif
BIT(initialized);
BIT(via_h1_upgrade);
BIT(conn_closed);
@@ -166,6 +170,18 @@ static void cf_h2_ctx_init(struct cf_h2_ctx *ctx, bool via_h1_upgrade)
Curl_hash_offt_init(&ctx->streams, 63, h2_stream_hash_free);
ctx->remote_max_sid = 2147483647;
ctx->via_h1_upgrade = via_h1_upgrade;
+#ifdef DEBUGBUILD
+ {
+ const char *p = getenv("CURL_H2_STREAM_WIN_MAX");
+
+ ctx->stream_win_max = H2_STREAM_WINDOW_SIZE_MAX;
+ if(p) {
+ curl_off_t l;
+ if(!Curl_str_number(&p, &l, INT_MAX))
+ ctx->stream_win_max = (int32_t)l;
+ }
+ }
+#endif
ctx->initialized = TRUE;
}
@@ -285,7 +301,15 @@ static int32_t cf_h2_get_desired_local_win(struct Curl_cfilter *cf,
* This gets less precise the higher the latency. */
return (int32_t)data->set.max_recv_speed;
}
+#ifdef DEBUGBUILD
+ else {
+ struct cf_h2_ctx *ctx = cf->ctx;
+ CURL_TRC_CF(data, cf, "stream_win_max=%d", ctx->stream_win_max);
+ return ctx->stream_win_max;
+ }
+#else
return H2_STREAM_WINDOW_SIZE_MAX;
+#endif
}
static CURLcode cf_h2_update_local_win(struct Curl_cfilter *cf,
@@ -302,6 +326,13 @@ static CURLcode cf_h2_update_local_win(struct Curl_cfilter *cf,
int32_t wsize = nghttp2_session_get_stream_effective_local_window_size(
ctx->h2, stream->id);
if(dwsize > wsize) {
+ rv = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE,
+ stream->id, dwsize);
+ if(rv) {
+ failf(data, "[%d] nghttp2 set_local_window_size(%d) failed: "
+ "%s(%d)", stream->id, dwsize, nghttp2_strerror(rv), rv);
+ return CURLE_HTTP2;
+ }
rv = nghttp2_submit_window_update(ctx->h2, NGHTTP2_FLAG_NONE,
stream->id, dwsize - wsize);
if(rv) {
diff --git a/tests/http/test_02_download.py b/tests/http/test_02_download.py
index 4b9ae3caefab..b55f022338ad 100644
--- a/tests/http/test_02_download.py
+++ b/tests/http/test_02_download.py
@@ -313,9 +313,9 @@ def test_02_20_h2_small_frames(self, env: Env, httpd):
assert httpd.stop()
assert httpd.start()
- # download via lib client, 1 at a time, pause/resume at different offsets
+ # download serial via lib client, pause/resume at different offsets
@pytest.mark.parametrize("pause_offset", [0, 10*1024, 100*1023, 640000])
- @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
+ @pytest.mark.parametrize("proto", ['http/1.1', 'h3'])
def test_02_21_lib_serial(self, env: Env, httpd, nghttpx, proto, pause_offset):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
@@ -332,6 +332,29 @@ def test_02_21_lib_serial(self, env: Env, httpd, nghttpx, proto, pause_offset):
srcfile = os.path.join(httpd.docs_dir, docname)
self.check_downloads(client, srcfile, count)
+ # h2 download parallel via lib client, pause/resume at different offsets
+ # debug-override stream window size to reproduce #16955
+ @pytest.mark.parametrize("pause_offset", [0, 10*1024, 100*1023, 640000])
+ @pytest.mark.parametrize("swin_max", [0, 10*1024])
+ def test_02_21_h2_lib_serial(self, env: Env, httpd, pause_offset, swin_max):
+ proto = 'h2'
+ count = 2
+ docname = 'data-10m'
+ url = f'https://localhost:{env.https_port}/{docname}'
+ run_env = os.environ.copy()
+ run_env['CURL_DEBUG'] = 'multi,http/2'
+ if swin_max > 0:
+ run_env['CURL_H2_STREAM_WIN_MAX'] = f'{swin_max}'
+ client = LocalClient(name='hx-download', env=env, run_env=run_env)
+ if not client.exists():
+ pytest.skip(f'example client not built: {client.name}')
+ r = client.run(args=[
+ '-n', f'{count}', '-P', f'{pause_offset}', '-V', proto, url
+ ])
+ r.check_exit_code(0)
+ srcfile = os.path.join(httpd.docs_dir, docname)
+ self.check_downloads(client, srcfile, count)
+
# download via lib client, several at a time, pause/resume
@pytest.mark.parametrize("pause_offset", [100*1023])
@pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])

View file

@ -91,7 +91,7 @@ in
stdenv.mkDerivation (finalAttrs: {
pname = "curl";
version = "8.12.1";
version = "8.13.0";
src = fetchurl {
urls = [
@ -100,9 +100,14 @@ stdenv.mkDerivation (finalAttrs: {
builtins.replaceStrings [ "." ] [ "_" ] finalAttrs.version
}/curl-${finalAttrs.version}.tar.xz"
];
hash = "sha256-A0Hx7ZeibIEauuvTfWK4M5VnkrdgfqPxXQAWE8dt4gI=";
hash = "sha256-Sgk5eaPC0C3i+8AFSaMncQB/LngDLG+qXs0vep4VICU=";
};
patches = [
# Backport of https://github.com/curl/curl/commit/5fbd78eb2dc4afbd8884e8eed27147fc3d4318f6
./0001-http2-fix-stream-window-size-after-unpausing.patch
];
# this could be accomplished by updateAutotoolsGnuConfigScriptsHook, but that causes infinite recursion
# necessary for FreeBSD code path in configure
postPatch = ''