Skip to main content

shiguredo_http2/
limits.rs

1//! HTTP/2 制限設定
2//!
3//! 接続やストリームの制限値を設定する。
4
5use crate::settings::{
6    DEFAULT_HEADER_TABLE_SIZE, DEFAULT_INITIAL_WINDOW_SIZE, DEFAULT_MAX_FRAME_SIZE, MaxFrameSize,
7    WindowSize,
8};
9
10/// HTTP/2 接続の制限設定
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct Limits {
13    max_concurrent_streams: Option<u32>,
14    initial_window_size: WindowSize,
15    max_frame_size: MaxFrameSize,
16    max_header_list_size: Option<u32>,
17    header_table_size: u32,
18    connection_window_size: WindowSize,
19    enable_connect_protocol: bool,
20    no_rfc7540_priorities: bool,
21    wt_initial_max_data: Option<u32>,
22    wt_initial_max_stream_data_uni: Option<u32>,
23    wt_initial_max_stream_data_bidi_local: Option<u32>,
24    wt_initial_max_streams_uni: Option<u32>,
25    wt_initial_max_streams_bidi: Option<u32>,
26    wt_initial_max_stream_data_bidi_remote: Option<u32>,
27}
28
29impl Limits {
30    /// ビルダーを生成する
31    pub const fn builder() -> LimitsBuilder {
32        LimitsBuilder {
33            max_concurrent_streams: Some(100),
34            initial_window_size: WindowSize::from_static(DEFAULT_INITIAL_WINDOW_SIZE),
35            max_frame_size: MaxFrameSize::from_static(DEFAULT_MAX_FRAME_SIZE),
36            max_header_list_size: Some(16384),
37            header_table_size: DEFAULT_HEADER_TABLE_SIZE,
38            connection_window_size: WindowSize::from_static(DEFAULT_INITIAL_WINDOW_SIZE),
39            enable_connect_protocol: false,
40            no_rfc7540_priorities: false,
41            wt_initial_max_data: None,
42            wt_initial_max_stream_data_uni: None,
43            wt_initial_max_stream_data_bidi_local: None,
44            wt_initial_max_streams_uni: None,
45            wt_initial_max_streams_bidi: None,
46            wt_initial_max_stream_data_bidi_remote: None,
47        }
48    }
49
50    /// 最大同時ストリーム数
51    #[must_use]
52    pub const fn max_concurrent_streams(&self) -> Option<u32> {
53        self.max_concurrent_streams
54    }
55
56    /// 初期ウィンドウサイズ
57    #[must_use]
58    pub const fn initial_window_size(&self) -> WindowSize {
59        self.initial_window_size
60    }
61
62    /// 最大フレームサイズ
63    #[must_use]
64    pub const fn max_frame_size(&self) -> MaxFrameSize {
65        self.max_frame_size
66    }
67
68    /// 最大ヘッダーリストサイズ
69    #[must_use]
70    pub const fn max_header_list_size(&self) -> Option<u32> {
71        self.max_header_list_size
72    }
73
74    /// HPACK 動的テーブルの最大サイズ
75    #[must_use]
76    pub const fn header_table_size(&self) -> u32 {
77        self.header_table_size
78    }
79
80    /// 接続レベルの初期ウィンドウサイズ
81    #[must_use]
82    pub const fn connection_window_size(&self) -> WindowSize {
83        self.connection_window_size
84    }
85
86    /// Extended CONNECT プロトコルの有効化 (RFC 8441)
87    #[must_use]
88    pub const fn enable_connect_protocol(&self) -> bool {
89        self.enable_connect_protocol
90    }
91
92    /// RFC 9113 Section 5.3.1/5.3.2 で非推奨となった RFC 7540 由来の優先度の無効化 (RFC 9218)
93    #[must_use]
94    pub const fn no_rfc7540_priorities(&self) -> bool {
95        self.no_rfc7540_priorities
96    }
97
98    /// SETTINGS_WT_INITIAL_MAX_DATA (0x2b61)
99    #[must_use]
100    pub const fn wt_initial_max_data(&self) -> Option<u32> {
101        self.wt_initial_max_data
102    }
103
104    /// SETTINGS_WT_INITIAL_MAX_STREAM_DATA_UNI (0x2b62)
105    #[must_use]
106    pub const fn wt_initial_max_stream_data_uni(&self) -> Option<u32> {
107        self.wt_initial_max_stream_data_uni
108    }
109
110    /// SETTINGS_WT_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL (0x2b63)
111    #[must_use]
112    pub const fn wt_initial_max_stream_data_bidi_local(&self) -> Option<u32> {
113        self.wt_initial_max_stream_data_bidi_local
114    }
115
116    /// SETTINGS_WT_INITIAL_MAX_STREAMS_UNI (0x2b64)
117    #[must_use]
118    pub const fn wt_initial_max_streams_uni(&self) -> Option<u32> {
119        self.wt_initial_max_streams_uni
120    }
121
122    /// SETTINGS_WT_INITIAL_MAX_STREAMS_BIDI (0x2b65)
123    #[must_use]
124    pub const fn wt_initial_max_streams_bidi(&self) -> Option<u32> {
125        self.wt_initial_max_streams_bidi
126    }
127
128    /// SETTINGS_WT_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE (0x2b66)
129    #[must_use]
130    pub const fn wt_initial_max_stream_data_bidi_remote(&self) -> Option<u32> {
131        self.wt_initial_max_stream_data_bidi_remote
132    }
133}
134
135impl Default for Limits {
136    fn default() -> Self {
137        Limits::builder()
138            .build()
139            .expect("default Limits values are always valid")
140    }
141}
142
143/// `Limits` のビルダー
144#[derive(Debug, Clone, PartialEq, Eq)]
145pub struct LimitsBuilder {
146    max_concurrent_streams: Option<u32>,
147    initial_window_size: WindowSize,
148    max_frame_size: MaxFrameSize,
149    max_header_list_size: Option<u32>,
150    header_table_size: u32,
151    connection_window_size: WindowSize,
152    enable_connect_protocol: bool,
153    no_rfc7540_priorities: bool,
154    wt_initial_max_data: Option<u32>,
155    wt_initial_max_stream_data_uni: Option<u32>,
156    wt_initial_max_stream_data_bidi_local: Option<u32>,
157    wt_initial_max_streams_uni: Option<u32>,
158    wt_initial_max_streams_bidi: Option<u32>,
159    wt_initial_max_stream_data_bidi_remote: Option<u32>,
160}
161
162impl LimitsBuilder {
163    /// 最大同時ストリーム数を設定する
164    #[must_use]
165    pub const fn max_concurrent_streams(mut self, max: Option<u32>) -> Self {
166        self.max_concurrent_streams = max;
167        self
168    }
169
170    /// 初期ウィンドウサイズを設定する
171    #[must_use]
172    pub const fn initial_window_size(mut self, size: WindowSize) -> Self {
173        self.initial_window_size = size;
174        self
175    }
176
177    /// 最大フレームサイズを設定する
178    #[must_use]
179    pub const fn max_frame_size(mut self, size: MaxFrameSize) -> Self {
180        self.max_frame_size = size;
181        self
182    }
183
184    /// 最大ヘッダーリストサイズを設定する
185    #[must_use]
186    pub const fn max_header_list_size(mut self, size: Option<u32>) -> Self {
187        self.max_header_list_size = size;
188        self
189    }
190
191    /// HPACK 動的テーブルの最大サイズを設定する
192    #[must_use]
193    pub const fn header_table_size(mut self, size: u32) -> Self {
194        self.header_table_size = size;
195        self
196    }
197
198    /// 接続レベルのウィンドウサイズを設定する
199    ///
200    /// RFC 9113 Section 6.9.2 / Section 6.5.2: 接続レベルのウィンドウは WINDOW_UPDATE
201    /// でのみ拡張可能で、`SETTINGS_INITIAL_WINDOW_SIZE` で縮小する手段はない。負の増分も
202    /// 存在しないため、プロトコル既定 (65535) 未満を要求しても実現できない。構築時点で
203    /// 拒否することで、利用側で握りつぶせない panic を保証する。
204    #[must_use]
205    pub const fn connection_window_size(mut self, size: WindowSize) -> Self {
206        assert!(
207            size.get() >= DEFAULT_INITIAL_WINDOW_SIZE,
208            "LimitsBuilder::connection_window_size: size must be >= DEFAULT_INITIAL_WINDOW_SIZE (RFC 9113 §6.9.2)"
209        );
210        self.connection_window_size = size;
211        self
212    }
213
214    /// Extended CONNECT プロトコルの有効化を設定する (RFC 8441)
215    #[must_use]
216    pub const fn enable_connect_protocol(mut self, enable: bool) -> Self {
217        self.enable_connect_protocol = enable;
218        self
219    }
220
221    /// RFC 9113 Section 5.3.1/5.3.2 で非推奨となった RFC 7540 由来の優先度の無効化を設定する (RFC 9218)
222    #[must_use]
223    pub const fn no_rfc7540_priorities(mut self, enable: bool) -> Self {
224        self.no_rfc7540_priorities = enable;
225        self
226    }
227
228    /// WebTransport 初期設定を一括設定する (draft-ietf-webtrans-http2-14 Section 11.2)
229    #[must_use]
230    pub const fn webtransport(
231        mut self,
232        max_data: Option<u32>,
233        max_stream_data_uni: Option<u32>,
234        max_stream_data_bidi_local: Option<u32>,
235        max_streams_uni: Option<u32>,
236        max_streams_bidi: Option<u32>,
237        max_stream_data_bidi_remote: Option<u32>,
238    ) -> Self {
239        self.wt_initial_max_data = max_data;
240        self.wt_initial_max_stream_data_uni = max_stream_data_uni;
241        self.wt_initial_max_stream_data_bidi_local = max_stream_data_bidi_local;
242        self.wt_initial_max_streams_uni = max_streams_uni;
243        self.wt_initial_max_streams_bidi = max_streams_bidi;
244        self.wt_initial_max_stream_data_bidi_remote = max_stream_data_bidi_remote;
245        self
246    }
247
248    /// 複合制約を検査して `Limits` を構築する
249    ///
250    /// # Errors
251    ///
252    /// - WebTransport 関連フィールドが設定されているのに `enable_connect_protocol = false`
253    ///   → [`LimitsError::WebtransportRequiresConnectProtocol`]
254    pub fn build(self) -> Result<Limits, LimitsError> {
255        if !self.enable_connect_protocol && self.has_webtransport_settings() {
256            return Err(LimitsError::WebtransportRequiresConnectProtocol);
257        }
258
259        Ok(Limits {
260            max_concurrent_streams: self.max_concurrent_streams,
261            initial_window_size: self.initial_window_size,
262            max_frame_size: self.max_frame_size,
263            max_header_list_size: self.max_header_list_size,
264            header_table_size: self.header_table_size,
265            connection_window_size: self.connection_window_size,
266            enable_connect_protocol: self.enable_connect_protocol,
267            no_rfc7540_priorities: self.no_rfc7540_priorities,
268            wt_initial_max_data: self.wt_initial_max_data,
269            wt_initial_max_stream_data_uni: self.wt_initial_max_stream_data_uni,
270            wt_initial_max_stream_data_bidi_local: self.wt_initial_max_stream_data_bidi_local,
271            wt_initial_max_streams_uni: self.wt_initial_max_streams_uni,
272            wt_initial_max_streams_bidi: self.wt_initial_max_streams_bidi,
273            wt_initial_max_stream_data_bidi_remote: self.wt_initial_max_stream_data_bidi_remote,
274        })
275    }
276
277    /// const コンテキスト用。不正制約でコンパイル時 panic になる
278    pub const fn build_static(self) -> Limits {
279        if !self.enable_connect_protocol && self.has_webtransport_settings() {
280            panic!(
281                "LimitsBuilder::build_static: WebTransport settings require enable_connect_protocol = true"
282            );
283        }
284
285        Limits {
286            max_concurrent_streams: self.max_concurrent_streams,
287            initial_window_size: self.initial_window_size,
288            max_frame_size: self.max_frame_size,
289            max_header_list_size: self.max_header_list_size,
290            header_table_size: self.header_table_size,
291            connection_window_size: self.connection_window_size,
292            enable_connect_protocol: self.enable_connect_protocol,
293            no_rfc7540_priorities: self.no_rfc7540_priorities,
294            wt_initial_max_data: self.wt_initial_max_data,
295            wt_initial_max_stream_data_uni: self.wt_initial_max_stream_data_uni,
296            wt_initial_max_stream_data_bidi_local: self.wt_initial_max_stream_data_bidi_local,
297            wt_initial_max_streams_uni: self.wt_initial_max_streams_uni,
298            wt_initial_max_streams_bidi: self.wt_initial_max_streams_bidi,
299            wt_initial_max_stream_data_bidi_remote: self.wt_initial_max_stream_data_bidi_remote,
300        }
301    }
302
303    const fn has_webtransport_settings(&self) -> bool {
304        self.wt_initial_max_data.is_some()
305            || self.wt_initial_max_stream_data_uni.is_some()
306            || self.wt_initial_max_stream_data_bidi_local.is_some()
307            || self.wt_initial_max_streams_uni.is_some()
308            || self.wt_initial_max_streams_bidi.is_some()
309            || self.wt_initial_max_stream_data_bidi_remote.is_some()
310    }
311}
312
313/// `Limits` 構築時検査エラー
314#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
315pub enum LimitsError {
316    /// WebTransport 関連設定があるのに `enable_connect_protocol = false`
317    ///
318    /// draft-ietf-webtrans-http2-14 §3.1: サーバーは WebTransport 対応を示すために
319    /// `SETTINGS_ENABLE_CONNECT_PROTOCOL = 1` を SETTINGS フレームで MUST 送信する。
320    /// SETTINGS の定義は §11.2 (将来変更される可能性がある)。
321    WebtransportRequiresConnectProtocol,
322}
323
324impl std::fmt::Display for LimitsError {
325    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
326        match self {
327            Self::WebtransportRequiresConnectProtocol => write!(
328                f,
329                "WebTransport settings require enable_connect_protocol = true"
330            ),
331        }
332    }
333}
334
335impl std::error::Error for LimitsError {}