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(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}