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 | pub fn ack() -> Settings { |
59 | Settings { |
60 | flags: SettingsFlags::ack(), |
61 | ..Settings::default() |
62 | } |
63 | } |
64 | |
65 | pub fn is_ack(&self) -> bool { |
66 | self.flags.is_ack() |
67 | } |
68 | |
69 | pub fn initial_window_size(&self) -> Option<u32> { |
70 | self.initial_window_size |
71 | } |
72 | |
73 | pub fn set_initial_window_size(&mut self, size: Option<u32>) { |
74 | self.initial_window_size = size; |
75 | } |
76 | |
77 | pub fn max_concurrent_streams(&self) -> Option<u32> { |
78 | self.max_concurrent_streams |
79 | } |
80 | |
81 | pub fn set_max_concurrent_streams(&mut self, max: Option<u32>) { |
82 | self.max_concurrent_streams = max; |
83 | } |
84 | |
85 | pub fn max_frame_size(&self) -> Option<u32> { |
86 | self.max_frame_size |
87 | } |
88 | |
89 | pub fn set_max_frame_size(&mut self, size: Option<u32>) { |
90 | if let Some(val) = size { |
91 | assert!(DEFAULT_MAX_FRAME_SIZE <= val && val <= MAX_MAX_FRAME_SIZE); |
92 | } |
93 | self.max_frame_size = size; |
94 | } |
95 | |
96 | pub fn max_header_list_size(&self) -> Option<u32> { |
97 | self.max_header_list_size |
98 | } |
99 | |
100 | pub fn set_max_header_list_size(&mut self, size: Option<u32>) { |
101 | self.max_header_list_size = size; |
102 | } |
103 | |
104 | pub fn is_push_enabled(&self) -> Option<bool> { |
105 | self.enable_push.map(|val| val != 0) |
106 | } |
107 | |
108 | pub fn set_enable_push(&mut self, enable: bool) { |
109 | self.enable_push = Some(enable as u32); |
110 | } |
111 | |
112 | pub fn is_extended_connect_protocol_enabled(&self) -> Option<bool> { |
113 | self.enable_connect_protocol.map(|val| val != 0) |
114 | } |
115 | |
116 | pub fn set_enable_connect_protocol(&mut self, val: Option<u32>) { |
117 | self.enable_connect_protocol = val; |
118 | } |
119 | |
120 | pub fn header_table_size(&self) -> Option<u32> { |
121 | self.header_table_size |
122 | } |
123 | |
124 | pub fn set_header_table_size(&mut self, size: Option<u32>) { |
125 | self.header_table_size = size; |
126 | } |
127 | |
128 | pub fn load(head: Head, payload: &[u8]) -> Result<Settings, Error> { |
129 | use self::Setting::*; |
130 | |
131 | debug_assert_eq!(head.kind(), crate::frame::Kind::Settings); |
132 | |
133 | if !head.stream_id().is_zero() { |
134 | return Err(Error::InvalidStreamId); |
135 | } |
136 | |
137 | // Load the flag |
138 | let flag = SettingsFlags::load(head.flag()); |
139 | |
140 | if flag.is_ack() { |
141 | // Ensure that the payload is empty |
142 | if !payload.is_empty() { |
143 | return Err(Error::InvalidPayloadLength); |
144 | } |
145 | |
146 | // Return the ACK frame |
147 | return Ok(Settings::ack()); |
148 | } |
149 | |
150 | // Ensure the payload length is correct, each setting is 6 bytes long. |
151 | if payload.len() % 6 != 0 { |
152 | tracing::debug!("invalid settings payload length; len= {:?}" , payload.len()); |
153 | return Err(Error::InvalidPayloadAckSettings); |
154 | } |
155 | |
156 | let mut settings = Settings::default(); |
157 | debug_assert!(!settings.flags.is_ack()); |
158 | |
159 | for raw in payload.chunks(6) { |
160 | match Setting::load(raw) { |
161 | Some(HeaderTableSize(val)) => { |
162 | settings.header_table_size = Some(val); |
163 | } |
164 | Some(EnablePush(val)) => match val { |
165 | 0 | 1 => { |
166 | settings.enable_push = Some(val); |
167 | } |
168 | _ => { |
169 | return Err(Error::InvalidSettingValue); |
170 | } |
171 | }, |
172 | Some(MaxConcurrentStreams(val)) => { |
173 | settings.max_concurrent_streams = Some(val); |
174 | } |
175 | Some(InitialWindowSize(val)) => { |
176 | if val as usize > MAX_INITIAL_WINDOW_SIZE { |
177 | return Err(Error::InvalidSettingValue); |
178 | } else { |
179 | settings.initial_window_size = Some(val); |
180 | } |
181 | } |
182 | Some(MaxFrameSize(val)) => { |
183 | if DEFAULT_MAX_FRAME_SIZE <= val && val <= MAX_MAX_FRAME_SIZE { |
184 | settings.max_frame_size = Some(val); |
185 | } else { |
186 | return Err(Error::InvalidSettingValue); |
187 | } |
188 | } |
189 | Some(MaxHeaderListSize(val)) => { |
190 | settings.max_header_list_size = Some(val); |
191 | } |
192 | Some(EnableConnectProtocol(val)) => match val { |
193 | 0 | 1 => { |
194 | settings.enable_connect_protocol = Some(val); |
195 | } |
196 | _ => { |
197 | return Err(Error::InvalidSettingValue); |
198 | } |
199 | }, |
200 | None => {} |
201 | } |
202 | } |
203 | |
204 | Ok(settings) |
205 | } |
206 | |
207 | fn payload_len(&self) -> usize { |
208 | let mut len = 0; |
209 | self.for_each(|_| len += 6); |
210 | len |
211 | } |
212 | |
213 | pub fn encode(&self, dst: &mut BytesMut) { |
214 | // Create & encode an appropriate frame head |
215 | let head = Head::new(Kind::Settings, self.flags.into(), StreamId::zero()); |
216 | let payload_len = self.payload_len(); |
217 | |
218 | tracing::trace!("encoding SETTINGS; len= {}" , payload_len); |
219 | |
220 | head.encode(payload_len, dst); |
221 | |
222 | // Encode the settings |
223 | self.for_each(|setting| { |
224 | tracing::trace!("encoding setting; val= {:?}" , setting); |
225 | setting.encode(dst) |
226 | }); |
227 | } |
228 | |
229 | fn for_each<F: FnMut(Setting)>(&self, mut f: F) { |
230 | use self::Setting::*; |
231 | |
232 | if let Some(v) = self.header_table_size { |
233 | f(HeaderTableSize(v)); |
234 | } |
235 | |
236 | if let Some(v) = self.enable_push { |
237 | f(EnablePush(v)); |
238 | } |
239 | |
240 | if let Some(v) = self.max_concurrent_streams { |
241 | f(MaxConcurrentStreams(v)); |
242 | } |
243 | |
244 | if let Some(v) = self.initial_window_size { |
245 | f(InitialWindowSize(v)); |
246 | } |
247 | |
248 | if let Some(v) = self.max_frame_size { |
249 | f(MaxFrameSize(v)); |
250 | } |
251 | |
252 | if let Some(v) = self.max_header_list_size { |
253 | f(MaxHeaderListSize(v)); |
254 | } |
255 | |
256 | if let Some(v) = self.enable_connect_protocol { |
257 | f(EnableConnectProtocol(v)); |
258 | } |
259 | } |
260 | } |
261 | |
262 | impl<T> From<Settings> for Frame<T> { |
263 | fn from(src: Settings) -> Frame<T> { |
264 | Frame::Settings(src) |
265 | } |
266 | } |
267 | |
268 | impl fmt::Debug for Settings { |
269 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
270 | let mut builder = f.debug_struct("Settings" ); |
271 | builder.field("flags" , &self.flags); |
272 | |
273 | self.for_each(|setting| match setting { |
274 | Setting::EnablePush(v) => { |
275 | builder.field("enable_push" , &v); |
276 | } |
277 | Setting::HeaderTableSize(v) => { |
278 | builder.field("header_table_size" , &v); |
279 | } |
280 | Setting::InitialWindowSize(v) => { |
281 | builder.field("initial_window_size" , &v); |
282 | } |
283 | Setting::MaxConcurrentStreams(v) => { |
284 | builder.field("max_concurrent_streams" , &v); |
285 | } |
286 | Setting::MaxFrameSize(v) => { |
287 | builder.field("max_frame_size" , &v); |
288 | } |
289 | Setting::MaxHeaderListSize(v) => { |
290 | builder.field("max_header_list_size" , &v); |
291 | } |
292 | Setting::EnableConnectProtocol(v) => { |
293 | builder.field("enable_connect_protocol" , &v); |
294 | } |
295 | }); |
296 | |
297 | builder.finish() |
298 | } |
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 | pub fn from_id(id: u16, val: u32) -> Option<Setting> { |
308 | use self::Setting::*; |
309 | |
310 | match id { |
311 | 1 => Some(HeaderTableSize(val)), |
312 | 2 => Some(EnablePush(val)), |
313 | 3 => Some(MaxConcurrentStreams(val)), |
314 | 4 => Some(InitialWindowSize(val)), |
315 | 5 => Some(MaxFrameSize(val)), |
316 | 6 => Some(MaxHeaderListSize(val)), |
317 | 8 => Some(EnableConnectProtocol(val)), |
318 | _ => None, |
319 | } |
320 | } |
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 | fn load(raw: &[u8]) -> Option<Setting> { |
333 | let id: u16 = (u16::from(raw[0]) << 8) | u16::from(raw[1]); |
334 | let val: u32 = unpack_octets_4!(raw, 2, u32); |
335 | |
336 | Setting::from_id(id, val) |
337 | } |
338 | |
339 | fn encode(&self, dst: &mut BytesMut) { |
340 | use self::Setting::*; |
341 | |
342 | let (kind, val) = match *self { |
343 | HeaderTableSize(v) => (1, v), |
344 | EnablePush(v) => (2, v), |
345 | MaxConcurrentStreams(v) => (3, v), |
346 | InitialWindowSize(v) => (4, v), |
347 | MaxFrameSize(v) => (5, v), |
348 | MaxHeaderListSize(v) => (6, v), |
349 | EnableConnectProtocol(v) => (8, v), |
350 | }; |
351 | |
352 | dst.put_u16(kind); |
353 | dst.put_u32(val); |
354 | } |
355 | } |
356 | |
357 | // ===== impl SettingsFlags ===== |
358 | |
359 | impl SettingsFlags { |
360 | pub fn empty() -> SettingsFlags { |
361 | SettingsFlags(0) |
362 | } |
363 | |
364 | pub fn load(bits: u8) -> SettingsFlags { |
365 | SettingsFlags(bits & ALL) |
366 | } |
367 | |
368 | pub fn ack() -> SettingsFlags { |
369 | SettingsFlags(ACK) |
370 | } |
371 | |
372 | pub fn is_ack(&self) -> bool { |
373 | self.0 & ACK == ACK |
374 | } |
375 | } |
376 | |
377 | impl From<SettingsFlags> for u8 { |
378 | fn from(src: SettingsFlags) -> u8 { |
379 | src.0 |
380 | } |
381 | } |
382 | |
383 | impl fmt::Debug for SettingsFlags { |
384 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
385 | util&mut DebugFlags<'_, '_>::debug_flags(f, self.0) |
386 | .flag_if(self.is_ack(), name:"ACK" ) |
387 | .finish() |
388 | } |
389 | } |
390 | |