1use std::fmt;
2
3use crate::frame::{util, Frame, FrameError, FrameSize, Head, Kind, StreamId};
4use ntex_bytes::{BufMut, BytesMut};
5
6#[derive(Copy, Clone, Default, Eq, PartialEq)]
7pub struct Settings {
8 flags: SettingsFlags,
9 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#[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
40pub const DEFAULT_SETTINGS_HEADER_TABLE_SIZE: usize = 4_096;
42
43pub const DEFAULT_INITIAL_WINDOW_SIZE: u32 = 65_535;
45
46pub const DEFAULT_MAX_FRAME_SIZE: FrameSize = 16_384;
48
49pub const MAX_INITIAL_WINDOW_SIZE: usize = (1 << 31) - 1;
51
52pub const MAX_MAX_FRAME_SIZE: FrameSize = (1 << 24) - 1;
54
55impl 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: u32) {
90 assert!((DEFAULT_MAX_FRAME_SIZE..=MAX_MAX_FRAME_SIZE).contains(&size));
91 self.max_frame_size = Some(size);
92 }
93
94 pub fn max_header_list_size(&self) -> Option<u32> {
95 self.max_header_list_size
96 }
97
98 pub fn set_max_header_list_size(&mut self, size: Option<u32>) {
99 self.max_header_list_size = size;
100 }
101
102 pub fn is_push_enabled(&self) -> Option<bool> {
103 self.enable_push.map(|val| val != 0)
104 }
105
106 pub fn set_enable_push(&mut self, enable: bool) {
107 self.enable_push = Some(enable as u32);
108 }
109
110 pub fn is_extended_connect_protocol_enabled(&self) -> Option<bool> {
111 self.enable_connect_protocol.map(|val| val != 0)
112 }
113
114 pub fn set_enable_connect_protocol(&mut self, val: Option<u32>) {
115 self.enable_connect_protocol = val;
116 }
117
118 pub fn header_table_size(&self) -> Option<u32> {
119 self.header_table_size
120 }
121
122 pub fn load(head: Head, payload: &[u8]) -> Result<Settings, FrameError> {
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(FrameError::InvalidStreamId);
135 }
136
137 let flag = SettingsFlags::load(head.flag());
139
140 if flag.is_ack() {
141 if !payload.is_empty() {
143 return Err(FrameError::InvalidPayloadLength);
144 }
145
146 return Ok(Settings::ack());
148 }
149
150 if payload.len() % 6 != 0 {
152 log::debug!("invalid settings payload length; len={:?}", payload.len());
153 return Err(FrameError::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(FrameError::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(FrameError::InvalidSettingValue);
178 } else {
179 settings.initial_window_size = Some(val);
180 }
181 }
182 Some(MaxFrameSize(val)) => {
183 if (DEFAULT_MAX_FRAME_SIZE..=MAX_MAX_FRAME_SIZE).contains(&val) {
184 settings.max_frame_size = Some(val);
185 } else {
186 return Err(FrameError::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(FrameError::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 log::trace!("encoding SETTINGS; len={self:?}");
215
216 let head = Head::new(Kind::Settings, self.flags.into(), StreamId::zero());
218 let payload_len = self.payload_len();
219 head.encode(payload_len, dst);
220
221 self.for_each(|setting| {
223 log::trace!("encoding setting; val={setting:?}");
224 setting.encode(dst)
225 });
226 }
227
228 fn for_each<F: FnMut(Setting)>(&self, mut f: F) {
229 use self::Setting::*;
230
231 if let Some(v) = self.header_table_size {
232 f(HeaderTableSize(v));
233 }
234
235 if let Some(v) = self.enable_push {
236 f(EnablePush(v));
237 }
238
239 if let Some(v) = self.max_concurrent_streams {
240 f(MaxConcurrentStreams(v));
241 }
242
243 if let Some(v) = self.initial_window_size {
244 f(InitialWindowSize(v));
245 }
246
247 if let Some(v) = self.max_frame_size {
248 f(MaxFrameSize(v));
249 }
250
251 if let Some(v) = self.max_header_list_size {
252 f(MaxHeaderListSize(v));
253 }
254
255 if let Some(v) = self.enable_connect_protocol {
256 f(EnableConnectProtocol(v));
257 }
258 }
259}
260
261impl From<Settings> for Frame {
262 fn from(src: Settings) -> Frame {
263 Frame::Settings(src)
264 }
265}
266
267impl fmt::Debug for Settings {
268 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269 let mut builder = f.debug_struct("Settings");
270 builder.field("flags", &self.flags);
271
272 self.for_each(|setting| match setting {
273 Setting::EnablePush(v) => {
274 builder.field("enable_push", &v);
275 }
276 Setting::HeaderTableSize(v) => {
277 builder.field("header_table_size", &v);
278 }
279 Setting::InitialWindowSize(v) => {
280 builder.field("initial_window_size", &v);
281 }
282 Setting::MaxConcurrentStreams(v) => {
283 builder.field("max_concurrent_streams", &v);
284 }
285 Setting::MaxFrameSize(v) => {
286 builder.field("max_frame_size", &v);
287 }
288 Setting::MaxHeaderListSize(v) => {
289 builder.field("max_header_list_size", &v);
290 }
291 Setting::EnableConnectProtocol(v) => {
292 builder.field("enable_connect_protocol", &v);
293 }
294 });
295
296 builder.finish()
297 }
298}
299
300impl Setting {
303 pub fn from_id(id: u16, val: u32) -> Option<Setting> {
307 use self::Setting::*;
308
309 match id {
310 1 => Some(HeaderTableSize(val)),
311 2 => Some(EnablePush(val)),
312 3 => Some(MaxConcurrentStreams(val)),
313 4 => Some(InitialWindowSize(val)),
314 5 => Some(MaxFrameSize(val)),
315 6 => Some(MaxHeaderListSize(val)),
316 8 => Some(EnableConnectProtocol(val)),
317 _ => None,
318 }
319 }
320
321 fn load(raw: &[u8]) -> Option<Setting> {
332 let id: u16 = (u16::from(raw[0]) << 8) | u16::from(raw[1]);
333 let val: u32 = unpack_octets_4!(raw, 2, u32);
334
335 Setting::from_id(id, val)
336 }
337
338 fn encode(&self, dst: &mut BytesMut) {
339 use self::Setting::*;
340
341 let (kind, val) = match *self {
342 HeaderTableSize(v) => (1, v),
343 EnablePush(v) => (2, v),
344 MaxConcurrentStreams(v) => (3, v),
345 InitialWindowSize(v) => (4, v),
346 MaxFrameSize(v) => (5, v),
347 MaxHeaderListSize(v) => (6, v),
348 EnableConnectProtocol(v) => (8, v),
349 };
350
351 dst.put_u16(kind);
352 dst.put_u32(val);
353 }
354}
355
356impl SettingsFlags {
359 pub fn empty() -> SettingsFlags {
360 SettingsFlags(0)
361 }
362
363 pub fn load(bits: u8) -> SettingsFlags {
364 SettingsFlags(bits & ALL)
365 }
366
367 pub fn ack() -> SettingsFlags {
368 SettingsFlags(ACK)
369 }
370
371 pub fn is_ack(&self) -> bool {
372 self.0 & ACK == ACK
373 }
374}
375
376impl From<SettingsFlags> for u8 {
377 fn from(src: SettingsFlags) -> u8 {
378 src.0
379 }
380}
381
382impl fmt::Debug for SettingsFlags {
383 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
384 util::debug_flags(f, self.0)
385 .flag_if(self.is_ack(), "ACK")
386 .finish()
387 }
388}