ntex_h2/
config.rs

1use std::{cell::Cell, time::Duration};
2
3use ntex_service::cfg::{CfgContext, Configuration};
4use ntex_util::time::Seconds;
5
6use crate::{consts, frame, frame::Settings, frame::WindowSize};
7
8#[derive(Copy, Clone, Debug)]
9/// Http2 connection configuration
10pub struct ServiceConfig {
11    pub(crate) settings: Settings,
12    /// Initial window size of locally initiated streams
13    pub(crate) window_sz: WindowSize,
14    pub(crate) window_sz_threshold: WindowSize,
15    /// How long a locally reset stream should ignore frames
16    pub(crate) reset_duration: Duration,
17    /// Maximum number of locally reset streams to keep at a time
18    pub(crate) reset_max: usize,
19    /// Initial window size for new connections.
20    pub(crate) connection_window_sz: WindowSize,
21    pub(crate) connection_window_sz_threshold: WindowSize,
22    /// Maximum number of remote initiated streams
23    pub(crate) remote_max_concurrent_streams: Option<u32>,
24    /// Limit number of continuation frames for headers
25    pub(crate) max_header_continuations: usize,
26    // /// If extended connect protocol is enabled.
27    // pub extended_connect_protocol_enabled: bool,
28    /// Connection timeouts
29    pub(crate) handshake_timeout: Seconds,
30    pub(crate) ping_timeout: Seconds,
31
32    config: CfgContext,
33}
34
35impl Default for ServiceConfig {
36    fn default() -> Self {
37        ServiceConfig::new()
38    }
39}
40
41impl Configuration for ServiceConfig {
42    const NAME: &str = "Http/2 service configuration";
43
44    fn ctx(&self) -> &CfgContext {
45        &self.config
46    }
47
48    fn set_ctx(&mut self, ctx: CfgContext) {
49        self.config = ctx;
50    }
51}
52
53#[allow(clippy::new_without_default)]
54impl ServiceConfig {
55    /// Create configuration
56    pub fn new() -> Self {
57        let window_sz = frame::DEFAULT_INITIAL_WINDOW_SIZE;
58        let window_sz_threshold = ((frame::DEFAULT_INITIAL_WINDOW_SIZE as f32) / 3.0) as u32;
59        let connection_window_sz = consts::DEFAULT_CONNECTION_WINDOW_SIZE;
60        let connection_window_sz_threshold =
61            ((consts::DEFAULT_CONNECTION_WINDOW_SIZE as f32) / 4.0) as u32;
62
63        let mut settings = Settings::default();
64        settings.set_max_concurrent_streams(Some(256));
65        settings.set_enable_push(false);
66        settings.set_max_header_list_size(Some(consts::DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE));
67
68        ServiceConfig {
69            window_sz,
70            window_sz_threshold,
71            connection_window_sz,
72            connection_window_sz_threshold,
73            settings,
74            reset_max: consts::DEFAULT_RESET_STREAM_MAX,
75            reset_duration: consts::DEFAULT_RESET_STREAM_SECS.into(),
76            remote_max_concurrent_streams: Some(256),
77            max_header_continuations: consts::DEFAULT_MAX_COUNTINUATIONS,
78            handshake_timeout: Seconds(5),
79            ping_timeout: Seconds(10),
80            config: CfgContext::default(),
81        }
82    }
83}
84
85impl ServiceConfig {
86    /// Indicates the initial window size (in octets) for stream-level
87    /// flow control for received data.
88    ///
89    /// The initial window of a stream is used as part of flow control. For more
90    /// details, see [`FlowControl`].
91    ///
92    /// The default value is 65,535.
93    pub fn initial_window_size(mut self, size: u32) -> Self {
94        self.window_sz = size;
95        self.window_sz_threshold = ((size as f32) / 3.0) as u32;
96        self.settings.set_initial_window_size(Some(size));
97        self
98    }
99
100    /// Indicates the initial window size (in octets) for connection-level flow control
101    /// for received data.
102    ///
103    /// The initial window of a connection is used as part of flow control. For more details,
104    /// see [`FlowControl`].
105    ///
106    /// The default value is 1Mb.
107    ///
108    /// [`FlowControl`]: ../struct.FlowControl.html
109    pub fn initial_connection_window_size(mut self, size: u32) -> Self {
110        assert!(size <= consts::MAX_WINDOW_SIZE);
111        self.connection_window_sz = size;
112        self.connection_window_sz_threshold = ((size as f32) / 4.0) as u32;
113        self
114    }
115
116    /// Indicates the size (in octets) of the largest HTTP/2 frame payload that the
117    /// configured server is able to accept.
118    ///
119    /// The sender may send data frames that are **smaller** than this value,
120    /// but any data larger than `max` will be broken up into multiple `DATA`
121    /// frames.
122    ///
123    /// The value **must** be between 16,384 and 16,777,215. The default value is 16,384.
124    ///
125    /// # Panics
126    ///
127    /// This function panics if `max` is not within the legal range specified
128    /// above.
129    pub fn max_frame_size(mut self, max: u32) -> Self {
130        self.settings.set_max_frame_size(max);
131        self
132    }
133
134    /// Sets the max size of received header frames.
135    ///
136    /// This advisory setting informs a peer of the maximum size of header list
137    /// that the sender is prepared to accept, in octets. The value is based on
138    /// the uncompressed size of header fields, including the length of the name
139    /// and value in octets plus an overhead of 32 octets for each header field.
140    ///
141    /// This setting is also used to limit the maximum amount of data that is
142    /// buffered to decode HEADERS frames.
143    ///
144    /// By default value is set to 48Kb.
145    pub fn max_header_list_size(mut self, max: u32) -> Self {
146        self.settings.set_max_header_list_size(Some(max));
147        self
148    }
149
150    /// Sets the max number of continuation frames for HEADERS
151    ///
152    /// By default value is set to 5
153    pub fn max_header_continuation_frames(mut self, max: usize) -> Self {
154        self.max_header_continuations = max;
155        self
156    }
157
158    /// Sets the maximum number of concurrent streams.
159    ///
160    /// The maximum concurrent streams setting only controls the maximum number
161    /// of streams that can be initiated by the remote peer. In other words,
162    /// when this setting is set to 100, this does not limit the number of
163    /// concurrent streams that can be created by the caller.
164    ///
165    /// It is recommended that this value be no smaller than 100, so as to not
166    /// unnecessarily limit parallelism. However, any value is legal, including
167    /// 0. If `max` is set to 0, then the remote will not be permitted to
168    /// initiate streams.
169    ///
170    /// Note that streams in the reserved state, i.e., push promises that have
171    /// been reserved but the stream has not started, do not count against this
172    /// setting.
173    ///
174    /// Also note that if the remote *does* exceed the value set here, it is not
175    /// a protocol level error. Instead, the `h2` library will immediately reset
176    /// the stream.
177    ///
178    /// See [Section 5.1.2] in the HTTP/2 spec for more details.
179    ///
180    /// [Section 5.1.2]: https://http2.github.io/http2-spec/#rfc.section.5.1.2
181    pub fn max_concurrent_streams(mut self, max: u32) -> Self {
182        self.remote_max_concurrent_streams = Some(max);
183        self.settings.set_max_concurrent_streams(Some(max));
184        self
185    }
186
187    /// Sets the maximum number of concurrent locally reset streams.
188    ///
189    /// When a stream is explicitly reset by either calling
190    /// [`SendResponse::send_reset`] or by dropping a [`SendResponse`] instance
191    /// before completing the stream, the HTTP/2 specification requires that
192    /// any further frames received for that stream must be ignored for "some
193    /// time".
194    ///
195    /// In order to satisfy the specification, internal state must be maintained
196    /// to implement the behavior. This state grows linearly with the number of
197    /// streams that are locally reset.
198    ///
199    /// The `max_concurrent_reset_streams` setting configures sets an upper
200    /// bound on the amount of state that is maintained. When this max value is
201    /// reached, the oldest reset stream is purged from memory.
202    ///
203    /// Once the stream has been fully purged from memory, any additional frames
204    /// received for that stream will result in a connection level protocol
205    /// error, forcing the connection to terminate.
206    ///
207    /// The default value is 32.
208    pub fn max_concurrent_reset_streams(mut self, val: usize) -> Self {
209        self.reset_max = val;
210        self
211    }
212
213    /// Sets the maximum number of concurrent locally reset streams.
214    ///
215    /// When a stream is explicitly reset by either calling
216    /// [`SendResponse::send_reset`] or by dropping a [`SendResponse`] instance
217    /// before completing the stream, the HTTP/2 specification requires that
218    /// any further frames received for that stream must be ignored for "some
219    /// time".
220    ///
221    /// In order to satisfy the specification, internal state must be maintained
222    /// to implement the behavior. This state grows linearly with the number of
223    /// streams that are locally reset.
224    ///
225    /// The `reset_stream_duration` setting configures the max amount of time
226    /// this state will be maintained in memory. Once the duration elapses, the
227    /// stream state is purged from memory.
228    ///
229    /// Once the stream has been fully purged from memory, any additional frames
230    /// received for that stream will result in a connection level protocol
231    /// error, forcing the connection to terminate.
232    ///
233    /// The default value is 30 seconds.
234    pub fn reset_stream_duration(mut self, dur: Seconds) -> Self {
235        self.reset_duration = dur.into();
236        self
237    }
238
239    // /// Enables the [extended CONNECT protocol].
240    // ///
241    // /// [extended CONNECT protocol]: https://datatracker.ietf.org/doc/html/rfc8441#section-4
242    // pub fn enable_connect_protocol(&self) -> &Self {
243    //     let mut s = self.0.settings.get();
244    //     s.set_enable_connect_protocol(Some(1));
245    //     self.0.settings.set(s);
246    //     self
247    // }
248
249    /// Set handshake timeout.
250    ///
251    /// Hadnshake includes receiving preface and completing connection preparation.
252    ///
253    /// By default handshake timeuot is 5 seconds.
254    pub fn handshake_timeout(mut self, timeout: Seconds) -> Self {
255        self.handshake_timeout = timeout;
256        self
257    }
258
259    /// Set ping timeout.
260    ///
261    /// By default ping time-out is set to 60 seconds.
262    pub fn ping_timeout(mut self, timeout: Seconds) -> Self {
263        self.ping_timeout = timeout;
264        self
265    }
266}
267
268thread_local! {
269    static SHUTDOWN: Cell<bool> = const { Cell::new(false) };
270}
271
272// Current limitation, shutdown is thread global
273impl ServiceConfig {
274    /// Check if service is shutting down.
275    pub fn is_shutdown(&self) -> bool {
276        SHUTDOWN.with(|v| v.get())
277    }
278
279    /// Set service shutdown.
280    pub fn shutdown() {
281        SHUTDOWN.with(|v| v.set(true));
282    }
283}