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}