From 6d81907540e8a487ed3098d3ed7de27fcfcef386 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 15 Feb 2026 08:56:43 +0900 Subject: [PATCH 1/2] fix(files,http,web): do not compress 206 at all (#3923) --- actix-files/CHANGES.md | 2 ++ actix-files/src/named.rs | 23 +---------------------- actix-files/tests/encoding.rs | 9 +++------ actix-http/CHANGES.md | 2 ++ actix-http/src/encoding/encoder.rs | 1 + actix-web/CHANGES.md | 2 ++ actix-web/src/middleware/compress.rs | 23 +++++++++++++++++++++++ 7 files changed, 34 insertions(+), 28 deletions(-) diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index f25a56228..6f4056f57 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -5,9 +5,11 @@ - Add `Files::try_compressed()` to support serving pre-compressed static files [#2615] - Fix handling of `bytes=0-` - Fix `NamedFile` panic when serving files with pre-UNIX epoch modification times. [#2748] +- Fix invalid `Content-Encoding: identity` header in `NamedFile` range responses. [#3191] [#2615]: https://github.com/actix/actix-web/pull/2615 [#2748]: https://github.com/actix/actix-web/issues/2748 +[#3191]: https://github.com/actix/actix-web/issues/3191 ## 0.6.10 diff --git a/actix-files/src/named.rs b/actix-files/src/named.rs index 09e3706e1..f1a4642d7 100644 --- a/actix-files/src/named.rs +++ b/actix-files/src/named.rs @@ -14,7 +14,7 @@ use actix_web::{ http::{ header::{ self, Charset, ContentDisposition, ContentEncoding, DispositionParam, DispositionType, - ExtendedValue, HeaderValue, + ExtendedValue, }, StatusCode, }, @@ -593,27 +593,6 @@ impl NamedFile { length = range.length; offset = range.start; - // When a Content-Encoding header is present in a 206 partial content response - // for video content, it prevents browser video players from starting playback - // before loading the whole video and also prevents seeking. - // - // See: https://github.com/actix/actix-web/issues/2815 - // - // The assumption of this fix is that the video player knows to not send an - // Accept-Encoding header for this request and that downstream middleware will - // not attempt compression for requests without it. - // - // TODO: Solve question around what to do if self.encoding is set and partial - // range is requested. Reject request? Ignoring self.encoding seems wrong, too. - // In practice, it should not come up. - if req.headers().contains_key(&header::ACCEPT_ENCODING) { - // don't allow compression middleware to modify partial content - res.insert_header(( - header::CONTENT_ENCODING, - HeaderValue::from_static("identity"), - )); - } - res.insert_header(( header::CONTENT_RANGE, format!("bytes {}-{}/{}", offset, offset + length - 1, self.md.len()), diff --git a/actix-files/tests/encoding.rs b/actix-files/tests/encoding.rs index 019abfb57..72e72b913 100644 --- a/actix-files/tests/encoding.rs +++ b/actix-files/tests/encoding.rs @@ -181,15 +181,12 @@ async fn partial_range_response_encoding() { assert_eq!(res.status(), StatusCode::PARTIAL_CONTENT); assert!(!res.headers().contains_key(header::CONTENT_ENCODING)); - // range request with accept-encoding returns a content-encoding header + // range request with accept-encoding still returns no content-encoding header let req = TestRequest::with_uri("/") .append_header((header::RANGE, "bytes=10-20")) - .append_header((header::ACCEPT_ENCODING, "identity")) + .append_header((header::ACCEPT_ENCODING, "gzip")) .to_request(); let res = test::call_service(&srv, req).await; assert_eq!(res.status(), StatusCode::PARTIAL_CONTENT); - assert_eq!( - res.headers().get(header::CONTENT_ENCODING).unwrap(), - "identity" - ); + assert!(!res.headers().contains_key(header::CONTENT_ENCODING)); } diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index b52aa5c58..89ad54596 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -5,9 +5,11 @@ - Minimum supported Rust version (MSRV) is now 1.88. - Fix truncated body ending without error when connection closed abnormally. [#3067] - Add config/method for `TCP_NODELAY`. [#3918] +- Do not compress 206 Partial Content responses. [#3191] [#3067]: https://github.com/actix/actix-web/pull/3067 [#3918]: https://github.com/actix/actix-web/pull/3918 +[#3191]: https://github.com/actix/actix-web/issues/3191 ## 3.11.2 diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index 0da95c462..f40338689 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -70,6 +70,7 @@ impl Encoder { let should_encode = !(head.headers().contains_key(&CONTENT_ENCODING) || head.status == StatusCode::SWITCHING_PROTOCOLS || head.status == StatusCode::NO_CONTENT + || head.status == StatusCode::PARTIAL_CONTENT || encoding == ContentEncoding::Identity); let body = match body.try_into_bytes() { diff --git a/actix-web/CHANGES.md b/actix-web/CHANGES.md index bebc1d3eb..a89060b57 100644 --- a/actix-web/CHANGES.md +++ b/actix-web/CHANGES.md @@ -8,11 +8,13 @@ - Add `experimental-introspection` feature to report configured routes [#3594] - Add config/method for `TCP_NODELAY`. [#3918] - Fix panic when `NormalizePath` rewrites a scoped dynamic path before extraction (e.g., `scope("{tail:.*}")` + `Path`). [#3562] +- Do not compress 206 Partial Content responses. [#3191] [#3895]: https://github.com/actix/actix-web/pull/3895 [#3594]: https://github.com/actix/actix-web/pull/3594 [#3918]: https://github.com/actix/actix-web/pull/3918 [#3562]: https://github.com/actix/actix-web/issues/3562 +[#3191]: https://github.com/actix/actix-web/issues/3191 ## 4.12.1 diff --git a/actix-web/src/middleware/compress.rs b/actix-web/src/middleware/compress.rs index 7f0d8a4fb..38bb909fd 100644 --- a/actix-web/src/middleware/compress.rs +++ b/actix-web/src/middleware/compress.rs @@ -449,6 +449,29 @@ mod tests { assert!(!res.headers().contains_key(header::CONTENT_ENCODING)); assert!(test::read_body(res).await.is_empty()); } + + #[actix_rt::test] + async fn skips_compression_partial_content() { + let app = test::init_service({ + App::new() + .wrap(Compress::default()) + .default_service(web::to(|| { + HttpResponse::PartialContent() + .insert_header((header::CONTENT_TYPE, "text/plain")) + .insert_header((header::CONTENT_RANGE, "bytes 0-10/100")) + .body(TEXT_DATA) + })) + }) + .await; + + let req = test::TestRequest::default() + .insert_header((header::ACCEPT_ENCODING, "gzip")) + .to_request(); + let res = test::call_service(&app, req).await; + assert_eq!(res.status(), StatusCode::PARTIAL_CONTENT); + assert!(!res.headers().contains_key(header::CONTENT_ENCODING)); + assert_eq!(test::read_body(res).await, TEXT_DATA.as_bytes()); + } } #[cfg(feature = "compress-brotli")] From 5a6c8d235bd8e04ca28c9516f9b1e2c987d89da5 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 15 Feb 2026 16:36:27 +0900 Subject: [PATCH 2/2] feat(http,web): expose config for H2 window sizes (#3926) * feat(http,web): expose config for H2 window sizes * changelog --- actix-http/CHANGES.md | 3 ++ actix-http/src/builder.rs | 31 ++++++++++++ actix-http/src/config.rs | 48 ++++++++++++++++++ actix-http/src/h2/mod.rs | 9 +++- actix-http/tests/test_server.rs | 64 +++++++++++++++++++++++- actix-web/CHANGES.md | 3 ++ actix-web/src/server.rs | 87 +++++++++++++++++++++++++++++++++ 7 files changed, 242 insertions(+), 3 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 89ad54596..c8a2bbd92 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -3,10 +3,13 @@ ## Unreleased - Minimum supported Rust version (MSRV) is now 1.88. +- Increase default HTTP/2 flow control window sizes. [#3638] +- Expose configuration methods to improve upload throughput. [#3638] - Fix truncated body ending without error when connection closed abnormally. [#3067] - Add config/method for `TCP_NODELAY`. [#3918] - Do not compress 206 Partial Content responses. [#3191] +[#3638]: https://github.com/actix/actix-web/issues/3638 [#3067]: https://github.com/actix/actix-web/pull/3067 [#3918]: https://github.com/actix/actix-web/pull/3918 [#3191]: https://github.com/actix/actix-web/issues/3191 diff --git a/actix-http/src/builder.rs b/actix-http/src/builder.rs index c01b63ccd..fff7ceefe 100644 --- a/actix-http/src/builder.rs +++ b/actix-http/src/builder.rs @@ -5,6 +5,7 @@ use actix_service::{IntoServiceFactory, Service, ServiceFactory}; use crate::{ body::{BoxBody, MessageBody}, + config::{DEFAULT_H2_CONN_WINDOW_SIZE, DEFAULT_H2_STREAM_WINDOW_SIZE}, h1::{self, ExpectHandler, H1Service, UpgradeHandler}, service::HttpService, ConnectCallback, Extensions, KeepAlive, Request, Response, ServiceConfigBuilder, @@ -21,6 +22,8 @@ pub struct HttpServiceBuilder { secure: bool, local_addr: Option, h1_allow_half_closed: bool, + h2_conn_window_size: u32, + h2_stream_window_size: u32, expect: X, upgrade: Option, on_connect_ext: Option>>, @@ -44,6 +47,8 @@ where secure: false, local_addr: None, h1_allow_half_closed: true, + h2_conn_window_size: DEFAULT_H2_CONN_WINDOW_SIZE, + h2_stream_window_size: DEFAULT_H2_STREAM_WINDOW_SIZE, // dispatcher parts expect: ExpectHandler, @@ -146,6 +151,22 @@ where self } + /// Sets initial stream-level flow control window size for HTTP/2 connections. + /// + /// See [`ServiceConfigBuilder::h2_initial_window_size`] for more details. + pub fn h2_initial_window_size(mut self, size: u32) -> Self { + self.h2_stream_window_size = size; + self + } + + /// Sets initial connection-level flow control window size for HTTP/2 connections. + /// + /// See [`ServiceConfigBuilder::h2_initial_connection_window_size`] for more details. + pub fn h2_initial_connection_window_size(mut self, size: u32) -> Self { + self.h2_conn_window_size = size; + self + } + /// Provide service for `EXPECT: 100-Continue` support. /// /// Service get called with request that contains `EXPECT` header. @@ -166,6 +187,8 @@ where secure: self.secure, local_addr: self.local_addr, h1_allow_half_closed: self.h1_allow_half_closed, + h2_conn_window_size: self.h2_conn_window_size, + h2_stream_window_size: self.h2_stream_window_size, expect: expect.into_factory(), upgrade: self.upgrade, on_connect_ext: self.on_connect_ext, @@ -192,6 +215,8 @@ where secure: self.secure, local_addr: self.local_addr, h1_allow_half_closed: self.h1_allow_half_closed, + h2_conn_window_size: self.h2_conn_window_size, + h2_stream_window_size: self.h2_stream_window_size, expect: self.expect, upgrade: Some(upgrade.into_factory()), on_connect_ext: self.on_connect_ext, @@ -229,6 +254,8 @@ where .secure(self.secure) .local_addr(self.local_addr) .h1_allow_half_closed(self.h1_allow_half_closed) + .h2_initial_window_size(self.h2_stream_window_size) + .h2_initial_connection_window_size(self.h2_conn_window_size) .build(); H1Service::with_config(cfg, service.into_factory()) @@ -256,6 +283,8 @@ where .secure(self.secure) .local_addr(self.local_addr) .h1_allow_half_closed(self.h1_allow_half_closed) + .h2_initial_window_size(self.h2_stream_window_size) + .h2_initial_connection_window_size(self.h2_conn_window_size) .build(); crate::h2::H2Service::with_config(cfg, service.into_factory()) @@ -280,6 +309,8 @@ where .secure(self.secure) .local_addr(self.local_addr) .h1_allow_half_closed(self.h1_allow_half_closed) + .h2_initial_window_size(self.h2_stream_window_size) + .h2_initial_connection_window_size(self.h2_conn_window_size) .build(); HttpService::with_config(cfg, service.into_factory()) diff --git a/actix-http/src/config.rs b/actix-http/src/config.rs index 5dc18eada..c0fbc7521 100644 --- a/actix-http/src/config.rs +++ b/actix-http/src/config.rs @@ -8,6 +8,16 @@ use bytes::BytesMut; use crate::{date::DateService, KeepAlive}; +/// Default HTTP/2 initial connection-level flow control window size. +/// +/// Matches awc's defaults to avoid poor throughput on high-BDP links. +pub(crate) const DEFAULT_H2_CONN_WINDOW_SIZE: u32 = 1024 * 1024 * 2; // 2MiB + +/// Default HTTP/2 initial stream-level flow control window size. +/// +/// Matches awc's defaults to avoid poor throughput on high-BDP links. +pub(crate) const DEFAULT_H2_STREAM_WINDOW_SIZE: u32 = 1024 * 1024; // 1MiB + /// A builder for creating a [`ServiceConfig`] #[derive(Default, Debug)] pub struct ServiceConfigBuilder { @@ -76,6 +86,28 @@ impl ServiceConfigBuilder { self } + /// Sets initial stream-level flow control window size for HTTP/2 connections. + /// + /// Higher values can improve upload performance on high-latency links at the cost of higher + /// worst-case memory usage per connection. + /// + /// The default value is 1MiB. + pub fn h2_initial_window_size(mut self, size: u32) -> Self { + self.inner.h2_stream_window_size = size; + self + } + + /// Sets initial connection-level flow control window size for HTTP/2 connections. + /// + /// Higher values can improve upload performance on high-latency links at the cost of higher + /// worst-case memory usage per connection. + /// + /// The default value is 2MiB. + pub fn h2_initial_connection_window_size(mut self, size: u32) -> Self { + self.inner.h2_conn_window_size = size; + self + } + /// Builds a [`ServiceConfig`] from this [`ServiceConfigBuilder`] instance pub fn build(self) -> ServiceConfig { ServiceConfig(Rc::new(self.inner)) @@ -96,6 +128,8 @@ struct Inner { tcp_nodelay: Option, date_service: DateService, h1_allow_half_closed: bool, + h2_conn_window_size: u32, + h2_stream_window_size: u32, } impl Default for Inner { @@ -109,6 +143,8 @@ impl Default for Inner { tcp_nodelay: None, date_service: DateService::new(), h1_allow_half_closed: true, + h2_conn_window_size: DEFAULT_H2_CONN_WINDOW_SIZE, + h2_stream_window_size: DEFAULT_H2_STREAM_WINDOW_SIZE, } } } @@ -131,6 +167,8 @@ impl ServiceConfig { tcp_nodelay: None, date_service: DateService::new(), h1_allow_half_closed: true, + h2_conn_window_size: DEFAULT_H2_CONN_WINDOW_SIZE, + h2_stream_window_size: DEFAULT_H2_STREAM_WINDOW_SIZE, })) } @@ -195,6 +233,16 @@ impl ServiceConfig { self.0.tcp_nodelay } + /// HTTP/2 initial stream-level flow control window size (in bytes). + pub fn h2_initial_window_size(&self) -> u32 { + self.0.h2_stream_window_size + } + + /// HTTP/2 initial connection-level flow control window size (in bytes). + pub fn h2_initial_connection_window_size(&self) -> u32 { + self.0.h2_conn_window_size + } + pub(crate) fn now(&self) -> Instant { self.0.date_service.now() } diff --git a/actix-http/src/h2/mod.rs b/actix-http/src/h2/mod.rs index e47099cac..300af2ed3 100644 --- a/actix-http/src/h2/mod.rs +++ b/actix-http/src/h2/mod.rs @@ -11,7 +11,7 @@ use actix_rt::time::{sleep_until, Sleep}; use bytes::Bytes; use futures_core::{ready, Stream}; use h2::{ - server::{handshake, Connection, Handshake}, + server::{Builder, Connection, Handshake}, RecvStream, }; @@ -61,8 +61,13 @@ pub(crate) fn handshake_with_timeout(io: T, config: &ServiceConfig) -> Handsh where T: AsyncRead + AsyncWrite + Unpin, { + let mut builder = Builder::new(); + builder + .initial_window_size(config.h2_initial_window_size()) + .initial_connection_window_size(config.h2_initial_connection_window_size()); + HandshakeWithTimeout { - handshake: handshake(io), + handshake: builder.handshake(io), timer: config .client_request_deadline() .map(|deadline| Box::pin(sleep_until(deadline.into()))), diff --git a/actix-http/tests/test_server.rs b/actix-http/tests/test_server.rs index 688fc9d0b..434652290 100644 --- a/actix-http/tests/test_server.rs +++ b/actix-http/tests/test_server.rs @@ -10,7 +10,10 @@ use actix_http::{ header, Error, HttpService, KeepAlive, Request, Response, StatusCode, Version, }; use actix_http_test::test_server; -use actix_rt::{net::TcpStream, time::sleep}; +use actix_rt::{ + net::TcpStream, + time::{sleep, timeout}, +}; use actix_service::fn_service; use actix_utils::future::{err, ok, ready}; use bytes::Bytes; @@ -953,3 +956,62 @@ async fn h2c_auto() { srv.stop().await; } + +#[actix_rt::test] +async fn h2_flow_control_window_sizes() { + let mut srv = test_server(|| { + HttpService::build() + .keep_alive(KeepAlive::Disabled) + .finish(|_req: Request| ok::<_, Infallible>(Response::ok())) + .tcp_auto_h2c() + }) + .await; + + let tcp = TcpStream::connect(srv.addr()).await.unwrap(); + + let mut builder = h2::client::Builder::new(); + builder.max_send_buffer_size(4 * 1024 * 1024); + + let (h2, connection) = builder.handshake(tcp).await.unwrap(); + tokio::spawn(async move { connection.await.unwrap() }); + let mut h2 = h2.ready().await.unwrap(); + + let request = ::http::Request::builder() + .method("POST") + .uri("/") + .body(()) + .unwrap(); + + let (response, mut send) = h2.send_request(request, false).unwrap(); + + // request more than the default 64KiB. if server is advertising larger flow control windows, + // we should get at least 1MiB assigned. + send.reserve_capacity(2 * 1024 * 1024); + + let cap = timeout(Duration::from_secs(2), async { + loop { + let cap = std::future::poll_fn(|cx| send.poll_capacity(cx)) + .await + .unwrap() + .unwrap(); + + if cap >= 1024 * 1024 { + break cap; + } + } + }) + .await + .unwrap(); + + assert!( + cap >= 1024 * 1024, + "expected >= 1MiB send capacity, got {cap}" + ); + + send.send_data(Bytes::new(), true).unwrap(); + + let res = response.await.unwrap(); + assert!(res.status().is_success()); + + srv.stop().await; +} diff --git a/actix-web/CHANGES.md b/actix-web/CHANGES.md index a89060b57..4d33d6753 100644 --- a/actix-web/CHANGES.md +++ b/actix-web/CHANGES.md @@ -3,6 +3,8 @@ ## Unreleased - Minimum supported Rust version (MSRV) is now 1.88. +- Improve HTTP/2 upload throughput by increasing default flow control window sizes. [#3638] +- Add `HttpServer::{h2_initial_window_size, h2_initial_connection_window_size}` methods for tuning. [#3638] - Add `HttpRequest::url_for_map` and `HttpRequest::url_for_iter` methods for named URL parameters. [#3895] - Ignore unparsable cookies in `Cookie` request header. - Add `experimental-introspection` feature to report configured routes [#3594] @@ -13,6 +15,7 @@ [#3895]: https://github.com/actix/actix-web/pull/3895 [#3594]: https://github.com/actix/actix-web/pull/3594 [#3918]: https://github.com/actix/actix-web/pull/3918 +[#3638]: https://github.com/actix/actix-web/issues/3638 [#3562]: https://github.com/actix/actix-web/issues/3562 [#3191]: https://github.com/actix/actix-web/issues/3191 diff --git a/actix-web/src/server.rs b/actix-web/src/server.rs index 39f1300bc..135522d3a 100644 --- a/actix-web/src/server.rs +++ b/actix-web/src/server.rs @@ -33,6 +33,8 @@ struct Config { client_request_timeout: Duration, client_disconnect_timeout: Duration, h1_allow_half_closed: bool, + h2_initial_window_size: Option, + h2_initial_connection_window_size: Option, #[allow(dead_code)] // only dead when no TLS features are enabled tls_handshake_timeout: Option, } @@ -120,6 +122,8 @@ where client_request_timeout: Duration::from_secs(5), client_disconnect_timeout: Duration::from_secs(1), h1_allow_half_closed: true, + h2_initial_window_size: None, + h2_initial_connection_window_size: None, tls_handshake_timeout: None, })), backlog: 1024, @@ -282,6 +286,33 @@ where self } + /// Sets initial stream-level flow control window size for HTTP/2 connections. + /// + /// Higher values can improve upload performance on high-latency links at the cost of higher + /// worst-case memory usage per connection. + /// + /// The default value is 1MiB. + #[cfg(feature = "http2")] + pub fn h2_initial_window_size(self, size: u32) -> Self { + self.config.lock().unwrap().h2_initial_window_size = Some(size); + self + } + + /// Sets initial connection-level flow control window size for HTTP/2 connections. + /// + /// Higher values can improve upload performance on high-latency links at the cost of higher + /// worst-case memory usage per connection. + /// + /// The default value is 2MiB. + #[cfg(feature = "http2")] + pub fn h2_initial_connection_window_size(self, size: u32) -> Self { + self.config + .lock() + .unwrap() + .h2_initial_connection_window_size = Some(size); + self + } + /// Sets function that will be called once before each connection is handled. /// /// It will receive a `&std::any::Any`, which contains underlying connection type and an @@ -590,6 +621,14 @@ where svc = svc.tcp_nodelay(enabled); } + if let Some(val) = cfg.h2_initial_window_size { + svc = svc.h2_initial_window_size(val); + } + + if let Some(val) = cfg.h2_initial_connection_window_size { + svc = svc.h2_initial_connection_window_size(val); + } + if let Some(handler) = on_connect_fn.clone() { svc = svc.on_connect_ext(move |io: &_, ext: _| (handler)(io as &dyn Any, ext)) @@ -639,6 +678,14 @@ where svc = svc.tcp_nodelay(enabled); } + if let Some(val) = cfg.h2_initial_window_size { + svc = svc.h2_initial_window_size(val); + } + + if let Some(val) = cfg.h2_initial_connection_window_size { + svc = svc.h2_initial_connection_window_size(val); + } + if let Some(handler) = on_connect_fn.clone() { svc = svc.on_connect_ext(move |io: &_, ext: _| (handler)(io as &dyn Any, ext)) @@ -719,6 +766,14 @@ where svc = svc.tcp_nodelay(enabled); } + if let Some(val) = c.h2_initial_window_size { + svc = svc.h2_initial_window_size(val); + } + + if let Some(val) = c.h2_initial_connection_window_size { + svc = svc.h2_initial_connection_window_size(val); + } + if let Some(handler) = on_connect_fn.clone() { svc = svc .on_connect_ext(move |io: &_, ext: _| (handler)(io as &dyn Any, ext)); @@ -774,6 +829,14 @@ where svc = svc.tcp_nodelay(enabled); } + if let Some(val) = c.h2_initial_window_size { + svc = svc.h2_initial_window_size(val); + } + + if let Some(val) = c.h2_initial_connection_window_size { + svc = svc.h2_initial_connection_window_size(val); + } + if let Some(handler) = on_connect_fn.clone() { svc = svc .on_connect_ext(move |io: &_, ext: _| (handler)(io as &dyn Any, ext)); @@ -844,6 +907,14 @@ where svc = svc.tcp_nodelay(enabled); } + if let Some(val) = c.h2_initial_window_size { + svc = svc.h2_initial_window_size(val); + } + + if let Some(val) = c.h2_initial_connection_window_size { + svc = svc.h2_initial_connection_window_size(val); + } + if let Some(handler) = on_connect_fn.clone() { svc = svc .on_connect_ext(move |io: &_, ext: _| (handler)(io as &dyn Any, ext)); @@ -914,6 +985,14 @@ where svc = svc.tcp_nodelay(enabled); } + if let Some(val) = c.h2_initial_window_size { + svc = svc.h2_initial_window_size(val); + } + + if let Some(val) = c.h2_initial_connection_window_size { + svc = svc.h2_initial_connection_window_size(val); + } + if let Some(handler) = on_connect_fn.clone() { svc = svc .on_connect_ext(move |io: &_, ext: _| (handler)(io as &dyn Any, ext)); @@ -985,6 +1064,14 @@ where svc = svc.tcp_nodelay(enabled); } + if let Some(val) = c.h2_initial_window_size { + svc = svc.h2_initial_window_size(val); + } + + if let Some(val) = c.h2_initial_connection_window_size { + svc = svc.h2_initial_connection_window_size(val); + } + if let Some(handler) = on_connect_fn.clone() { svc = svc .on_connect_ext(move |io: &_, ext: _| (handler)(io as &dyn Any, ext));