/src/h2/src/frame/settings.rs
Line | Count | Source |
1 | | use std::fmt; |
2 | | |
3 | | use crate::frame::{util, Error, Frame, FrameSize, Head, Kind, StreamId}; |
4 | | use bytes::{BufMut, BytesMut}; |
5 | | |
6 | | #[derive(Clone, Default, Eq, PartialEq)] |
7 | | pub struct Settings { |
8 | | flags: SettingsFlags, |
9 | | // Fields |
10 | | header_table_size: Option<u32>, |
11 | | enable_push: Option<u32>, |
12 | | max_concurrent_streams: Option<u32>, |
13 | | initial_window_size: Option<u32>, |
14 | | max_frame_size: Option<u32>, |
15 | | max_header_list_size: Option<u32>, |
16 | | enable_connect_protocol: Option<u32>, |
17 | | } |
18 | | |
19 | | /// An enum that lists all valid settings that can be sent in a SETTINGS |
20 | | /// frame. |
21 | | /// |
22 | | /// Each setting has a value that is a 32 bit unsigned integer (6.5.1.). |
23 | | #[derive(Debug)] |
24 | | pub enum Setting { |
25 | | HeaderTableSize(u32), |
26 | | EnablePush(u32), |
27 | | MaxConcurrentStreams(u32), |
28 | | InitialWindowSize(u32), |
29 | | MaxFrameSize(u32), |
30 | | MaxHeaderListSize(u32), |
31 | | EnableConnectProtocol(u32), |
32 | | } |
33 | | |
34 | | #[derive(Copy, Clone, Eq, PartialEq, Default)] |
35 | | pub struct SettingsFlags(u8); |
36 | | |
37 | | const ACK: u8 = 0x1; |
38 | | const ALL: u8 = ACK; |
39 | | |
40 | | /// The default value of SETTINGS_HEADER_TABLE_SIZE |
41 | | pub const DEFAULT_SETTINGS_HEADER_TABLE_SIZE: usize = 4_096; |
42 | | |
43 | | /// The default value of SETTINGS_INITIAL_WINDOW_SIZE |
44 | | pub const DEFAULT_INITIAL_WINDOW_SIZE: u32 = 65_535; |
45 | | |
46 | | /// The default value of MAX_FRAME_SIZE |
47 | | pub const DEFAULT_MAX_FRAME_SIZE: FrameSize = 16_384; |
48 | | |
49 | | /// INITIAL_WINDOW_SIZE upper bound |
50 | | pub const MAX_INITIAL_WINDOW_SIZE: usize = (1 << 31) - 1; |
51 | | |
52 | | /// MAX_FRAME_SIZE upper bound |
53 | | pub const MAX_MAX_FRAME_SIZE: FrameSize = (1 << 24) - 1; |
54 | | |
55 | | // ===== impl Settings ===== |
56 | | |
57 | | impl Settings { |
58 | 7.30k | pub fn ack() -> Settings { |
59 | 7.30k | Settings { |
60 | 7.30k | flags: SettingsFlags::ack(), |
61 | 7.30k | ..Settings::default() |
62 | 7.30k | } |
63 | 7.30k | } |
64 | | |
65 | 7.38k | pub fn is_ack(&self) -> bool { |
66 | 7.38k | self.flags.is_ack() |
67 | 7.38k | } |
68 | | |
69 | 7.29k | pub fn initial_window_size(&self) -> Option<u32> { |
70 | 7.29k | self.initial_window_size |
71 | 7.29k | } |
72 | | |
73 | 0 | pub fn set_initial_window_size(&mut self, size: Option<u32>) { |
74 | 0 | self.initial_window_size = size; |
75 | 0 | } |
76 | | |
77 | 20.5k | pub fn max_concurrent_streams(&self) -> Option<u32> { |
78 | 20.5k | self.max_concurrent_streams |
79 | 20.5k | } |
80 | | |
81 | 0 | pub fn set_max_concurrent_streams(&mut self, max: Option<u32>) { |
82 | 0 | self.max_concurrent_streams = max; |
83 | 0 | } |
84 | | |
85 | 20.5k | pub fn max_frame_size(&self) -> Option<u32> { |
86 | 20.5k | self.max_frame_size |
87 | 20.5k | } |
88 | | |
89 | 0 | pub fn set_max_frame_size(&mut self, size: Option<u32>) { |
90 | 0 | if let Some(val) = size { |
91 | 0 | assert!(DEFAULT_MAX_FRAME_SIZE <= val && val <= MAX_MAX_FRAME_SIZE); |
92 | 0 | } |
93 | 0 | self.max_frame_size = size; |
94 | 0 | } |
95 | | |
96 | 13.2k | pub fn max_header_list_size(&self) -> Option<u32> { |
97 | 13.2k | self.max_header_list_size |
98 | 13.2k | } |
99 | | |
100 | 0 | pub fn set_max_header_list_size(&mut self, size: Option<u32>) { |
101 | 0 | self.max_header_list_size = size; |
102 | 0 | } |
103 | | |
104 | 20.5k | pub fn is_push_enabled(&self) -> Option<bool> { |
105 | 20.5k | self.enable_push.map(|val| val != 0) |
106 | 20.5k | } |
107 | | |
108 | 0 | pub fn set_enable_push(&mut self, enable: bool) { |
109 | 0 | self.enable_push = Some(enable as u32); |
110 | 0 | } |
111 | | |
112 | 20.5k | pub fn is_extended_connect_protocol_enabled(&self) -> Option<bool> { |
113 | 20.5k | self.enable_connect_protocol.map(|val| val != 0) |
114 | 20.5k | } |
115 | | |
116 | 0 | pub fn set_enable_connect_protocol(&mut self, val: Option<u32>) { |
117 | 0 | self.enable_connect_protocol = val; |
118 | 0 | } |
119 | | |
120 | 7.29k | pub fn header_table_size(&self) -> Option<u32> { |
121 | 7.29k | self.header_table_size |
122 | 7.29k | } |
123 | | |
124 | 0 | pub fn set_header_table_size(&mut self, size: Option<u32>) { |
125 | 0 | self.header_table_size = size; |
126 | 0 | } |
127 | | |
128 | 7.54k | pub fn load(head: Head, payload: &[u8]) -> Result<Settings, Error> { |
129 | | use self::Setting::*; |
130 | | |
131 | 7.54k | debug_assert_eq!(head.kind(), crate::frame::Kind::Settings); |
132 | | |
133 | 7.54k | if !head.stream_id().is_zero() { |
134 | 45 | return Err(Error::InvalidStreamId); |
135 | 7.49k | } |
136 | | |
137 | | // Load the flag |
138 | 7.49k | let flag = SettingsFlags::load(head.flag()); |
139 | | |
140 | 7.49k | if flag.is_ack() { |
141 | | // Ensure that the payload is empty |
142 | 10 | if !payload.is_empty() { |
143 | 2 | return Err(Error::InvalidPayloadLength); |
144 | 8 | } |
145 | | |
146 | | // Return the ACK frame |
147 | 8 | return Ok(Settings::ack()); |
148 | 7.48k | } |
149 | | |
150 | | // Ensure the payload length is correct, each setting is 6 bytes long. |
151 | 7.48k | if payload.len() % 6 != 0 { |
152 | 1 | tracing::debug!("invalid settings payload length; len={:?}", payload.len()); |
153 | 1 | return Err(Error::InvalidPayloadAckSettings); |
154 | 7.48k | } |
155 | | |
156 | 7.48k | let mut settings = Settings::default(); |
157 | 7.48k | debug_assert!(!settings.flags.is_ack()); |
158 | | |
159 | 33.5k | for raw in payload.chunks(6) { |
160 | 33.5k | match Setting::load(raw) { |
161 | 1.83k | Some(HeaderTableSize(val)) => { |
162 | 1.83k | settings.header_table_size = Some(val); |
163 | 1.83k | } |
164 | 596 | Some(EnablePush(val)) => match val { |
165 | 561 | 0 | 1 => { |
166 | 561 | settings.enable_push = Some(val); |
167 | 561 | } |
168 | | _ => { |
169 | 35 | return Err(Error::InvalidSettingValue); |
170 | | } |
171 | | }, |
172 | 617 | Some(MaxConcurrentStreams(val)) => { |
173 | 617 | settings.max_concurrent_streams = Some(val); |
174 | 617 | } |
175 | 4.41k | Some(InitialWindowSize(val)) => { |
176 | 4.41k | if val as usize > MAX_INITIAL_WINDOW_SIZE { |
177 | 9 | return Err(Error::InvalidSettingValue); |
178 | 4.40k | } else { |
179 | 4.40k | settings.initial_window_size = Some(val); |
180 | 4.40k | } |
181 | | } |
182 | 363 | Some(MaxFrameSize(val)) => { |
183 | 363 | if DEFAULT_MAX_FRAME_SIZE <= val && val <= MAX_MAX_FRAME_SIZE { |
184 | 337 | settings.max_frame_size = Some(val); |
185 | 337 | } else { |
186 | 26 | return Err(Error::InvalidSettingValue); |
187 | | } |
188 | | } |
189 | 287 | Some(MaxHeaderListSize(val)) => { |
190 | 287 | settings.max_header_list_size = Some(val); |
191 | 287 | } |
192 | 250 | Some(EnableConnectProtocol(val)) => match val { |
193 | 212 | 0 | 1 => { |
194 | 212 | settings.enable_connect_protocol = Some(val); |
195 | 212 | } |
196 | | _ => { |
197 | 38 | return Err(Error::InvalidSettingValue); |
198 | | } |
199 | | }, |
200 | 25.1k | None => {} |
201 | | } |
202 | | } |
203 | | |
204 | 7.37k | Ok(settings) |
205 | 7.54k | } |
206 | | |
207 | 20.5k | fn payload_len(&self) -> usize { |
208 | 20.5k | let mut len = 0; |
209 | 20.5k | self.for_each(|_| len += 6); |
210 | 20.5k | len |
211 | 20.5k | } |
212 | | |
213 | 20.5k | pub fn encode(&self, dst: &mut BytesMut) { |
214 | | // Create & encode an appropriate frame head |
215 | 20.5k | let head = Head::new(Kind::Settings, self.flags.into(), StreamId::zero()); |
216 | 20.5k | let payload_len = self.payload_len(); |
217 | | |
218 | 20.5k | tracing::trace!("encoding SETTINGS; len={}", payload_len); |
219 | | |
220 | 20.5k | head.encode(payload_len, dst); |
221 | | |
222 | | // Encode the settings |
223 | 20.5k | self.for_each(|setting| { |
224 | 0 | tracing::trace!("encoding setting; val={:?}", setting); |
225 | 0 | setting.encode(dst) |
226 | 0 | }); |
227 | 20.5k | } |
228 | | |
229 | 41.1k | fn for_each<F: FnMut(Setting)>(&self, mut f: F) { |
230 | | use self::Setting::*; |
231 | | |
232 | 41.1k | if let Some(v) = self.header_table_size { |
233 | 0 | f(HeaderTableSize(v)); |
234 | 41.1k | } |
235 | | |
236 | 41.1k | if let Some(v) = self.enable_push { |
237 | 0 | f(EnablePush(v)); |
238 | 41.1k | } |
239 | | |
240 | 41.1k | if let Some(v) = self.max_concurrent_streams { |
241 | 0 | f(MaxConcurrentStreams(v)); |
242 | 41.1k | } |
243 | | |
244 | 41.1k | if let Some(v) = self.initial_window_size { |
245 | 0 | f(InitialWindowSize(v)); |
246 | 41.1k | } |
247 | | |
248 | 41.1k | if let Some(v) = self.max_frame_size { |
249 | 0 | f(MaxFrameSize(v)); |
250 | 41.1k | } |
251 | | |
252 | 41.1k | if let Some(v) = self.max_header_list_size { |
253 | 0 | f(MaxHeaderListSize(v)); |
254 | 41.1k | } |
255 | | |
256 | 41.1k | if let Some(v) = self.enable_connect_protocol { |
257 | 0 | f(EnableConnectProtocol(v)); |
258 | 41.1k | } |
259 | 41.1k | } <h2::frame::settings::Settings>::for_each::<<h2::frame::settings::Settings>::payload_len::{closure#0}> Line | Count | Source | 229 | 20.5k | fn for_each<F: FnMut(Setting)>(&self, mut f: F) { | 230 | | use self::Setting::*; | 231 | | | 232 | 20.5k | if let Some(v) = self.header_table_size { | 233 | 0 | f(HeaderTableSize(v)); | 234 | 20.5k | } | 235 | | | 236 | 20.5k | if let Some(v) = self.enable_push { | 237 | 0 | f(EnablePush(v)); | 238 | 20.5k | } | 239 | | | 240 | 20.5k | if let Some(v) = self.max_concurrent_streams { | 241 | 0 | f(MaxConcurrentStreams(v)); | 242 | 20.5k | } | 243 | | | 244 | 20.5k | if let Some(v) = self.initial_window_size { | 245 | 0 | f(InitialWindowSize(v)); | 246 | 20.5k | } | 247 | | | 248 | 20.5k | if let Some(v) = self.max_frame_size { | 249 | 0 | f(MaxFrameSize(v)); | 250 | 20.5k | } | 251 | | | 252 | 20.5k | if let Some(v) = self.max_header_list_size { | 253 | 0 | f(MaxHeaderListSize(v)); | 254 | 20.5k | } | 255 | | | 256 | 20.5k | if let Some(v) = self.enable_connect_protocol { | 257 | 0 | f(EnableConnectProtocol(v)); | 258 | 20.5k | } | 259 | 20.5k | } |
<h2::frame::settings::Settings>::for_each::<<h2::frame::settings::Settings>::encode::{closure#0}> Line | Count | Source | 229 | 20.5k | fn for_each<F: FnMut(Setting)>(&self, mut f: F) { | 230 | | use self::Setting::*; | 231 | | | 232 | 20.5k | if let Some(v) = self.header_table_size { | 233 | 0 | f(HeaderTableSize(v)); | 234 | 20.5k | } | 235 | | | 236 | 20.5k | if let Some(v) = self.enable_push { | 237 | 0 | f(EnablePush(v)); | 238 | 20.5k | } | 239 | | | 240 | 20.5k | if let Some(v) = self.max_concurrent_streams { | 241 | 0 | f(MaxConcurrentStreams(v)); | 242 | 20.5k | } | 243 | | | 244 | 20.5k | if let Some(v) = self.initial_window_size { | 245 | 0 | f(InitialWindowSize(v)); | 246 | 20.5k | } | 247 | | | 248 | 20.5k | if let Some(v) = self.max_frame_size { | 249 | 0 | f(MaxFrameSize(v)); | 250 | 20.5k | } | 251 | | | 252 | 20.5k | if let Some(v) = self.max_header_list_size { | 253 | 0 | f(MaxHeaderListSize(v)); | 254 | 20.5k | } | 255 | | | 256 | 20.5k | if let Some(v) = self.enable_connect_protocol { | 257 | 0 | f(EnableConnectProtocol(v)); | 258 | 20.5k | } | 259 | 20.5k | } |
Unexecuted instantiation: <h2::frame::settings::Settings>::for_each::<<h2::frame::settings::Settings as core::fmt::Debug>::fmt::{closure#0}> |
260 | | } |
261 | | |
262 | | impl<T> From<Settings> for Frame<T> { |
263 | 27.9k | fn from(src: Settings) -> Frame<T> { |
264 | 27.9k | Frame::Settings(src) |
265 | 27.9k | } <h2::frame::Frame as core::convert::From<h2::frame::settings::Settings>>::from Line | Count | Source | 263 | 7.38k | fn from(src: Settings) -> Frame<T> { | 264 | 7.38k | Frame::Settings(src) | 265 | 7.38k | } |
<h2::frame::Frame<h2::proto::streams::prioritize::Prioritized<bytes::bytes::Bytes>> as core::convert::From<h2::frame::settings::Settings>>::from Line | Count | Source | 263 | 669 | fn from(src: Settings) -> Frame<T> { | 264 | 669 | Frame::Settings(src) | 265 | 669 | } |
<h2::frame::Frame<h2::proto::streams::prioritize::Prioritized<bytes::bytes::Bytes>> as core::convert::From<h2::frame::settings::Settings>>::from Line | Count | Source | 263 | 19.9k | fn from(src: Settings) -> Frame<T> { | 264 | 19.9k | Frame::Settings(src) | 265 | 19.9k | } |
|
266 | | } |
267 | | |
268 | | impl fmt::Debug for Settings { |
269 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
270 | 0 | let mut builder = f.debug_struct("Settings"); |
271 | 0 | builder.field("flags", &self.flags); |
272 | | |
273 | 0 | self.for_each(|setting| match setting { |
274 | 0 | Setting::EnablePush(v) => { |
275 | 0 | builder.field("enable_push", &v); |
276 | 0 | } |
277 | 0 | Setting::HeaderTableSize(v) => { |
278 | 0 | builder.field("header_table_size", &v); |
279 | 0 | } |
280 | 0 | Setting::InitialWindowSize(v) => { |
281 | 0 | builder.field("initial_window_size", &v); |
282 | 0 | } |
283 | 0 | Setting::MaxConcurrentStreams(v) => { |
284 | 0 | builder.field("max_concurrent_streams", &v); |
285 | 0 | } |
286 | 0 | Setting::MaxFrameSize(v) => { |
287 | 0 | builder.field("max_frame_size", &v); |
288 | 0 | } |
289 | 0 | Setting::MaxHeaderListSize(v) => { |
290 | 0 | builder.field("max_header_list_size", &v); |
291 | 0 | } |
292 | 0 | Setting::EnableConnectProtocol(v) => { |
293 | 0 | builder.field("enable_connect_protocol", &v); |
294 | 0 | } |
295 | 0 | }); |
296 | | |
297 | 0 | builder.finish() |
298 | 0 | } |
299 | | } |
300 | | |
301 | | // ===== impl Setting ===== |
302 | | |
303 | | impl Setting { |
304 | | /// Creates a new `Setting` with the correct variant corresponding to the |
305 | | /// given setting id, based on the settings IDs defined in section |
306 | | /// 6.5.2. |
307 | 33.5k | pub fn from_id(id: u16, val: u32) -> Option<Setting> { |
308 | | use self::Setting::*; |
309 | | |
310 | 33.5k | match id { |
311 | 1.83k | 1 => Some(HeaderTableSize(val)), |
312 | 596 | 2 => Some(EnablePush(val)), |
313 | 617 | 3 => Some(MaxConcurrentStreams(val)), |
314 | 4.41k | 4 => Some(InitialWindowSize(val)), |
315 | 363 | 5 => Some(MaxFrameSize(val)), |
316 | 287 | 6 => Some(MaxHeaderListSize(val)), |
317 | 250 | 8 => Some(EnableConnectProtocol(val)), |
318 | 25.1k | _ => None, |
319 | | } |
320 | 33.5k | } |
321 | | |
322 | | /// Creates a new `Setting` by parsing the given buffer of 6 bytes, which |
323 | | /// contains the raw byte representation of the setting, according to the |
324 | | /// "SETTINGS format" defined in section 6.5.1. |
325 | | /// |
326 | | /// The `raw` parameter should have length at least 6 bytes, since the |
327 | | /// length of the raw setting is exactly 6 bytes. |
328 | | /// |
329 | | /// # Panics |
330 | | /// |
331 | | /// If given a buffer shorter than 6 bytes, the function will panic. |
332 | 33.5k | fn load(raw: &[u8]) -> Option<Setting> { |
333 | 33.5k | let id: u16 = (u16::from(raw[0]) << 8) | u16::from(raw[1]); |
334 | 33.5k | let val: u32 = unpack_octets_4!(raw, 2, u32); |
335 | | |
336 | 33.5k | Setting::from_id(id, val) |
337 | 33.5k | } |
338 | | |
339 | 0 | fn encode(&self, dst: &mut BytesMut) { |
340 | | use self::Setting::*; |
341 | | |
342 | 0 | let (kind, val) = match *self { |
343 | 0 | HeaderTableSize(v) => (1, v), |
344 | 0 | EnablePush(v) => (2, v), |
345 | 0 | MaxConcurrentStreams(v) => (3, v), |
346 | 0 | InitialWindowSize(v) => (4, v), |
347 | 0 | MaxFrameSize(v) => (5, v), |
348 | 0 | MaxHeaderListSize(v) => (6, v), |
349 | 0 | EnableConnectProtocol(v) => (8, v), |
350 | | }; |
351 | | |
352 | 0 | dst.put_u16(kind); |
353 | 0 | dst.put_u32(val); |
354 | 0 | } |
355 | | } |
356 | | |
357 | | // ===== impl SettingsFlags ===== |
358 | | |
359 | | impl SettingsFlags { |
360 | 0 | pub fn empty() -> SettingsFlags { |
361 | 0 | SettingsFlags(0) |
362 | 0 | } |
363 | | |
364 | 7.49k | pub fn load(bits: u8) -> SettingsFlags { |
365 | 7.49k | SettingsFlags(bits & ALL) |
366 | 7.49k | } |
367 | | |
368 | 7.30k | pub fn ack() -> SettingsFlags { |
369 | 7.30k | SettingsFlags(ACK) |
370 | 7.30k | } |
371 | | |
372 | 14.8k | pub fn is_ack(&self) -> bool { |
373 | 14.8k | self.0 & ACK == ACK |
374 | 14.8k | } |
375 | | } |
376 | | |
377 | | impl From<SettingsFlags> for u8 { |
378 | 20.5k | fn from(src: SettingsFlags) -> u8 { |
379 | 20.5k | src.0 |
380 | 20.5k | } |
381 | | } |
382 | | |
383 | | impl fmt::Debug for SettingsFlags { |
384 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
385 | 0 | util::debug_flags(f, self.0) |
386 | 0 | .flag_if(self.is_ack(), "ACK") |
387 | 0 | .finish() |
388 | 0 | } |
389 | | } |