Skip to main content

ntex_h2/
config.rs

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