1use std::fmt;
2
3use crate::frame::{util, Error, Frame, FrameSize, Head, Kind, StreamId};
4use bytes::{BufMut, BytesMut};
5
6#[derive(Clone, Default, Eq, PartialEq)]
7pub 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)]
24pub 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)]
35pub struct SettingsFlags(u8);
36
37const ACK: u8 = 0x1;
38const ALL: u8 = ACK;
39
40/// The default value of SETTINGS_HEADER_TABLE_SIZE
41pub const DEFAULT_SETTINGS_HEADER_TABLE_SIZE: usize = 4_096;
42
43/// The default value of SETTINGS_INITIAL_WINDOW_SIZE
44pub const DEFAULT_INITIAL_WINDOW_SIZE: u32 = 65_535;
45
46/// The default value of MAX_FRAME_SIZE
47pub const DEFAULT_MAX_FRAME_SIZE: FrameSize = 16_384;
48
49/// INITIAL_WINDOW_SIZE upper bound
50pub const MAX_INITIAL_WINDOW_SIZE: usize = (1 << 31) - 1;
51
52/// MAX_FRAME_SIZE upper bound
53pub const MAX_MAX_FRAME_SIZE: FrameSize = (1 << 24) - 1;
54
55// ===== impl Settings =====
56
57impl 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
262impl<T> From<Settings> for Frame<T> {
263 fn from(src: Settings) -> Frame<T> {
264 Frame::Settings(src)
265 }
266}
267
268impl 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
303impl 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
359impl 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
377impl From<SettingsFlags> for u8 {
378 fn from(src: SettingsFlags) -> u8 {
379 src.0
380 }
381}
382
383impl 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