diff --git a/docs/libcurl/curl_easy_pause.3 b/docs/libcurl/curl_easy_pause.3 index 32e8570cc971..048ab80d20c7 100644 --- a/docs/libcurl/curl_easy_pause.3 +++ b/docs/libcurl/curl_easy_pause.3 @@ -80,7 +80,9 @@ this because it needs to drain the socket for the other transfers and the already announced window size for the paused transfer will allow the server to continue sending data up to that window size amount. By default, libcurl announces a 32 megabyte window size, which thus can make libcurl end up -buffering 32 megabyte of data for a paused stream. +buffering 32 megabyte of data for a paused stream. The window size can be +changed with \fICURLOPT_STREAM_WINDOW_SIZE(3)\fP (easy interface) or +\fICURLMOPT_STREAM_WINDOW_SIZE(3)\fP (multi interface). When such a paused stream is unpaused again, any buffered data will be delivered first. @@ -109,4 +111,5 @@ CURLE_OK (zero) means that the option was set properly, and a non-zero return code means something wrong occurred after the new state was set. See the \fIlibcurl-errors(3)\fP man page for the full list with descriptions. .SH "SEE ALSO" -.BR curl_easy_cleanup "(3), " curl_easy_reset "(3)" +.BR curl_easy_cleanup "(3), " curl_easy_reset "(3), " +.BR CURLOPT_STREAM_WINDOW_SIZE "(3), " CURLMOPT_STREAM_WINDOW_SIZE "(3)" diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3 index 2fcfe2e75058..c2864f0b5256 100644 --- a/docs/libcurl/curl_easy_setopt.3 +++ b/docs/libcurl/curl_easy_setopt.3 @@ -367,6 +367,8 @@ This HTTP/2 stream depends on another exclusively. See \fICURLOPT_STREAM_DEPENDS_E(3)\fP .IP CURLOPT_STREAM_WEIGHT Set this HTTP/2 stream's weight. See \fICURLOPT_STREAM_WEIGHT(3)\fP +.IP CURLOPT_STREAM_WINDOW_SIZE +Set the HTTP/2 stream window size. See \fICURLMOPT_STREAM_WINDOW_SIZE(3)\fP .SH SMTP OPTIONS .IP CURLOPT_MAIL_FROM Address of the sender. See \fICURLOPT_MAIL_FROM(3)\fP diff --git a/docs/libcurl/curl_multi_setopt.3 b/docs/libcurl/curl_multi_setopt.3 index 1b2fb90d0023..b2b6c57528db 100644 --- a/docs/libcurl/curl_multi_setopt.3 +++ b/docs/libcurl/curl_multi_setopt.3 @@ -63,6 +63,8 @@ See \fICURLMOPT_PUSHDATA(3)\fP See \fICURLMOPT_SOCKETFUNCTION(3)\fP .IP CURLMOPT_SOCKETDATA See \fICURLMOPT_SOCKETDATA(3)\fP +.IP CURLMOPT_STREAM_WINDOW_SIZE +See \fICURLMOPT_STREAM_WINDOW_SIZE(3)\fP .IP CURLMOPT_TIMERFUNCTION See \fICURLMOPT_TIMERFUNCTION(3)\fP .IP CURLMOPT_TIMERDATA diff --git a/docs/libcurl/opts/CURLMOPT_STREAM_WINDOW_SIZE.3 b/docs/libcurl/opts/CURLMOPT_STREAM_WINDOW_SIZE.3 new file mode 100644 index 000000000000..3b532a836de2 --- /dev/null +++ b/docs/libcurl/opts/CURLMOPT_STREAM_WINDOW_SIZE.3 @@ -0,0 +1,60 @@ +.\" ************************************************************************** +.\" * _ _ ____ _ +.\" * Project ___| | | | _ \| | +.\" * / __| | | | |_) | | +.\" * | (__| |_| | _ <| |___ +.\" * \___|\___/|_| \_\_____| +.\" * +.\" * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. +.\" * +.\" * This software is licensed as described in the file COPYING, which +.\" * you should have received as part of this distribution. The terms +.\" * are also available at https://curl.se/docs/copyright.html. +.\" * +.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell +.\" * copies of the Software, and permit persons to whom the Software is +.\" * furnished to do so, under the terms of the COPYING file. +.\" * +.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +.\" * KIND, either express or implied. +.\" * +.\" ************************************************************************** +.\" +.TH CURLMOPT_STREAM_WINDOW_SIZE 3 "17 Nov 2021" "libcurl 7.81.0" "curl_multi_setopt options" +.SH NAME +CURLMOPT_STREAM_WINDOW_SIZE \- set stream window size for http2 +.SH SYNOPSIS +.nf +#include + +CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_STREAM_WINDOW_SIZE, + long size); +.fi +.SH DESCRIPTION +Pass a long indicating the sender's HTTP/2 initial stream window \fBsize\fP in +bytes. This setting affects the initial window size of all streams. When using +\fIcurl_easy_pause(3)\fP , this value is also the maximum number of bytes libcurl +has to buffer for a paused stream. + +This option is for the multi handle's use only. When using the easy interface, +you should instead use the \fICURLOPT_STREAM_WINDOW_SIZE(3)\fP option. + +Valid values range from 1 to 2147483647 (0x7FFFFFFF). +.SH DEFAULT +33554432 (32 megabytes) +.SH PROTOCOLS +HTTP/2 +.SH EXAMPLE +.nf + CURLM *m = curl_multi_init(); + /* set stream window size to HTTP/2 default 2^16-1 */ + curl_multi_setopt(m, CURLMOPT_STREAM_WINDOW_SIZE, 65535L); +.fi +.SH AVAILABILITY +Added in 7.81.0 +.SH RETURN VALUE +Returns CURLM_OK of the option is supported and window size is within range, +CURLM_BAD_FUNCTION_ARGUMENT if not within range. Returns CURLM_UNKNOWN_OPTION +if the option is not supported. +.SH "SEE ALSO" +.BR curl_easy_pause "(3), " CURLOPT_STREAM_WINDOW_SIZE "(3)" diff --git a/docs/libcurl/opts/CURLOPT_STREAM_WINDOW_SIZE.3 b/docs/libcurl/opts/CURLOPT_STREAM_WINDOW_SIZE.3 new file mode 100644 index 000000000000..82be875d2620 --- /dev/null +++ b/docs/libcurl/opts/CURLOPT_STREAM_WINDOW_SIZE.3 @@ -0,0 +1,62 @@ +.\" ************************************************************************** +.\" * _ _ ____ _ +.\" * Project ___| | | | _ \| | +.\" * / __| | | | |_) | | +.\" * | (__| |_| | _ <| |___ +.\" * \___|\___/|_| \_\_____| +.\" * +.\" * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. +.\" * +.\" * This software is licensed as described in the file COPYING, which +.\" * you should have received as part of this distribution. The terms +.\" * are also available at https://curl.se/docs/copyright.html. +.\" * +.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell +.\" * copies of the Software, and permit persons to whom the Software is +.\" * furnished to do so, under the terms of the COPYING file. +.\" * +.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +.\" * KIND, either express or implied. +.\" * +.\" ************************************************************************** +.\" +.TH CURLOPT_STREAM_WINDOW_SIZE 3 "17 Nov 2021" "libcurl 7.81.0" "curl_easy_setopt options" +.SH NAME +CURLOPT_STREAM_WINDOW_SIZE \- set stream window size for http2 +.SH SYNOPSIS +.nf +#include + +CURLMcode curl_easy_setopt(CURL *handle, CURLOPT_STREAM_WINDOW_SIZE, + long size); +.fi +.SH DESCRIPTION +Pass a long indicating the sender's HTTP/2 initial stream window \fBsize\fP in +bytes. This setting affects the initial window size of all streams. When using +\fIcurl_easy_pause(3)\fP , this value is also the maximum number of bytes libcurl +has to buffer for a paused stream. + +If you add this easy handle to a multi handle, this setting is not +acknowledged, and you must instead use \fIcurl_multi_setopt(3)\fP and the +\fICURLMOPT_STREAM_WINDOW_SIZE(3)\fP option. + +Valid values range from 1 to 2147483647 (0x7FFFFFFF). +.SH DEFAULT +33554432 (32 megabytes) +.SH PROTOCOLS +HTTP/2 +.SH EXAMPLE +.nf + CURL *curl = curl_easy_init(); + /* set stream window size to HTTP/2 default 2^16-1 */ + curl_easy_setopt(curl, CURLOPT_STREAM_WINDOW_SIZE, 65535L); +.fi +.SH AVAILABILITY +Added in 7.81.0 +.SH RETURN VALUE + +Returns CURLE_OK of the option is supported and window size is within range, +CURLE_BAD_FUNCTION_ARGUMENT if not within range. Returns CURLE_UNKNOWN_OPTION +if the option is not supported. +.SH "SEE ALSO" +.BR curl_easy_pause "(3), " CURLMOPT_STREAM_WINDOW_SIZE "(3)" diff --git a/docs/libcurl/opts/Makefile.inc b/docs/libcurl/opts/Makefile.inc index 5a270a8a9a15..9ea9ffe8a52b 100644 --- a/docs/libcurl/opts/Makefile.inc +++ b/docs/libcurl/opts/Makefile.inc @@ -103,6 +103,7 @@ man_MANS = \ CURLMOPT_PUSHFUNCTION.3 \ CURLMOPT_SOCKETDATA.3 \ CURLMOPT_SOCKETFUNCTION.3 \ + CURLMOPT_STREAM_WINDOW_SIZE.3 \ CURLMOPT_TIMERDATA.3 \ CURLMOPT_TIMERFUNCTION.3 \ CURLOPT_ABSTRACT_UNIX_SOCKET.3 \ @@ -361,6 +362,7 @@ man_MANS = \ CURLOPT_STREAM_DEPENDS.3 \ CURLOPT_STREAM_DEPENDS_E.3 \ CURLOPT_STREAM_WEIGHT.3 \ + CURLOPT_STREAM_WINDOW_SIZE.3 \ CURLOPT_SUPPRESS_CONNECT_HEADERS.3 \ CURLOPT_TCP_FASTOPEN.3 \ CURLOPT_TCP_KEEPALIVE.3 \ diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index 791834d6e6cf..f249a8baf8f3 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -339,6 +339,7 @@ CURLMOPT_PUSHDATA 7.44.0 CURLMOPT_PUSHFUNCTION 7.44.0 CURLMOPT_SOCKETDATA 7.15.4 CURLMOPT_SOCKETFUNCTION 7.15.4 +CURLMOPT_STREAM_WINDOW_SIZE 7.81.0 CURLMOPT_TIMERDATA 7.16.0 CURLMOPT_TIMERFUNCTION 7.16.0 CURLMSG_DONE 7.9.6 @@ -651,6 +652,7 @@ CURLOPT_STDERR 7.1 CURLOPT_STREAM_DEPENDS 7.46.0 CURLOPT_STREAM_DEPENDS_E 7.46.0 CURLOPT_STREAM_WEIGHT 7.46.0 +CURLOPT_STREAM_WINDOW_SIZE 7.81.0 CURLOPT_SUPPRESS_CONNECT_HEADERS 7.54.0 CURLOPT_TCP_FASTOPEN 7.49.0 CURLOPT_TCP_KEEPALIVE 7.25.0 diff --git a/include/curl/curl.h b/include/curl/curl.h index 7b69ce2d673a..62a74b7b85ca 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -2135,6 +2135,9 @@ typedef enum { /* Set MIME option flags. */ CURLOPT(CURLOPT_MIME_OPTIONS, CURLOPTTYPE_LONG, 315), + /* Stream window size */ + CURLOPT(CURLOPT_STREAM_WINDOW_SIZE, CURLOPTTYPE_LONG, 316), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; diff --git a/include/curl/multi.h b/include/curl/multi.h index 37f9829b3b31..a08b4bbf12f4 100644 --- a/include/curl/multi.h +++ b/include/curl/multi.h @@ -395,6 +395,9 @@ typedef enum { /* maximum number of concurrent streams to support on a connection */ CURLOPT(CURLMOPT_MAX_CONCURRENT_STREAMS, CURLOPTTYPE_LONG, 16), + /* stream window size (default 32M) */ + CURLOPT(CURLMOPT_STREAM_WINDOW_SIZE, CURLOPTTYPE_LONG, 17), + CURLMOPT_LASTENTRY /* the last unused */ } CURLMoption; diff --git a/lib/easy.c b/lib/easy.c index 2aca93845b45..a33a3c0649fc 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -681,6 +681,10 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) /* Copy the MAXCONNECTS option to the multi handle */ curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, data->set.maxconnects); + /* Copy the STREAM_WINDOW_SIZE option to the multi handle */ + curl_multi_setopt(multi, CURLMOPT_STREAM_WINDOW_SIZE, + data->set.stream_window_size); + mcode = curl_multi_add_handle(multi, data); if(mcode) { curl_multi_cleanup(multi); diff --git a/lib/easyoptions.c b/lib/easyoptions.c index 04871ad1e3b7..94609815dfb9 100644 --- a/lib/easyoptions.c +++ b/lib/easyoptions.c @@ -309,6 +309,7 @@ struct curl_easyoption Curl_easyopts[] = { {"STREAM_DEPENDS", CURLOPT_STREAM_DEPENDS, CURLOT_OBJECT, 0}, {"STREAM_DEPENDS_E", CURLOPT_STREAM_DEPENDS_E, CURLOT_OBJECT, 0}, {"STREAM_WEIGHT", CURLOPT_STREAM_WEIGHT, CURLOT_LONG, 0}, + {"STREAM_WINDOW_SIZE", CURLOPT_STREAM_WINDOW_SIZE, CURLOT_LONG, 0}, {"SUPPRESS_CONNECT_HEADERS", CURLOPT_SUPPRESS_CONNECT_HEADERS, CURLOT_LONG, 0}, {"TCP_FASTOPEN", CURLOPT_TCP_FASTOPEN, CURLOT_LONG, 0}, @@ -360,6 +361,6 @@ struct curl_easyoption Curl_easyopts[] = { */ int Curl_easyopts_check(void) { - return ((CURLOPT_LASTENTRY%10000) != (315 + 1)); + return ((CURLOPT_LASTENTRY%10000) != (316 + 1)); } #endif diff --git a/lib/http2.c b/lib/http2.c index 992fbbb26db1..a2784f64b459 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -56,8 +56,6 @@ #define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1 #endif -#define HTTP2_HUGE_WINDOW_SIZE (32 * 1024 * 1024) /* 32 MB */ - #ifdef DEBUG_HTTP2 #define H2BUGF(x) x #else @@ -1194,7 +1192,7 @@ static void populate_settings(struct Curl_easy *data, iv[0].value = Curl_multi_max_concurrent_streams(data->multi); iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[1].value = HTTP2_HUGE_WINDOW_SIZE; + iv[1].value = data->multi->stream_window_size; iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; iv[2].value = data->multi->push_cb != NULL; @@ -2375,7 +2373,7 @@ CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause) else { struct HTTP *stream = data->req.p.http; struct http_conn *httpc = &data->conn->proto.httpc; - uint32_t window = !pause * HTTP2_HUGE_WINDOW_SIZE; + uint32_t window = !pause * data->multi->stream_window_size; int rv = nghttp2_session_set_local_window_size(httpc->h2, NGHTTP2_FLAG_NONE, stream->stream_id, diff --git a/lib/http2.h b/lib/http2.h index d6986d97faf8..7daeaf8efb76 100644 --- a/lib/http2.h +++ b/lib/http2.h @@ -31,6 +31,8 @@ from the peer */ #define DEFAULT_MAX_CONCURRENT_STREAMS 100 +#define HTTP2_HUGE_WINDOW_SIZE (32 * 1024 * 1024) /* 32 MB */ + /* * Store nghttp2 version info in this buffer. */ diff --git a/lib/multi.c b/lib/multi.c index ce634fcacbce..fd25b8f06645 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -379,6 +379,9 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ /* -1 means it not set by user, use the default value */ multi->maxconnects = -1; multi->max_concurrent_streams = 100; +#ifdef USE_NGHTTP2 + multi->stream_window_size = HTTP2_HUGE_WINDOW_SIZE; +#endif multi->ipv6_works = Curl_ipv6works(NULL); #ifdef USE_WINSOCK @@ -3154,6 +3157,16 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi, multi->max_concurrent_streams = curlx_sltoui(streams); } break; + case CURLMOPT_STREAM_WINDOW_SIZE: + { + long stream_window_size = va_arg(param, long); + if((stream_window_size > 0) && + ((unsigned long)stream_window_size <= 0x7FFFFFFF)) + multi->stream_window_size = curlx_sltoui(stream_window_size); + else + return CURLM_BAD_FUNCTION_ARGUMENT; + } + break; default: res = CURLM_UNKNOWN_OPTION; break; diff --git a/lib/multihandle.h b/lib/multihandle.h index 2e4a6ffba5ba..aab199d398dd 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -139,6 +139,7 @@ struct Curl_multi { struct curltime timer_lastcall; /* the fixed time for the timeout for the previous callback */ unsigned int max_concurrent_streams; + unsigned int stream_window_size; #ifdef USE_WINSOCK WSAEVENT wsa_event; /* winsock event used for waits */ diff --git a/lib/setopt.c b/lib/setopt.c index ddb010259f97..af505432e820 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -3036,6 +3036,16 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_PREREQDATA: data->set.prereq_userp = va_arg(param, void *); break; + case CURLOPT_STREAM_WINDOW_SIZE: + { + long stream_window_size = va_arg(param, long); + if((stream_window_size > 0) && + ((unsigned long)stream_window_size <= 0x7FFFFFFF)) + data->set.stream_window_size = curlx_sltoui(stream_window_size); + else + return CURLE_BAD_FUNCTION_ARGUMENT; + } + break; default: /* unknown tag and its companion, just ignore: */ result = CURLE_UNKNOWN_OPTION; diff --git a/lib/url.c b/lib/url.c index 9f446817f58a..aa3980b8f6a5 100644 --- a/lib/url.c +++ b/lib/url.c @@ -619,6 +619,9 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->fnmatch = ZERO_NULL; set->upkeep_interval_ms = CURL_UPKEEP_INTERVAL_DEFAULT; set->maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */ +#ifdef USE_NGHTTP2 + set->stream_window_size = HTTP2_HUGE_WINDOW_SIZE; /* for easy handles */ +#endif set->maxage_conn = 118; set->maxlifetime_conn = 0; set->http09_allowed = FALSE; diff --git a/lib/urldata.h b/lib/urldata.h index 22c66cd441b3..215087611cbd 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1775,6 +1775,7 @@ struct UserDefined { struct Curl_easy *stream_depends_on; int stream_weight; struct Curl_http2_dep *stream_dependents; + unsigned int stream_window_size; curl_resolver_start_callback resolver_start; /* optional callback called before resolver start */