iota_http/
config.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2025 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::time::Duration;
6
7const DEFAULT_HTTP2_KEEPALIVE_TIMEOUT_SECS: u64 = 20;
8
9#[derive(Debug, Clone)]
10pub struct Config {
11    init_stream_window_size: Option<u32>,
12    init_connection_window_size: Option<u32>,
13    max_concurrent_streams: Option<u32>,
14    pub(crate) tcp_keepalive: Option<Duration>,
15    pub(crate) tcp_nodelay: bool,
16    http2_keepalive_interval: Option<Duration>,
17    http2_keepalive_timeout: Option<Duration>,
18    http2_adaptive_window: Option<bool>,
19    http2_max_pending_accept_reset_streams: Option<usize>,
20    http2_max_header_list_size: Option<u32>,
21    max_frame_size: Option<u32>,
22    pub(crate) accept_http1: bool,
23    enable_connect_protocol: bool,
24    pub(crate) max_connection_age: Option<Duration>,
25    pub(crate) allow_insecure: bool,
26}
27
28impl Default for Config {
29    fn default() -> Self {
30        Self {
31            init_stream_window_size: None,
32            init_connection_window_size: None,
33            max_concurrent_streams: None,
34            tcp_keepalive: None,
35            tcp_nodelay: true,
36            http2_keepalive_interval: None,
37            http2_keepalive_timeout: None,
38            http2_adaptive_window: None,
39            http2_max_pending_accept_reset_streams: None,
40            http2_max_header_list_size: None,
41            max_frame_size: None,
42            accept_http1: true,
43            enable_connect_protocol: true,
44            max_connection_age: None,
45            allow_insecure: false,
46        }
47    }
48}
49
50impl Config {
51    /// Sets the [`SETTINGS_INITIAL_WINDOW_SIZE`][spec] option for HTTP2
52    /// stream-level flow control.
53    ///
54    /// Default is 65,535
55    ///
56    /// [spec]: https://httpwg.org/specs/rfc9113.html#InitialWindowSize
57    pub fn initial_stream_window_size(self, sz: impl Into<Option<u32>>) -> Self {
58        Self {
59            init_stream_window_size: sz.into(),
60            ..self
61        }
62    }
63
64    /// Sets the max connection-level flow control for HTTP2
65    ///
66    /// Default is 65,535
67    pub fn initial_connection_window_size(self, sz: impl Into<Option<u32>>) -> Self {
68        Self {
69            init_connection_window_size: sz.into(),
70            ..self
71        }
72    }
73
74    /// Sets the [`SETTINGS_MAX_CONCURRENT_STREAMS`][spec] option for HTTP2
75    /// connections.
76    ///
77    /// Default is no limit (`None`).
78    ///
79    /// [spec]: https://httpwg.org/specs/rfc9113.html#n-stream-concurrency
80    pub fn max_concurrent_streams(self, max: impl Into<Option<u32>>) -> Self {
81        Self {
82            max_concurrent_streams: max.into(),
83            ..self
84        }
85    }
86
87    /// Sets the maximum time option in milliseconds that a connection may exist
88    ///
89    /// Default is no limit (`None`).
90    pub fn max_connection_age(self, max_connection_age: Duration) -> Self {
91        Self {
92            max_connection_age: Some(max_connection_age),
93            ..self
94        }
95    }
96
97    /// Set whether HTTP2 Ping frames are enabled on accepted connections.
98    ///
99    /// If `None` is specified, HTTP2 keepalive is disabled, otherwise the
100    /// duration specified will be the time interval between HTTP2 Ping
101    /// frames. The timeout for receiving an acknowledgement of the
102    /// keepalive ping can be set with [`Config::http2_keepalive_timeout`].
103    ///
104    /// Default is no HTTP2 keepalive (`None`)
105    pub fn http2_keepalive_interval(self, http2_keepalive_interval: Option<Duration>) -> Self {
106        Self {
107            http2_keepalive_interval,
108            ..self
109        }
110    }
111
112    /// Sets a timeout for receiving an acknowledgement of the keepalive ping.
113    ///
114    /// If the ping is not acknowledged within the timeout, the connection will
115    /// be closed. Does nothing if http2_keep_alive_interval is disabled.
116    ///
117    /// Default is 20 seconds.
118    pub fn http2_keepalive_timeout(self, http2_keepalive_timeout: Option<Duration>) -> Self {
119        Self {
120            http2_keepalive_timeout,
121            ..self
122        }
123    }
124
125    /// Sets whether to use an adaptive flow control. Defaults to false.
126    /// Enabling this will override the limits set in
127    /// http2_initial_stream_window_size and
128    /// http2_initial_connection_window_size.
129    pub fn http2_adaptive_window(self, enabled: Option<bool>) -> Self {
130        Self {
131            http2_adaptive_window: enabled,
132            ..self
133        }
134    }
135
136    /// Configures the maximum number of pending reset streams allowed before a
137    /// GOAWAY will be sent.
138    ///
139    /// This will default to whatever the default in h2 is. As of v0.3.17, it is
140    /// 20.
141    ///
142    /// See <https://github.com/hyperium/hyper/issues/2877> for more information.
143    pub fn http2_max_pending_accept_reset_streams(self, max: Option<usize>) -> Self {
144        Self {
145            http2_max_pending_accept_reset_streams: max,
146            ..self
147        }
148    }
149
150    /// Set whether TCP keepalive messages are enabled on accepted connections.
151    ///
152    /// If `None` is specified, keepalive is disabled, otherwise the duration
153    /// specified will be the time to remain idle before sending TCP keepalive
154    /// probes.
155    ///
156    /// Default is no keepalive (`None`)
157    pub fn tcp_keepalive(self, tcp_keepalive: Option<Duration>) -> Self {
158        Self {
159            tcp_keepalive,
160            ..self
161        }
162    }
163
164    /// Set the value of `TCP_NODELAY` option for accepted connections. Enabled
165    /// by default.
166    pub fn tcp_nodelay(self, enabled: bool) -> Self {
167        Self {
168            tcp_nodelay: enabled,
169            ..self
170        }
171    }
172
173    /// Sets the max size of received header frames.
174    ///
175    /// This will default to whatever the default in hyper is. As of v1.4.1, it
176    /// is 16 KiB.
177    pub fn http2_max_header_list_size(self, max: impl Into<Option<u32>>) -> Self {
178        Self {
179            http2_max_header_list_size: max.into(),
180            ..self
181        }
182    }
183
184    /// Sets the maximum frame size to use for HTTP2.
185    ///
186    /// Passing `None` will do nothing.
187    ///
188    /// If not set, will default from underlying transport.
189    pub fn max_frame_size(self, frame_size: impl Into<Option<u32>>) -> Self {
190        Self {
191            max_frame_size: frame_size.into(),
192            ..self
193        }
194    }
195
196    /// Allow this accepting http1 requests.
197    ///
198    /// Default is `true`.
199    pub fn accept_http1(self, accept_http1: bool) -> Self {
200        Config {
201            accept_http1,
202            ..self
203        }
204    }
205
206    /// Allow accepting insecure connections when a tls_config is provided.
207    ///
208    /// This will allow clients to connect both using TLS as well as without TLS
209    /// on the same network interface.
210    ///
211    /// Default is `false`.
212    ///
213    /// NOTE: This presently will only work for `tokio::net::TcpStream` IO
214    /// connections
215    pub fn allow_insecure(self, allow_insecure: bool) -> Self {
216        Config {
217            allow_insecure,
218            ..self
219        }
220    }
221
222    pub(crate) fn connection_builder(
223        &self,
224    ) -> hyper_util::server::conn::auto::Builder<hyper_util::rt::TokioExecutor> {
225        let mut builder =
226            hyper_util::server::conn::auto::Builder::new(hyper_util::rt::TokioExecutor::new());
227
228        if !self.accept_http1 {
229            builder = builder.http2_only();
230        }
231
232        if self.enable_connect_protocol {
233            builder.http2().enable_connect_protocol();
234        }
235
236        let http2_keepalive_timeout = self
237            .http2_keepalive_timeout
238            .unwrap_or_else(|| Duration::new(DEFAULT_HTTP2_KEEPALIVE_TIMEOUT_SECS, 0));
239
240        builder
241            .http2()
242            .timer(hyper_util::rt::TokioTimer::new())
243            .initial_connection_window_size(self.init_connection_window_size)
244            .initial_stream_window_size(self.init_stream_window_size)
245            .max_concurrent_streams(self.max_concurrent_streams)
246            .keep_alive_interval(self.http2_keepalive_interval)
247            .keep_alive_timeout(http2_keepalive_timeout)
248            .adaptive_window(self.http2_adaptive_window.unwrap_or_default())
249            .max_pending_accept_reset_streams(self.http2_max_pending_accept_reset_streams)
250            .max_frame_size(self.max_frame_size);
251
252        if let Some(max_header_list_size) = self.http2_max_header_list_size {
253            builder.http2().max_header_list_size(max_header_list_size);
254        }
255
256        builder
257    }
258}