From bff977b73ca8d737f5492c86c09fd64735c45461 Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Wed, 8 Dec 2021 15:03:00 -0800 Subject: [PATCH] feat(http2): add `http2_max_send_buf_size` option to client and server This value is like a high-water mark. It applies per stream. Once a stream has buffered that amount of bytes to send, it won't poll more data from the `HttpBody` until the stream has been able to flush under it. --- Cargo.toml | 2 +- src/client/client.rs | 14 ++++++++++++++ src/client/conn.rs | 15 +++++++++++++++ src/proto/h2/client.rs | 4 ++++ src/proto/h2/server.rs | 6 +++++- src/server/conn.rs | 15 +++++++++++++++ src/server/server.rs | 14 ++++++++++++++ 7 files changed, 68 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 06a6863624..a71bf2af7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ http = "0.2" http-body = "0.4" httpdate = "1.0" httparse = "1.5.1" -h2 = { version = "0.3.3", optional = true } +h2 = { version = "0.3.9", optional = true } itoa = "0.4.1" tracing = { version = "0.1", default-features = false, features = ["std"] } pin-project-lite = "0.2.4" diff --git a/src/client/client.rs b/src/client/client.rs index 6d9d678702..3c1a843090 100644 --- a/src/client/client.rs +++ b/src/client/client.rs @@ -1223,6 +1223,20 @@ impl Builder { self } + /// Set the maximum write buffer size for each HTTP/2 stream. + /// + /// Default is currently 1MB, but may change. + /// + /// # Panics + /// + /// The value must be no larger than `u32::MAX`. + #[cfg(feature = "http2")] + #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] + pub fn http2_max_send_buf_size(&mut self, max: usize) -> &mut Self { + self.conn_builder.http2_max_send_buf_size(max); + self + } + /// Set whether to retry requests that get disrupted before ever starting /// to write. /// diff --git a/src/client/conn.rs b/src/client/conn.rs index 5f087a55e1..1273edabf5 100644 --- a/src/client/conn.rs +++ b/src/client/conn.rs @@ -837,6 +837,21 @@ impl Builder { self } + /// Set the maximum write buffer size for each HTTP/2 stream. + /// + /// Default is currently 1MB, but may change. + /// + /// # Panics + /// + /// The value must be no larger than `u32::MAX`. + #[cfg(feature = "http2")] + #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] + pub fn http2_max_send_buf_size(&mut self, max: usize) -> &mut Self { + assert!(max <= std::u32::MAX as usize); + self.h2_builder.max_send_buffer_size = max; + self + } + /// Constructs a connection with the configured options and IO. /// See [`client::conn`](crate::client::conn) for more. /// diff --git a/src/proto/h2/client.rs b/src/proto/h2/client.rs index ae20c8515b..809d8b8505 100644 --- a/src/proto/h2/client.rs +++ b/src/proto/h2/client.rs @@ -36,6 +36,7 @@ type ConnEof = oneshot::Receiver; const DEFAULT_CONN_WINDOW: u32 = 1024 * 1024 * 5; // 5mb const DEFAULT_STREAM_WINDOW: u32 = 1024 * 1024 * 2; // 2mb const DEFAULT_MAX_FRAME_SIZE: u32 = 1024 * 16; // 16kb +const DEFAULT_MAX_SEND_BUF_SIZE: usize = 1024 * 1024; // 1mb #[derive(Clone, Debug)] pub(crate) struct Config { @@ -50,6 +51,7 @@ pub(crate) struct Config { #[cfg(feature = "runtime")] pub(crate) keep_alive_while_idle: bool, pub(crate) max_concurrent_reset_streams: Option, + pub(crate) max_send_buffer_size: usize, } impl Default for Config { @@ -66,6 +68,7 @@ impl Default for Config { #[cfg(feature = "runtime")] keep_alive_while_idle: false, max_concurrent_reset_streams: None, + max_send_buffer_size: DEFAULT_MAX_SEND_BUF_SIZE, } } } @@ -76,6 +79,7 @@ fn new_builder(config: &Config) -> Builder { .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_send_buffer_size(config.max_send_buffer_size) .enable_push(false); if let Some(max) = config.max_concurrent_reset_streams { builder.max_concurrent_reset_streams(max); diff --git a/src/proto/h2/server.rs b/src/proto/h2/server.rs index c9c1380d54..94452fb4bb 100644 --- a/src/proto/h2/server.rs +++ b/src/proto/h2/server.rs @@ -33,6 +33,7 @@ use crate::{Body, Response}; const DEFAULT_CONN_WINDOW: u32 = 1024 * 1024; // 1mb const DEFAULT_STREAM_WINDOW: u32 = 1024 * 1024; // 1mb const DEFAULT_MAX_FRAME_SIZE: u32 = 1024 * 16; // 16kb +const DEFAULT_MAX_SEND_BUF_SIZE: usize = 1024 * 400; // 400kb #[derive(Clone, Debug)] pub(crate) struct Config { @@ -45,6 +46,7 @@ pub(crate) struct Config { pub(crate) keep_alive_interval: Option, #[cfg(feature = "runtime")] pub(crate) keep_alive_timeout: Duration, + pub(crate) max_send_buffer_size: usize, } impl Default for Config { @@ -59,6 +61,7 @@ impl Default for Config { keep_alive_interval: None, #[cfg(feature = "runtime")] keep_alive_timeout: Duration::from_secs(20), + max_send_buffer_size: DEFAULT_MAX_SEND_BUF_SIZE, } } } @@ -109,7 +112,8 @@ where builder .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_frame_size(config.max_frame_size) + .max_send_buffer_size(config.max_send_buffer_size); if let Some(max) = config.max_concurrent_streams { builder.max_concurrent_streams(max); } diff --git a/src/server/conn.rs b/src/server/conn.rs index c49c8ae571..eb9d847788 100644 --- a/src/server/conn.rs +++ b/src/server/conn.rs @@ -543,6 +543,21 @@ impl Http { self } + /// Set the maximum write buffer size for each HTTP/2 stream. + /// + /// Default is currently ~400KB, but may change. + /// + /// # Panics + /// + /// The value must be no larger than `u32::MAX`. + #[cfg(feature = "http2")] + #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] + pub fn http2_max_send_buf_size(&mut self, max: usize) -> &mut Self { + assert!(max <= std::u32::MAX as usize); + self.h2_builder.max_send_buffer_size = max; + self + } + /// Set the maximum buffer size for the connection. /// /// Default is ~400kb. diff --git a/src/server/server.rs b/src/server/server.rs index 87027bbefc..9cfb8157bc 100644 --- a/src/server/server.rs +++ b/src/server/server.rs @@ -439,6 +439,20 @@ impl Builder { self } + /// Set the maximum write buffer size for each HTTP/2 stream. + /// + /// Default is currently ~400KB, but may change. + /// + /// # Panics + /// + /// The value must be no larger than `u32::MAX`. + #[cfg(feature = "http2")] + #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] + pub fn http2_max_send_buf_size(mut self, max: usize) -> Self { + self.protocol.http2_max_send_buf_size(max); + self + } + /// Sets the `Executor` to deal with connection tasks. /// /// Default is `tokio::spawn`.