webparse/http/http2/frame/
settings.rs

1// Copyright 2022 - 2023 Wenmeng See the COPYRIGHT
2// file at the top-level directory of this distribution.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8//
9// Author: tickbh
10// -----
11// Created Date: 2023/09/01 02:19:26
12
13use crate::{
14    http::http2::{
15        frame::{Kind, StreamIdentifier},
16        DEFAULT_MAX_FRAME_SIZE, MAX_INITIAL_WINDOW_SIZE, MAX_MAX_FRAME_SIZE,
17    },
18    Http2Error, WebResult,
19};
20use algorithm::buf::{Binary, BinaryMut, Bt, BtMut};
21
22use super::{frame::FrameHeader, Flag};
23
24#[derive(Clone, Eq, PartialEq, Debug, Hash)]
25pub struct Settings {
26    flags: Flag,
27    // Fields
28    header_table_size: Option<u32>,
29    enable_push: Option<u32>,
30    max_concurrent_streams: Option<u32>,
31    initial_window_size: Option<u32>,
32    max_frame_size: Option<u32>,
33    max_header_list_size: Option<u32>,
34    enable_connect_protocol: Option<u32>,
35}
36
37#[derive(Debug)]
38pub enum Setting {
39    /// 允许发送者以八位字节的形式通知远程端点用于解码头块的头压缩表的最大尺寸。
40    /// 编码器可以通过使用特定于头部块内头部压缩格式的信令来选择等于或小于此值的
41    /// 任何大小(请参见[压缩])。初始值是4,096个八位字节。
42    HeaderTableSize(u32),
43    /// 此设置可用于禁用服务器推送(第8.2节)。
44    /// 如果一个端点接收到这个参数设置为0的值,它不应该发送一个PUSH_PROMISE帧。
45    /// 一个端点既将这个参数设置为0,并且确认它也必须将PUSH_PROMISE帧的接收视为连接错误(见5.4节)。
46    /// 类型PROTOCOL_ERROR。初始值为1,表示允许服务器推送。
47    /// 除0或1以外的任何值必须视为PROTOCOL_ERROR类型的连接错误
48    EnablePush(u32),
49    ///表示发件人允许的最大并发流数。这个限制是有方向性的:
50    /// 它适用于发送者允许接收者创建的数据流。最初这个值没有限制。
51    /// 建议此值不小于100,以免不必要地限制并行性。
52    /// 值为0的SETTINGS_MAX_CONCURRENT_STREAMS不应被视为特殊的端点。
53    /// 零值确实会阻止创建新的流;然而,这也可能发生在活动流所耗尽的任何限制上。
54    /// 服务器应该只在短时间内设置一个零值;如果服务器不希望接受请求,关闭连接更合适。
55    MaxConcurrentStreams(u32),
56    /// 指示发送者的流级别流控制的初始窗口大小(以八位字节为单位)。
57    /// 初始值是2 ^ 16-1(65,535)个八位组。该设置会影响所有流的窗口大小(请参阅第6.9.2节)。
58    /// 高于最大流量控制窗口大小2 ^ 31-1的值必须视为FLOW_CONTROL_ERROR类型的连接错误;
59    /// 然而,这也可能发生在活动流所耗尽的任何限制上。服务器应该只在短时间内设置一个零值;
60    /// 如果服务器不希望接受请求,关闭连接更合适。
61    InitialWindowSize(u32),
62    /// 指示发送者愿意接收的最大帧有效载荷的大小,以八位字节为单位。
63    /// 初始值是2 ^ 14(16,384)个八位字节。
64    /// 端点通告的值必须在该初始值和最大允许帧大小之间(2 ^ 24-1或16,777,215个八位字节),
65    /// 包括在内。此范围之外的值务必视为PROTOCOL_ERROR类型的连接错误
66    MaxFrameSize(u32),
67    /// 此通报设置以八位字节的形式通知对等方发送方准备接受的标题列表的最大大小。
68    /// 该值基于头字段的未压缩大小,包括名称和八位字节的值的长度,以及每个头字段的开销32个字节。
69    /// 对于任何给定的请求,可能会强制实施一个比所宣传的更低的限制。
70    MaxHeaderListSize(u32),
71    EnableConnectProtocol(u32),
72}
73
74impl Default for Settings {
75    fn default() -> Self {
76        // Self {
77        //     flags: Default::default(),
78        //     header_table_size: Some(DEFAULT_SETTINGS_HEADER_TABLE_SIZE as u32),
79        //     enable_push: Default::default(),
80        //     max_concurrent_streams: Default::default(),
81        //     initial_window_size: Some(DEFAULT_INITIAL_WINDOW_SIZE),
82        //     max_frame_size: Some(DEFAULT_MAX_FRAME_SIZE),
83        //     max_header_list_size: Default::default(),
84        //     enable_connect_protocol: Default::default(),
85        // }
86        Self {
87            flags: Default::default(),
88            header_table_size: Default::default(),
89            enable_push: Default::default(),
90            max_concurrent_streams: Default::default(),
91            initial_window_size: Default::default(),
92            max_frame_size: Default::default(),
93            max_header_list_size: Default::default(),
94            enable_connect_protocol: Default::default(),
95        }
96    }
97}
98
99// ===== impl Setting =====
100
101impl Setting {
102    /// Creates a new `Setting` with the correct variant corresponding to the
103    /// given setting id, based on the settings IDs defined in section
104    /// 6.5.2.
105    pub fn from_id(id: u16, val: u32) -> Option<Setting> {
106        use self::Setting::*;
107
108        match id {
109            1 => Some(HeaderTableSize(val)),
110            2 => Some(EnablePush(val)),
111            3 => Some(MaxConcurrentStreams(val)),
112            4 => Some(InitialWindowSize(val)),
113            5 => Some(MaxFrameSize(val)),
114            6 => Some(MaxHeaderListSize(val)),
115            8 => Some(EnableConnectProtocol(val)),
116            _ => None,
117        }
118    }
119
120    fn parse<T: Bt>(bytes: &mut T) -> Option<Setting> {
121        let id: u16 = bytes.get_u16();
122        let val: u32 = bytes.get_u32();
123
124        Setting::from_id(id, val)
125    }
126
127    fn encode<B: Bt + BtMut>(&self, dst: &mut B) -> WebResult<usize> {
128        use self::Setting::*;
129
130        let (kind, val) = match *self {
131            HeaderTableSize(v) => (1, v),
132            EnablePush(v) => (2, v),
133            MaxConcurrentStreams(v) => (3, v),
134            InitialWindowSize(v) => (4, v),
135            MaxFrameSize(v) => (5, v),
136            MaxHeaderListSize(v) => (6, v),
137            EnableConnectProtocol(v) => (8, v),
138        };
139
140        dst.put_u16(kind);
141        dst.put_u32(val);
142        Ok(6)
143    }
144}
145
146impl Settings {
147    pub fn ack() -> Settings {
148        Settings {
149            flags: Flag::ack(),
150            ..Settings::default()
151        }
152    }
153
154    pub fn is_ack(&self) -> bool {
155        self.flags.is_ack()
156    }
157
158    pub fn flags(&self) -> Flag {
159        self.flags
160    }
161
162    pub fn initial_window_size(&self) -> Option<u32> {
163        self.initial_window_size
164    }
165
166    pub fn set_initial_window_size(&mut self, size: Option<u32>) {
167        self.initial_window_size = size;
168    }
169
170    pub fn max_concurrent_streams(&self) -> Option<u32> {
171        self.max_concurrent_streams
172    }
173
174    pub fn set_max_concurrent_streams(&mut self, max: Option<u32>) {
175        self.max_concurrent_streams = max;
176    }
177
178    pub fn max_frame_size(&self) -> Option<u32> {
179        self.max_frame_size
180    }
181
182    pub fn set_max_frame_size(&mut self, size: Option<u32>) {
183        if let Some(val) = size {
184            assert!(DEFAULT_MAX_FRAME_SIZE <= val && val <= MAX_MAX_FRAME_SIZE);
185        }
186        self.max_frame_size = size;
187    }
188
189    pub fn max_header_list_size(&self) -> Option<u32> {
190        self.max_header_list_size
191    }
192
193    pub fn set_max_header_list_size(&mut self, size: Option<u32>) {
194        self.max_header_list_size = size;
195    }
196
197    pub fn is_push_enabled(&self) -> Option<bool> {
198        self.enable_push.map(|val| val != 0)
199    }
200
201    pub fn set_enable_push(&mut self, enable: bool) {
202        self.enable_push = Some(enable as u32);
203    }
204
205    pub fn is_extended_connect_protocol_enabled(&self) -> Option<bool> {
206        self.enable_connect_protocol.map(|val| val != 0)
207    }
208
209    pub fn set_enable_connect_protocol(&mut self, val: Option<u32>) {
210        self.enable_connect_protocol = val;
211    }
212
213    pub fn header_table_size(&self) -> Option<u32> {
214        self.header_table_size
215    }
216
217    /*
218    pub fn set_header_table_size(&mut self, size: Option<u32>) {
219        self.header_table_size = size;
220    }
221    */
222
223    fn parse_setting<T: Bt>(payload: &mut T) -> WebResult<Settings> {
224        use self::Setting::*;
225
226        // Ensure the payload length is correct, each setting is 6 bytes long.
227        if payload.remaining() % 6 != 0 {
228            return Err(Http2Error::into(Http2Error::InvalidPayloadLength));
229        }
230
231        let mut settings = Settings::default();
232        debug_assert!(!settings.flags.is_ack());
233
234        let len = payload.remaining() / 6;
235        for _ in 0..len {
236            match Setting::parse(payload) {
237                Some(HeaderTableSize(val)) => {
238                    settings.header_table_size = Some(val);
239                }
240                Some(EnablePush(val)) => match val {
241                    0 | 1 => {
242                        settings.enable_push = Some(val);
243                    }
244                    _ => {
245                        return Err(Http2Error::InvalidSettingValue.into());
246                    }
247                },
248                Some(MaxConcurrentStreams(val)) => {
249                    settings.max_concurrent_streams = Some(val);
250                }
251                Some(InitialWindowSize(val)) => {
252                    if val as usize > MAX_INITIAL_WINDOW_SIZE {
253                        return Err(Http2Error::InvalidSettingValue.into());
254                    } else {
255                        settings.initial_window_size = Some(val);
256                    }
257                }
258                Some(MaxFrameSize(val)) => {
259                    if DEFAULT_MAX_FRAME_SIZE <= val && val <= MAX_MAX_FRAME_SIZE {
260                        settings.max_frame_size = Some(val);
261                    } else {
262                        return Err(Http2Error::InvalidSettingValue.into());
263                    }
264                }
265                Some(MaxHeaderListSize(val)) => {
266                    settings.max_header_list_size = Some(val);
267                }
268                Some(EnableConnectProtocol(val)) => match val {
269                    0 | 1 => {
270                        settings.enable_connect_protocol = Some(val);
271                    }
272                    _ => {
273                        return Err(Http2Error::InvalidSettingValue.into());
274                    }
275                },
276                None => {}
277            }
278        }
279        Ok(settings)
280    }
281
282    pub fn parse<T: Bt>(head: FrameHeader, payload: &mut T) -> WebResult<Settings> {
283        debug_assert_eq!(head.kind(), &Kind::Settings);
284
285        if !head.stream_id().is_zero() {
286            return Err(Http2Error::into(Http2Error::InvalidStreamId));
287        }
288
289        // Load the flag
290        let flag = head.flag();
291
292        if flag.is_ack() {
293            // Ensure that the payload is empty
294            if payload.has_remaining() {
295                return Err(Http2Error::into(Http2Error::InvalidPayloadLength));
296            }
297
298            // Return the ACK frame
299            return Ok(Settings::ack());
300        }
301
302        Self::parse_setting(payload)
303    }
304
305    pub fn payload_len(&self) -> usize {
306        let mut len = 0;
307        self.for_each(|_| len += 6);
308        len
309    }
310
311    pub fn parse_http_settings(&self, value: &str) -> WebResult<Settings> {
312        use base64::Engine;
313        match base64::engine::general_purpose::URL_SAFE_NO_PAD.decode(value.as_bytes()) {
314            Err(_e) => {
315                return Err(crate::WebError::Http2(Http2Error::InvalidSettingValue));
316            }
317            Ok(v) => {
318                let mut binary = Binary::from(v);
319                return Self::parse_setting(&mut binary);
320            }
321        }
322    }
323
324    pub fn encode_http_settings(&self) -> String {
325        use base64::Engine;
326        let mut dst = BinaryMut::new();
327        self.for_each(|setting| {
328            log::trace!("HTTP2: 编码设置信息; val={:?}", setting);
329            setting.encode(&mut dst).unwrap();
330        });
331        base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(dst.chunk())
332    }
333
334    pub fn encode<B: Bt + BtMut>(&self, dst: &mut B) -> WebResult<usize> {
335        // Create & encode an appropriate frame head
336        let mut head =
337            FrameHeader::new(Kind::Settings, self.flags.into(), StreamIdentifier::zero());
338        head.length = self.payload_len() as u32;
339
340        let mut size = 0;
341        size += head.encode(dst)?;
342
343        // Encode the settings
344        self.for_each(|setting| size += setting.encode(dst).unwrap());
345        log::trace!("HTTP2: 编码设置信息; len={}", size);
346        Ok(size)
347    }
348
349    fn for_each<F: FnMut(Setting)>(&self, mut f: F) {
350        use self::Setting::*;
351
352        if let Some(v) = self.header_table_size {
353            f(HeaderTableSize(v));
354        }
355
356        if let Some(v) = self.enable_push {
357            f(EnablePush(v));
358        }
359
360        if let Some(v) = self.max_concurrent_streams {
361            f(MaxConcurrentStreams(v));
362        }
363
364        if let Some(v) = self.initial_window_size {
365            f(InitialWindowSize(v));
366        }
367
368        if let Some(v) = self.max_frame_size {
369            f(MaxFrameSize(v));
370        }
371
372        if let Some(v) = self.max_header_list_size {
373            f(MaxHeaderListSize(v));
374        }
375
376        if let Some(v) = self.enable_connect_protocol {
377            f(EnableConnectProtocol(v));
378        }
379    }
380}