From d6c5376dea5011e879ace382737d981f33769453 Mon Sep 17 00:00:00 2001 From: "Nathan (Blaise) Bruer" Date: Tue, 31 Oct 2023 17:27:35 -0500 Subject: [PATCH] Add Http2 flags for advanced configurations Some users will want to tune advanced http2 configurations. This PR gives that ability. --- cas/cas_main.rs | 41 ++++++++++++++++++++++++++++++++++++++++- config/cas_server.rs | 40 +++++++++++++++++++++++++++++++++++++++- util/serde_utils.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 2 deletions(-) diff --git a/cas/cas_main.rs b/cas/cas_main.rs index 3a88e50f6..f4697b2d9 100644 --- a/cas/cas_main.rs +++ b/cas/cas_main.rs @@ -426,7 +426,46 @@ async fn inner_main(cfg: CasConfig, server_start_timestamp: u64) -> Result<(), B let socket_addr = server_cfg.listen_address.parse::()?; let tcp_listener = TcpListener::bind(&socket_addr).await?; - let http = Http::new(); + let mut http = Http::new(); + let http_config = &server_cfg.advanced_http; + if let Some(value) = http_config.http2_max_pending_accept_reset_streams { + http.http2_max_pending_accept_reset_streams( + usize::try_from(value).err_tip(|| "Could not convert http2_max_pending_accept_reset_streams")?, + ); + } + if let Some(value) = http_config.http2_initial_stream_window_size { + http.http2_initial_stream_window_size(value); + } + if let Some(value) = http_config.http2_initial_connection_window_size { + http.http2_initial_connection_window_size(value); + } + if let Some(value) = http_config.http2_adaptive_window { + http.http2_adaptive_window(value); + } + if let Some(value) = http_config.http2_max_frame_size { + http.http2_max_frame_size(value); + } + if let Some(value) = http_config.http2_max_concurrent_streams { + http.http2_max_concurrent_streams(value); + } + if let Some(value) = http_config.http2_keep_alive_interval { + http.http2_keep_alive_interval(Duration::from_secs(u64::from(value))); + } + if let Some(value) = http_config.http2_keep_alive_timeout { + http.http2_keep_alive_timeout(Duration::from_secs(u64::from(value))); + } + if let Some(value) = http_config.http2_max_send_buf_size { + http.http2_max_send_buf_size( + usize::try_from(value).err_tip(|| "Could not convert http2_max_send_buf_size")?, + ); + } + if let Some(true) = http_config.http2_enable_connect_protocol { + http.http2_enable_connect_protocol(); + } + if let Some(value) = http_config.http2_max_header_list_size { + http.http2_max_header_list_size(value); + } + log::warn!("Ready, listening on {}", socket_addr); root_futures.push(Box::pin(async move { loop { diff --git a/config/cas_server.rs b/config/cas_server.rs index c280996d2..670865d63 100644 --- a/config/cas_server.rs +++ b/config/cas_server.rs @@ -15,7 +15,9 @@ use std::collections::HashMap; use serde::Deserialize; -use serde_utils::{convert_numeric_with_shellexpand, convert_string_with_shellexpand}; +use serde_utils::{ + convert_numeric_with_shellexpand, convert_optinoal_numeric_with_shellexpand, convert_string_with_shellexpand, +}; use crate::schedulers::SchedulerConfig; use crate::stores::{StoreConfig, StoreRefName}; @@ -200,6 +202,38 @@ pub struct TlsConfig { pub key_file: String, } +/// Advanced Http configurations. These are generally should not be set. +/// For documentation on what each of these do, see the hyper documentation: +/// See: https://docs.rs/hyper/latest/hyper/server/conn/struct.Http.html +/// +/// Note: All of these default to hyper's default values unless otherwise +/// specified. +#[derive(Deserialize, Debug, Default)] +pub struct HttpServerConfig { + #[serde(deserialize_with = "convert_optinoal_numeric_with_shellexpand")] + pub http2_max_pending_accept_reset_streams: Option, + #[serde(deserialize_with = "convert_optinoal_numeric_with_shellexpand")] + pub http2_initial_stream_window_size: Option, + #[serde(deserialize_with = "convert_optinoal_numeric_with_shellexpand")] + pub http2_initial_connection_window_size: Option, + pub http2_adaptive_window: Option, + #[serde(deserialize_with = "convert_optinoal_numeric_with_shellexpand")] + pub http2_max_frame_size: Option, + #[serde(deserialize_with = "convert_optinoal_numeric_with_shellexpand")] + pub http2_max_concurrent_streams: Option, + /// Note: This is in seconds. + #[serde(deserialize_with = "convert_optinoal_numeric_with_shellexpand")] + pub http2_keep_alive_interval: Option, + /// Note: This is in seconds. + #[serde(deserialize_with = "convert_optinoal_numeric_with_shellexpand")] + pub http2_keep_alive_timeout: Option, + #[serde(deserialize_with = "convert_optinoal_numeric_with_shellexpand")] + pub http2_max_send_buf_size: Option, + pub http2_enable_connect_protocol: Option, + #[serde(deserialize_with = "convert_optinoal_numeric_with_shellexpand")] + pub http2_max_header_list_size: Option, +} + #[derive(Deserialize, Debug)] pub struct ServerConfig { /// Name of the server. This is used to help identify the service @@ -218,6 +252,10 @@ pub struct ServerConfig { #[serde(default)] pub compression: CompressionConfig, + /// Advanced Http server configuration. + #[serde(default)] + pub advanced_http: HttpServerConfig, + /// Services to attach to server. pub services: Option, diff --git a/util/serde_utils.rs b/util/serde_utils.rs index b1d99c0d5..7887818d8 100644 --- a/util/serde_utils.rs +++ b/util/serde_utils.rs @@ -57,6 +57,49 @@ where deserializer.deserialize_any(USizeVisitor::(PhantomData:: {})) } +/// Same as convert_numeric_with_shellexpand, but supports Option. +pub fn convert_optinoal_numeric_with_shellexpand<'de, D, T, E>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, + E: fmt::Display, + T: TryFrom + FromStr, + >::Error: fmt::Display, +{ + // define a visitor that deserializes + // `ActualData` encoded as json within a string + struct USizeVisitor>(PhantomData); + + impl<'de, T, FromStrErr> de::Visitor<'de> for USizeVisitor + where + FromStrErr: fmt::Display, + T: TryFrom + FromStr, + >::Error: fmt::Display, + { + type Value = Option; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string containing json data") + } + + fn visit_i64(self, v: i64) -> Result { + Ok(Some(v.try_into().map_err(de::Error::custom)?)) + } + + fn visit_str(self, v: &str) -> Result { + if v.is_empty() { + return Ok(None); + } + Ok(Some( + (*shellexpand::env(v).map_err(de::Error::custom)?) + .parse::() + .map_err(de::Error::custom)?, + )) + } + } + + deserializer.deserialize_any(USizeVisitor::(PhantomData:: {})) +} + /// Helper for serde macro so you can use shellexpand variables in the json configuration /// files when the number is a numeric type. pub fn convert_string_with_shellexpand<'de, D: Deserializer<'de>>(deserializer: D) -> Result {