From 4c84e8c1c26a1464221de96b9f39816ce7251a5f Mon Sep 17 00:00:00 2001 From: 0x676e67 Date: Sat, 17 Aug 2024 02:51:37 +0800 Subject: [PATCH] feat(client): Add HTTP/2 builder options `header_table_size()` and `max_concurrent_streams()` (#3731) --- src/client/conn/http2.rs | 48 +++++++++++++++++++++++++++++++++++----- src/proto/h2/client.rs | 18 ++++++++++++--- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/src/client/conn/http2.rs b/src/client/conn/http2.rs index aee135f672..ce86c6dcb4 100644 --- a/src/client/conn/http2.rs +++ b/src/client/conn/http2.rs @@ -337,13 +337,9 @@ where /// Sets the maximum frame size to use for HTTP2. /// - /// Passing `None` will do nothing. - /// - /// If not set, hyper will use a default. + /// Default is currently 16KB, but can change. pub fn max_frame_size(&mut self, sz: impl Into>) -> &mut Self { - if let Some(sz) = sz.into() { - self.h2_builder.max_frame_size = sz; - } + self.h2_builder.max_frame_size = sz.into(); self } @@ -355,6 +351,46 @@ where self } + /// Sets the header table size. + /// + /// This setting informs the peer of the maximum size of the header compression + /// table used to encode header blocks, in octets. The encoder may select any value + /// equal to or less than the header table size specified by the sender. + /// + /// The default value of crate `h2` is 4,096. + pub fn header_table_size(&mut self, size: impl Into>) -> &mut Self { + self.h2_builder.header_table_size = size.into(); + self + } + + /// Sets the maximum number of concurrent streams. + /// + /// The maximum concurrent streams setting only controls the maximum number + /// of streams that can be initiated by the remote peer. In other words, + /// when this setting is set to 100, this does not limit the number of + /// concurrent streams that can be created by the caller. + /// + /// It is recommended that this value be no smaller than 100, so as to not + /// unnecessarily limit parallelism. However, any value is legal, including + /// 0. If `max` is set to 0, then the remote will not be permitted to + /// initiate streams. + /// + /// Note that streams in the reserved state, i.e., push promises that have + /// been reserved but the stream has not started, do not count against this + /// setting. + /// + /// Also note that if the remote *does* exceed the value set here, it is not + /// a protocol level error. Instead, the `h2` library will immediately reset + /// the stream. + /// + /// See [Section 5.1.2] in the HTTP/2 spec for more details. + /// + /// [Section 5.1.2]: https://http2.github.io/http2-spec/#rfc.section.5.1.2 + pub fn max_concurrent_streams(&mut self, max: impl Into>) -> &mut Self { + self.h2_builder.max_concurrent_streams = max.into(); + self + } + /// Sets an interval for HTTP2 Ping frames should be sent to keep a /// connection alive. /// diff --git a/src/proto/h2/client.rs b/src/proto/h2/client.rs index d2430cbf99..5e9641e408 100644 --- a/src/proto/h2/client.rs +++ b/src/proto/h2/client.rs @@ -68,7 +68,7 @@ pub(crate) struct Config { pub(crate) initial_conn_window_size: u32, pub(crate) initial_stream_window_size: u32, pub(crate) initial_max_send_streams: usize, - pub(crate) max_frame_size: u32, + pub(crate) max_frame_size: Option, pub(crate) max_header_list_size: u32, pub(crate) keep_alive_interval: Option, pub(crate) keep_alive_timeout: Duration, @@ -76,6 +76,8 @@ pub(crate) struct Config { pub(crate) max_concurrent_reset_streams: Option, pub(crate) max_send_buffer_size: usize, pub(crate) max_pending_accept_reset_streams: Option, + pub(crate) header_table_size: Option, + pub(crate) max_concurrent_streams: Option, } impl Default for Config { @@ -85,7 +87,7 @@ impl Default for Config { initial_conn_window_size: DEFAULT_CONN_WINDOW, initial_stream_window_size: DEFAULT_STREAM_WINDOW, initial_max_send_streams: DEFAULT_INITIAL_MAX_SEND_STREAMS, - max_frame_size: DEFAULT_MAX_FRAME_SIZE, + max_frame_size: Some(DEFAULT_MAX_FRAME_SIZE), max_header_list_size: DEFAULT_MAX_HEADER_LIST_SIZE, keep_alive_interval: None, keep_alive_timeout: Duration::from_secs(20), @@ -93,6 +95,8 @@ impl Default for Config { max_concurrent_reset_streams: None, max_send_buffer_size: DEFAULT_MAX_SEND_BUF_SIZE, max_pending_accept_reset_streams: None, + header_table_size: None, + max_concurrent_streams: None, } } } @@ -103,16 +107,24 @@ fn new_builder(config: &Config) -> Builder { .initial_max_send_streams(config.initial_max_send_streams) .initial_window_size(config.initial_stream_window_size) .initial_connection_window_size(config.initial_conn_window_size) - .max_frame_size(config.max_frame_size) .max_header_list_size(config.max_header_list_size) .max_send_buffer_size(config.max_send_buffer_size) .enable_push(false); + if let Some(max) = config.max_frame_size { + builder.max_frame_size(max); + } if let Some(max) = config.max_concurrent_reset_streams { builder.max_concurrent_reset_streams(max); } if let Some(max) = config.max_pending_accept_reset_streams { builder.max_pending_accept_reset_streams(max); } + if let Some(size) = config.header_table_size { + builder.header_table_size(size); + } + if let Some(max) = config.max_concurrent_streams { + builder.max_concurrent_streams(max); + } builder }