/src/h2/src/frame/headers.rs
Line | Count | Source |
1 | | use super::{util, StreamDependency, StreamId}; |
2 | | use crate::ext::Protocol; |
3 | | use crate::frame::{Error, Frame, Head, Kind}; |
4 | | use crate::hpack::{self, BytesStr}; |
5 | | |
6 | | use http::header::{self, HeaderName, HeaderValue}; |
7 | | use http::{uri, HeaderMap, Method, Request, StatusCode, Uri}; |
8 | | |
9 | | use bytes::{Buf, BufMut, BytesMut}; |
10 | | |
11 | | use std::fmt; |
12 | | use std::io::Cursor; |
13 | | use std::ops::ControlFlow; |
14 | | |
15 | | type EncodeBuf<'a> = bytes::buf::Limit<&'a mut BytesMut>; |
16 | | |
17 | | const MAX_HEADER_LIST_ABUSE_MULTIPLIER: usize = 4; |
18 | | |
19 | | /// Header frame |
20 | | /// |
21 | | /// This could be either a request or a response. |
22 | | #[derive(Eq, PartialEq)] |
23 | | pub struct Headers { |
24 | | /// The ID of the stream with which this frame is associated. |
25 | | stream_id: StreamId, |
26 | | |
27 | | /// The stream dependency information, if any. |
28 | | stream_dep: Option<StreamDependency>, |
29 | | |
30 | | /// The header block fragment |
31 | | header_block: HeaderBlock, |
32 | | |
33 | | /// The associated flags |
34 | | flags: HeadersFlag, |
35 | | } |
36 | | |
37 | | #[derive(Copy, Clone, Eq, PartialEq)] |
38 | | pub struct HeadersFlag(u8); |
39 | | |
40 | | #[derive(Eq, PartialEq)] |
41 | | pub struct PushPromise { |
42 | | /// The ID of the stream with which this frame is associated. |
43 | | stream_id: StreamId, |
44 | | |
45 | | /// The ID of the stream being reserved by this PushPromise. |
46 | | promised_id: StreamId, |
47 | | |
48 | | /// The header block fragment |
49 | | header_block: HeaderBlock, |
50 | | |
51 | | /// The associated flags |
52 | | flags: PushPromiseFlag, |
53 | | } |
54 | | |
55 | | #[derive(Copy, Clone, Eq, PartialEq)] |
56 | | pub struct PushPromiseFlag(u8); |
57 | | |
58 | | #[derive(Debug)] |
59 | | pub struct Continuation { |
60 | | /// Stream ID of continuation frame |
61 | | stream_id: StreamId, |
62 | | |
63 | | header_block: EncodingHeaderBlock, |
64 | | } |
65 | | |
66 | | // TODO: These fields shouldn't be `pub` |
67 | | #[derive(Debug, Default, Eq, PartialEq)] |
68 | | pub struct Pseudo { |
69 | | // Request |
70 | | pub method: Option<Method>, |
71 | | pub scheme: Option<BytesStr>, |
72 | | pub authority: Option<BytesStr>, |
73 | | pub path: Option<BytesStr>, |
74 | | pub protocol: Option<Protocol>, |
75 | | |
76 | | // Response |
77 | | pub status: Option<StatusCode>, |
78 | | } |
79 | | |
80 | | #[derive(Debug)] |
81 | | pub struct Iter { |
82 | | /// Pseudo headers |
83 | | pseudo: Option<Pseudo>, |
84 | | |
85 | | /// Header fields |
86 | | fields: header::IntoIter<HeaderValue>, |
87 | | } |
88 | | |
89 | | #[derive(Debug, PartialEq, Eq)] |
90 | | struct HeaderBlock { |
91 | | /// The decoded header fields |
92 | | fields: HeaderMap, |
93 | | |
94 | | /// Precomputed size of all of our header fields, for perf reasons |
95 | | field_size: usize, |
96 | | |
97 | | /// Set to true if decoding went over the max header list size. |
98 | | is_over_size: bool, |
99 | | |
100 | | /// Pseudo headers, these are broken out as they must be sent as part of the |
101 | | /// headers frame. |
102 | | pseudo: Pseudo, |
103 | | } |
104 | | |
105 | | #[derive(Debug)] |
106 | | struct EncodingHeaderBlock { |
107 | | hpack: BytesMut, |
108 | | } |
109 | | |
110 | | const END_STREAM: u8 = 0x1; |
111 | | const END_HEADERS: u8 = 0x4; |
112 | | const PADDED: u8 = 0x8; |
113 | | const PRIORITY: u8 = 0x20; |
114 | | const ALL: u8 = END_STREAM | END_HEADERS | PADDED | PRIORITY; |
115 | | |
116 | | // ===== impl Headers ===== |
117 | | |
118 | | impl Headers { |
119 | | /// Create a new HEADERS frame |
120 | 400k | pub fn new(stream_id: StreamId, pseudo: Pseudo, fields: HeaderMap) -> Self { |
121 | 400k | Headers { |
122 | 400k | stream_id, |
123 | 400k | stream_dep: None, |
124 | 400k | header_block: HeaderBlock { |
125 | 400k | field_size: calculate_headermap_size(&fields), |
126 | 400k | fields, |
127 | 400k | is_over_size: false, |
128 | 400k | pseudo, |
129 | 400k | }, |
130 | 400k | flags: HeadersFlag::default(), |
131 | 400k | } |
132 | 400k | } |
133 | | |
134 | 0 | pub fn trailers(stream_id: StreamId, fields: HeaderMap) -> Self { |
135 | 0 | let mut flags = HeadersFlag::default(); |
136 | 0 | flags.set_end_stream(); |
137 | | |
138 | 0 | Headers { |
139 | 0 | stream_id, |
140 | 0 | stream_dep: None, |
141 | 0 | header_block: HeaderBlock { |
142 | 0 | field_size: calculate_headermap_size(&fields), |
143 | 0 | fields, |
144 | 0 | is_over_size: false, |
145 | 0 | pseudo: Pseudo::default(), |
146 | 0 | }, |
147 | 0 | flags, |
148 | 0 | } |
149 | 0 | } |
150 | | |
151 | | /// Loads the header frame but doesn't actually do HPACK decoding. |
152 | | /// |
153 | | /// HPACK decoding is done in the `load_hpack` step. |
154 | 40.6k | pub fn load(head: Head, mut src: BytesMut) -> Result<(Self, BytesMut), Error> { |
155 | 40.6k | let flags = HeadersFlag(head.flag()); |
156 | 40.6k | let mut pad = 0; |
157 | | |
158 | 40.6k | tracing::trace!("loading headers; flags={:?}", flags); |
159 | | |
160 | 40.6k | if head.stream_id().is_zero() { |
161 | 3 | return Err(Error::InvalidStreamId); |
162 | 40.6k | } |
163 | | |
164 | | // Read the padding length |
165 | 40.6k | if flags.is_padded() { |
166 | 2.76k | if src.is_empty() { |
167 | 3 | return Err(Error::MalformedMessage); |
168 | 2.75k | } |
169 | 2.75k | pad = src[0] as usize; |
170 | | |
171 | | // Drop the padding |
172 | 2.75k | src.advance(1); |
173 | 37.9k | } |
174 | | |
175 | | // Read the stream dependency |
176 | 40.6k | let stream_dep = if flags.is_priority() { |
177 | 5.82k | if src.len() < 5 { |
178 | 1 | return Err(Error::MalformedMessage); |
179 | 5.82k | } |
180 | 5.82k | let stream_dep = StreamDependency::load(&src[..5])?; |
181 | | |
182 | 5.82k | if stream_dep.dependency_id() == head.stream_id() { |
183 | 69 | return Err(Error::InvalidDependencyId); |
184 | 5.75k | } |
185 | | |
186 | | // Drop the next 5 bytes |
187 | 5.75k | src.advance(5); |
188 | | |
189 | 5.75k | Some(stream_dep) |
190 | | } else { |
191 | 34.8k | None |
192 | | }; |
193 | | |
194 | 40.6k | if pad > 0 { |
195 | 2.67k | if pad > src.len() { |
196 | 14 | return Err(Error::TooMuchPadding); |
197 | 2.66k | } |
198 | | |
199 | 2.66k | let len = src.len() - pad; |
200 | 2.66k | src.truncate(len); |
201 | 37.9k | } |
202 | | |
203 | 40.6k | let headers = Headers { |
204 | 40.6k | stream_id: head.stream_id(), |
205 | 40.6k | stream_dep, |
206 | 40.6k | header_block: HeaderBlock { |
207 | 40.6k | fields: HeaderMap::new(), |
208 | 40.6k | field_size: 0, |
209 | 40.6k | is_over_size: false, |
210 | 40.6k | pseudo: Pseudo::default(), |
211 | 40.6k | }, |
212 | 40.6k | flags, |
213 | 40.6k | }; |
214 | | |
215 | 40.6k | Ok((headers, src)) |
216 | 40.6k | } |
217 | | |
218 | 51.0k | pub fn load_hpack( |
219 | 51.0k | &mut self, |
220 | 51.0k | src: &mut BytesMut, |
221 | 51.0k | max_header_list_size: usize, |
222 | 51.0k | decoder: &mut hpack::Decoder, |
223 | 51.0k | ) -> Result<(), Error> { |
224 | 51.0k | self.header_block.load(src, max_header_list_size, decoder) |
225 | 51.0k | } |
226 | | |
227 | 418k | pub fn stream_id(&self) -> StreamId { |
228 | 418k | self.stream_id |
229 | 418k | } |
230 | | |
231 | 40.6k | pub fn is_end_headers(&self) -> bool { |
232 | 40.6k | self.flags.is_end_headers() |
233 | 40.6k | } |
234 | | |
235 | 145 | pub fn set_end_headers(&mut self) { |
236 | 145 | self.flags.set_end_headers(); |
237 | 145 | } |
238 | | |
239 | 402k | pub fn is_end_stream(&self) -> bool { |
240 | 402k | self.flags.is_end_stream() |
241 | 402k | } |
242 | | |
243 | 741 | pub fn set_end_stream(&mut self) { |
244 | 741 | self.flags.set_end_stream() |
245 | 741 | } |
246 | | |
247 | 10.1k | pub fn is_over_size(&self) -> bool { |
248 | 10.1k | self.header_block.is_over_size |
249 | 10.1k | } |
250 | | |
251 | 1.42k | pub fn into_parts(self) -> (Pseudo, HeaderMap) { |
252 | 1.42k | (self.header_block.pseudo, self.header_block.fields) |
253 | 1.42k | } |
254 | | |
255 | | #[cfg(feature = "unstable")] |
256 | 0 | pub fn pseudo_mut(&mut self) -> &mut Pseudo { |
257 | 0 | &mut self.header_block.pseudo |
258 | 0 | } |
259 | | |
260 | 26 | pub(crate) fn pseudo(&self) -> &Pseudo { |
261 | 26 | &self.header_block.pseudo |
262 | 26 | } |
263 | | |
264 | | /// Whether it has status 1xx |
265 | 1.30k | pub(crate) fn is_informational(&self) -> bool { |
266 | 1.30k | self.header_block.pseudo.is_informational() |
267 | 1.30k | } |
268 | | |
269 | 401k | pub fn fields(&self) -> &HeaderMap { |
270 | 401k | &self.header_block.fields |
271 | 401k | } |
272 | | |
273 | 13 | pub fn into_fields(self) -> HeaderMap { |
274 | 13 | self.header_block.fields |
275 | 13 | } |
276 | | |
277 | 218k | pub fn encode( |
278 | 218k | self, |
279 | 218k | encoder: &mut hpack::Encoder, |
280 | 218k | dst: &mut EncodeBuf<'_>, |
281 | 218k | ) -> Option<Continuation> { |
282 | | // At this point, the `is_end_headers` flag should always be set |
283 | 218k | debug_assert!(self.flags.is_end_headers()); |
284 | | |
285 | | // Get the HEADERS frame head |
286 | 218k | let head = self.head(); |
287 | | |
288 | 218k | self.header_block |
289 | 218k | .into_encoding(encoder) |
290 | 218k | .encode(&head, dst, Some(encoder), |_| {}) |
291 | 218k | } |
292 | | |
293 | 218k | fn head(&self) -> Head { |
294 | 218k | Head::new(Kind::Headers, self.flags.into(), self.stream_id) |
295 | 218k | } |
296 | | } |
297 | | |
298 | | impl<T> From<Headers> for Frame<T> { |
299 | 625k | fn from(src: Headers) -> Self { |
300 | 625k | Frame::Headers(src) |
301 | 625k | } <h2::frame::Frame as core::convert::From<h2::frame::headers::Headers>>::from Line | Count | Source | 299 | 407k | fn from(src: Headers) -> Self { | 300 | 407k | Frame::Headers(src) | 301 | 407k | } |
<h2::frame::Frame<h2::proto::streams::prioritize::Prioritized<bytes::bytes::Bytes>> as core::convert::From<h2::frame::headers::Headers>>::from Line | Count | Source | 299 | 218k | fn from(src: Headers) -> Self { | 300 | 218k | Frame::Headers(src) | 301 | 218k | } |
|
302 | | } |
303 | | |
304 | | impl fmt::Debug for Headers { |
305 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
306 | 0 | let mut builder = f.debug_struct("Headers"); |
307 | 0 | builder |
308 | 0 | .field("stream_id", &self.stream_id) |
309 | 0 | .field("flags", &self.flags); |
310 | | |
311 | 0 | if let Some(ref protocol) = self.header_block.pseudo.protocol { |
312 | 0 | builder.field("protocol", protocol); |
313 | 0 | } |
314 | | |
315 | 0 | if let Some(ref dep) = self.stream_dep { |
316 | 0 | builder.field("stream_dep", dep); |
317 | 0 | } |
318 | | |
319 | | // `fields` and `pseudo` purposefully not included |
320 | 0 | builder.finish() |
321 | 0 | } |
322 | | } |
323 | | |
324 | | // ===== util ===== |
325 | | |
326 | | #[derive(Debug, PartialEq, Eq)] |
327 | | pub struct ParseU64Error; |
328 | | |
329 | 413 | pub fn parse_u64(src: &[u8]) -> Result<u64, ParseU64Error> { |
330 | 413 | if src.len() > 19 { |
331 | | // At danger for overflow... |
332 | 2 | return Err(ParseU64Error); |
333 | 411 | } |
334 | | |
335 | 411 | let mut ret = 0; |
336 | | |
337 | 1.08k | for &d in src { |
338 | 712 | if d < b'0' || d > b'9' { |
339 | 41 | return Err(ParseU64Error); |
340 | 671 | } |
341 | | |
342 | 671 | ret *= 10; |
343 | 671 | ret += (d - b'0') as u64; |
344 | | } |
345 | | |
346 | 370 | Ok(ret) |
347 | 413 | } |
348 | | |
349 | | // ===== impl PushPromise ===== |
350 | | |
351 | | #[derive(Debug)] |
352 | | pub enum PushPromiseHeaderError { |
353 | | InvalidContentLength(Result<u64, ParseU64Error>), |
354 | | NotSafeAndCacheable, |
355 | | } |
356 | | |
357 | | impl PushPromise { |
358 | 0 | pub fn new( |
359 | 0 | stream_id: StreamId, |
360 | 0 | promised_id: StreamId, |
361 | 0 | pseudo: Pseudo, |
362 | 0 | fields: HeaderMap, |
363 | 0 | ) -> Self { |
364 | 0 | PushPromise { |
365 | 0 | flags: PushPromiseFlag::default(), |
366 | 0 | header_block: HeaderBlock { |
367 | 0 | field_size: calculate_headermap_size(&fields), |
368 | 0 | fields, |
369 | 0 | is_over_size: false, |
370 | 0 | pseudo, |
371 | 0 | }, |
372 | 0 | promised_id, |
373 | 0 | stream_id, |
374 | 0 | } |
375 | 0 | } |
376 | | |
377 | 274 | pub fn validate_request(req: &Request<()>) -> Result<(), PushPromiseHeaderError> { |
378 | | use PushPromiseHeaderError::*; |
379 | | // The spec has some requirements for promised request headers |
380 | | // [https://httpwg.org/specs/rfc7540.html#PushRequests] |
381 | | |
382 | | // A promised request "that indicates the presence of a request body |
383 | | // MUST reset the promised stream with a stream error" |
384 | 274 | for content_length in req.headers().get_all(header::CONTENT_LENGTH) { |
385 | 166 | let parsed_length = parse_u64(content_length.as_bytes()); |
386 | 166 | if parsed_length != Ok(0) { |
387 | 18 | return Err(InvalidContentLength(parsed_length)); |
388 | 148 | } |
389 | | } |
390 | | // "The server MUST include a method in the :method pseudo-header field |
391 | | // that is safe and cacheable" |
392 | 256 | if !Self::safe_and_cacheable(req.method()) { |
393 | 60 | return Err(NotSafeAndCacheable); |
394 | 196 | } |
395 | | |
396 | 196 | Ok(()) |
397 | 274 | } |
398 | | |
399 | 256 | fn safe_and_cacheable(method: &Method) -> bool { |
400 | | // Cacheable: https://httpwg.org/specs/rfc7231.html#cacheable.methods |
401 | | // Safe: https://httpwg.org/specs/rfc7231.html#safe.methods |
402 | 256 | method == Method::GET || method == Method::HEAD |
403 | 256 | } |
404 | | |
405 | 0 | pub fn fields(&self) -> &HeaderMap { |
406 | 0 | &self.header_block.fields |
407 | 0 | } |
408 | | |
409 | | #[cfg(feature = "unstable")] |
410 | 0 | pub fn into_fields(self) -> HeaderMap { |
411 | 0 | self.header_block.fields |
412 | 0 | } |
413 | | |
414 | | /// Loads the push promise frame but doesn't actually do HPACK decoding. |
415 | | /// |
416 | | /// HPACK decoding is done in the `load_hpack` step. |
417 | 2.52k | pub fn load(head: Head, mut src: BytesMut) -> Result<(Self, BytesMut), Error> { |
418 | 2.52k | let flags = PushPromiseFlag(head.flag()); |
419 | 2.52k | let mut pad = 0; |
420 | | |
421 | 2.52k | if head.stream_id().is_zero() { |
422 | 2 | return Err(Error::InvalidStreamId); |
423 | 2.52k | } |
424 | | |
425 | | // Read the padding length |
426 | 2.52k | if flags.is_padded() { |
427 | 122 | if src.is_empty() { |
428 | 10 | return Err(Error::MalformedMessage); |
429 | 112 | } |
430 | | |
431 | | // TODO: Ensure payload is sized correctly |
432 | 112 | pad = src[0] as usize; |
433 | | |
434 | | // Drop the padding |
435 | 112 | src.advance(1); |
436 | 2.40k | } |
437 | | |
438 | 2.51k | if src.len() < 5 { |
439 | 23 | return Err(Error::MalformedMessage); |
440 | 2.49k | } |
441 | | |
442 | 2.49k | let (promised_id, _) = StreamId::parse(&src[..4]); |
443 | | // Drop promised_id bytes |
444 | 2.49k | src.advance(4); |
445 | | |
446 | 2.49k | if pad > 0 { |
447 | 94 | if pad > src.len() { |
448 | 9 | return Err(Error::TooMuchPadding); |
449 | 85 | } |
450 | | |
451 | 85 | let len = src.len() - pad; |
452 | 85 | src.truncate(len); |
453 | 2.39k | } |
454 | | |
455 | 2.48k | let frame = PushPromise { |
456 | 2.48k | flags, |
457 | 2.48k | header_block: HeaderBlock { |
458 | 2.48k | fields: HeaderMap::new(), |
459 | 2.48k | field_size: 0, |
460 | 2.48k | is_over_size: false, |
461 | 2.48k | pseudo: Pseudo::default(), |
462 | 2.48k | }, |
463 | 2.48k | promised_id, |
464 | 2.48k | stream_id: head.stream_id(), |
465 | 2.48k | }; |
466 | 2.48k | Ok((frame, src)) |
467 | 2.52k | } |
468 | | |
469 | 2.48k | pub fn load_hpack( |
470 | 2.48k | &mut self, |
471 | 2.48k | src: &mut BytesMut, |
472 | 2.48k | max_header_list_size: usize, |
473 | 2.48k | decoder: &mut hpack::Decoder, |
474 | 2.48k | ) -> Result<(), Error> { |
475 | 2.48k | self.header_block.load(src, max_header_list_size, decoder) |
476 | 2.48k | } |
477 | | |
478 | 1.05k | pub fn stream_id(&self) -> StreamId { |
479 | 1.05k | self.stream_id |
480 | 1.05k | } |
481 | | |
482 | 1.98k | pub fn promised_id(&self) -> StreamId { |
483 | 1.98k | self.promised_id |
484 | 1.98k | } |
485 | | |
486 | 2.48k | pub fn is_end_headers(&self) -> bool { |
487 | 2.48k | self.flags.is_end_headers() |
488 | 2.48k | } |
489 | | |
490 | 0 | pub fn set_end_headers(&mut self) { |
491 | 0 | self.flags.set_end_headers(); |
492 | 0 | } |
493 | | |
494 | 931 | pub fn is_over_size(&self) -> bool { |
495 | 931 | self.header_block.is_over_size |
496 | 931 | } |
497 | | |
498 | 0 | pub fn encode( |
499 | 0 | self, |
500 | 0 | encoder: &mut hpack::Encoder, |
501 | 0 | dst: &mut EncodeBuf<'_>, |
502 | 0 | ) -> Option<Continuation> { |
503 | | // At this point, the `is_end_headers` flag should always be set |
504 | 0 | debug_assert!(self.flags.is_end_headers()); |
505 | | |
506 | 0 | let head = self.head(); |
507 | 0 | let promised_id = self.promised_id; |
508 | | |
509 | 0 | self.header_block |
510 | 0 | .into_encoding(encoder) |
511 | 0 | .encode(&head, dst, Some(encoder), |dst| { |
512 | 0 | dst.put_u32(promised_id.into()); |
513 | 0 | }) |
514 | 0 | } |
515 | | |
516 | 0 | fn head(&self) -> Head { |
517 | 0 | Head::new(Kind::PushPromise, self.flags.into(), self.stream_id) |
518 | 0 | } |
519 | | |
520 | | /// Consume `self`, returning the parts of the frame |
521 | 931 | pub fn into_parts(self) -> (Pseudo, HeaderMap) { |
522 | 931 | (self.header_block.pseudo, self.header_block.fields) |
523 | 931 | } |
524 | | } |
525 | | |
526 | | impl<T> From<PushPromise> for Frame<T> { |
527 | 1.05k | fn from(src: PushPromise) -> Self { |
528 | 1.05k | Frame::PushPromise(src) |
529 | 1.05k | } <h2::frame::Frame as core::convert::From<h2::frame::headers::PushPromise>>::from Line | Count | Source | 527 | 1.05k | fn from(src: PushPromise) -> Self { | 528 | 1.05k | Frame::PushPromise(src) | 529 | 1.05k | } |
Unexecuted instantiation: <h2::frame::Frame<h2::proto::streams::prioritize::Prioritized<bytes::bytes::Bytes>> as core::convert::From<h2::frame::headers::PushPromise>>::from |
530 | | } |
531 | | |
532 | | impl fmt::Debug for PushPromise { |
533 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
534 | 0 | f.debug_struct("PushPromise") |
535 | 0 | .field("stream_id", &self.stream_id) |
536 | 0 | .field("promised_id", &self.promised_id) |
537 | 0 | .field("flags", &self.flags) |
538 | | // `fields` and `pseudo` purposefully not included |
539 | 0 | .finish() |
540 | 0 | } |
541 | | } |
542 | | |
543 | | // ===== impl Continuation ===== |
544 | | |
545 | | impl Continuation { |
546 | 0 | fn head(&self) -> Head { |
547 | 0 | Head::new(Kind::Continuation, END_HEADERS, self.stream_id) |
548 | 0 | } |
549 | | |
550 | 0 | pub fn encode(self, dst: &mut EncodeBuf<'_>) -> Option<Continuation> { |
551 | | // Get the CONTINUATION frame head |
552 | 0 | let head = self.head(); |
553 | | |
554 | 0 | self.header_block.encode(&head, dst, None, |_| {}) |
555 | 0 | } |
556 | | } |
557 | | |
558 | | // ===== impl Pseudo ===== |
559 | | |
560 | | impl Pseudo { |
561 | 400k | pub fn request(method: Method, uri: Uri, protocol: Option<Protocol>) -> Self { |
562 | 400k | let parts = uri::Parts::from(uri); |
563 | | |
564 | 400k | let (scheme, path) = if method == Method::CONNECT && protocol.is_none() { |
565 | 0 | (None, None) |
566 | | } else { |
567 | 400k | let path = parts |
568 | 400k | .path_and_query |
569 | 400k | .map(|v| BytesStr::from(v.as_str())) |
570 | 400k | .unwrap_or(BytesStr::from_static("")); |
571 | | |
572 | 400k | let path = if !path.is_empty() { |
573 | 399k | path |
574 | 583 | } else if method == Method::OPTIONS { |
575 | 0 | BytesStr::from_static("*") |
576 | | } else { |
577 | 583 | BytesStr::from_static("/") |
578 | | }; |
579 | | |
580 | 400k | (parts.scheme, Some(path)) |
581 | | }; |
582 | | |
583 | 400k | let mut pseudo = Pseudo { |
584 | 400k | method: Some(method), |
585 | 400k | scheme: None, |
586 | 400k | authority: None, |
587 | 400k | path, |
588 | 400k | protocol, |
589 | 400k | status: None, |
590 | 400k | }; |
591 | | |
592 | | // If the URI includes a scheme component, add it to the pseudo headers |
593 | 400k | if let Some(scheme) = scheme { |
594 | 399k | pseudo.set_scheme(scheme); |
595 | 399k | } |
596 | | |
597 | | // If the URI includes an authority component, add it to the pseudo |
598 | | // headers |
599 | 400k | if let Some(authority) = parts.authority { |
600 | 400k | pseudo.set_authority(BytesStr::from(authority.as_str())); |
601 | 400k | } |
602 | | |
603 | 400k | pseudo |
604 | 400k | } |
605 | | |
606 | 0 | pub fn response(status: StatusCode) -> Self { |
607 | 0 | Pseudo { |
608 | 0 | method: None, |
609 | 0 | scheme: None, |
610 | 0 | authority: None, |
611 | 0 | path: None, |
612 | 0 | protocol: None, |
613 | 0 | status: Some(status), |
614 | 0 | } |
615 | 0 | } |
616 | | |
617 | | #[cfg(feature = "unstable")] |
618 | 0 | pub fn set_status(&mut self, value: StatusCode) { |
619 | 0 | self.status = Some(value); |
620 | 0 | } |
621 | | |
622 | 399k | pub fn set_scheme(&mut self, scheme: uri::Scheme) { |
623 | 399k | let bytes_str = match scheme.as_str() { |
624 | 399k | "http" => BytesStr::from_static("http"), |
625 | 399k | "https" => BytesStr::from_static("https"), |
626 | 48 | s => BytesStr::from(s), |
627 | | }; |
628 | 399k | self.scheme = Some(bytes_str); |
629 | 399k | } |
630 | | |
631 | | #[cfg(feature = "unstable")] |
632 | 0 | pub fn set_protocol(&mut self, protocol: Protocol) { |
633 | 0 | self.protocol = Some(protocol); |
634 | 0 | } |
635 | | |
636 | 400k | pub fn set_authority(&mut self, authority: BytesStr) { |
637 | 400k | self.authority = Some(authority); |
638 | 400k | } |
639 | | |
640 | | /// Whether it has status 1xx |
641 | 2.72k | pub(crate) fn is_informational(&self) -> bool { |
642 | 2.72k | self.status |
643 | 2.72k | .map_or(false, |status| status.is_informational()) |
644 | 2.72k | } |
645 | | } |
646 | | |
647 | | // ===== impl EncodingHeaderBlock ===== |
648 | | |
649 | | impl EncodingHeaderBlock { |
650 | 218k | fn encode<F>( |
651 | 218k | mut self, |
652 | 218k | head: &Head, |
653 | 218k | dst: &mut EncodeBuf<'_>, |
654 | 218k | encoder: Option<&mut hpack::Encoder>, |
655 | 218k | f: F, |
656 | 218k | ) -> Option<Continuation> |
657 | 218k | where |
658 | 218k | F: FnOnce(&mut EncodeBuf<'_>), |
659 | | { |
660 | 218k | let head_pos = dst.get_ref().len(); |
661 | | |
662 | | // At this point, we don't know how big the h2 frame will be. |
663 | | // So, we write the head with length 0, then write the body, and |
664 | | // finally write the length once we know the size. |
665 | 218k | head.encode(0, dst); |
666 | | |
667 | 218k | let payload_pos = dst.get_ref().len(); |
668 | | |
669 | 218k | f(dst); |
670 | | |
671 | | // Now, encode the header payload |
672 | 218k | let continuation = if self.hpack.len() > dst.remaining_mut() { |
673 | 0 | let head_part = self.hpack.split_to(dst.remaining_mut()); |
674 | 0 | dst.put_slice(&head_part); |
675 | | |
676 | 0 | Some(Continuation { |
677 | 0 | stream_id: head.stream_id(), |
678 | 0 | header_block: self, |
679 | 0 | }) |
680 | | } else { |
681 | 218k | dst.put_slice(&self.hpack); |
682 | | // The block is fully written, so the buffer can be reused by the |
683 | | // next frame on this connection. |
684 | 218k | if let Some(encoder) = encoder { |
685 | 218k | encoder.return_scratch(self.hpack); |
686 | 218k | } |
687 | | |
688 | 218k | None |
689 | | }; |
690 | | |
691 | | // Compute the header block length |
692 | 218k | let payload_len = (dst.get_ref().len() - payload_pos) as u64; |
693 | | |
694 | | // Write the frame length |
695 | 218k | let payload_len_be = payload_len.to_be_bytes(); |
696 | 1.09M | assert!(payload_len_be[0..5].iter().all(|b| *b == 0)); <h2::frame::headers::EncodingHeaderBlock>::encode::<<h2::frame::headers::Headers>::encode::{closure#0}>::{closure#0}Line | Count | Source | 696 | 1.09M | assert!(payload_len_be[0..5].iter().all(|b| *b == 0)); |
Unexecuted instantiation: <h2::frame::headers::EncodingHeaderBlock>::encode::<<h2::frame::headers::PushPromise>::encode::{closure#0}>::{closure#0}Unexecuted instantiation: <h2::frame::headers::EncodingHeaderBlock>::encode::<<h2::frame::headers::Continuation>::encode::{closure#0}>::{closure#0} |
697 | 218k | (dst.get_mut()[head_pos..head_pos + 3]).copy_from_slice(&payload_len_be[5..]); |
698 | | |
699 | 218k | if continuation.is_some() { |
700 | | // There will be continuation frames, so the `is_end_headers` flag |
701 | | // must be unset |
702 | 0 | debug_assert!(dst.get_ref()[head_pos + 4] & END_HEADERS == END_HEADERS); |
703 | | |
704 | 0 | dst.get_mut()[head_pos + 4] -= END_HEADERS; |
705 | 218k | } |
706 | | |
707 | 218k | continuation |
708 | 218k | } <h2::frame::headers::EncodingHeaderBlock>::encode::<<h2::frame::headers::Headers>::encode::{closure#0}>Line | Count | Source | 650 | 218k | fn encode<F>( | 651 | 218k | mut self, | 652 | 218k | head: &Head, | 653 | 218k | dst: &mut EncodeBuf<'_>, | 654 | 218k | encoder: Option<&mut hpack::Encoder>, | 655 | 218k | f: F, | 656 | 218k | ) -> Option<Continuation> | 657 | 218k | where | 658 | 218k | F: FnOnce(&mut EncodeBuf<'_>), | 659 | | { | 660 | 218k | let head_pos = dst.get_ref().len(); | 661 | | | 662 | | // At this point, we don't know how big the h2 frame will be. | 663 | | // So, we write the head with length 0, then write the body, and | 664 | | // finally write the length once we know the size. | 665 | 218k | head.encode(0, dst); | 666 | | | 667 | 218k | let payload_pos = dst.get_ref().len(); | 668 | | | 669 | 218k | f(dst); | 670 | | | 671 | | // Now, encode the header payload | 672 | 218k | let continuation = if self.hpack.len() > dst.remaining_mut() { | 673 | 0 | let head_part = self.hpack.split_to(dst.remaining_mut()); | 674 | 0 | dst.put_slice(&head_part); | 675 | | | 676 | 0 | Some(Continuation { | 677 | 0 | stream_id: head.stream_id(), | 678 | 0 | header_block: self, | 679 | 0 | }) | 680 | | } else { | 681 | 218k | dst.put_slice(&self.hpack); | 682 | | // The block is fully written, so the buffer can be reused by the | 683 | | // next frame on this connection. | 684 | 218k | if let Some(encoder) = encoder { | 685 | 218k | encoder.return_scratch(self.hpack); | 686 | 218k | } | 687 | | | 688 | 218k | None | 689 | | }; | 690 | | | 691 | | // Compute the header block length | 692 | 218k | let payload_len = (dst.get_ref().len() - payload_pos) as u64; | 693 | | | 694 | | // Write the frame length | 695 | 218k | let payload_len_be = payload_len.to_be_bytes(); | 696 | 218k | assert!(payload_len_be[0..5].iter().all(|b| *b == 0)); | 697 | 218k | (dst.get_mut()[head_pos..head_pos + 3]).copy_from_slice(&payload_len_be[5..]); | 698 | | | 699 | 218k | if continuation.is_some() { | 700 | | // There will be continuation frames, so the `is_end_headers` flag | 701 | | // must be unset | 702 | 0 | debug_assert!(dst.get_ref()[head_pos + 4] & END_HEADERS == END_HEADERS); | 703 | | | 704 | 0 | dst.get_mut()[head_pos + 4] -= END_HEADERS; | 705 | 218k | } | 706 | | | 707 | 218k | continuation | 708 | 218k | } |
Unexecuted instantiation: <h2::frame::headers::EncodingHeaderBlock>::encode::<<h2::frame::headers::PushPromise>::encode::{closure#0}>Unexecuted instantiation: <h2::frame::headers::EncodingHeaderBlock>::encode::<<h2::frame::headers::Continuation>::encode::{closure#0}> |
709 | | } |
710 | | |
711 | | // ===== impl Iter ===== |
712 | | |
713 | | impl Iterator for Iter { |
714 | | type Item = hpack::Header<Option<HeaderName>>; |
715 | | |
716 | 1.09M | fn next(&mut self) -> Option<Self::Item> { |
717 | | use crate::hpack::Header::*; |
718 | | |
719 | 1.09M | if let Some(ref mut pseudo) = self.pseudo { |
720 | 1.09M | if let Some(method) = pseudo.method.take() { |
721 | 218k | return Some(Method(method)); |
722 | 874k | } |
723 | | |
724 | 874k | if let Some(scheme) = pseudo.scheme.take() { |
725 | 218k | return Some(Scheme(scheme)); |
726 | 655k | } |
727 | | |
728 | 655k | if let Some(authority) = pseudo.authority.take() { |
729 | 218k | return Some(Authority(authority)); |
730 | 437k | } |
731 | | |
732 | 437k | if let Some(path) = pseudo.path.take() { |
733 | 218k | return Some(Path(path)); |
734 | 218k | } |
735 | | |
736 | 218k | if let Some(protocol) = pseudo.protocol.take() { |
737 | 0 | return Some(Protocol(protocol)); |
738 | 218k | } |
739 | | |
740 | 218k | if let Some(status) = pseudo.status.take() { |
741 | 0 | return Some(Status(status)); |
742 | 218k | } |
743 | 0 | } |
744 | | |
745 | 218k | self.pseudo = None; |
746 | | |
747 | 218k | self.fields |
748 | 218k | .next() |
749 | 218k | .map(|(name, value)| Field { name, value }) |
750 | 1.09M | } |
751 | | } |
752 | | |
753 | | // ===== impl HeadersFlag ===== |
754 | | |
755 | | impl HeadersFlag { |
756 | 0 | pub fn empty() -> HeadersFlag { |
757 | 0 | HeadersFlag(0) |
758 | 0 | } |
759 | | |
760 | 0 | pub fn load(bits: u8) -> HeadersFlag { |
761 | 0 | HeadersFlag(bits & ALL) |
762 | 0 | } |
763 | | |
764 | 402k | pub fn is_end_stream(&self) -> bool { |
765 | 402k | self.0 & END_STREAM == END_STREAM |
766 | 402k | } |
767 | | |
768 | 741 | pub fn set_end_stream(&mut self) { |
769 | 741 | self.0 |= END_STREAM; |
770 | 741 | } |
771 | | |
772 | 40.6k | pub fn is_end_headers(&self) -> bool { |
773 | 40.6k | self.0 & END_HEADERS == END_HEADERS |
774 | 40.6k | } |
775 | | |
776 | 145 | pub fn set_end_headers(&mut self) { |
777 | 145 | self.0 |= END_HEADERS; |
778 | 145 | } |
779 | | |
780 | 40.6k | pub fn is_padded(&self) -> bool { |
781 | 40.6k | self.0 & PADDED == PADDED |
782 | 40.6k | } |
783 | | |
784 | 40.6k | pub fn is_priority(&self) -> bool { |
785 | 40.6k | self.0 & PRIORITY == PRIORITY |
786 | 40.6k | } |
787 | | } |
788 | | |
789 | | impl Default for HeadersFlag { |
790 | | /// Returns a `HeadersFlag` value with `END_HEADERS` set. |
791 | 400k | fn default() -> Self { |
792 | 400k | HeadersFlag(END_HEADERS) |
793 | 400k | } |
794 | | } |
795 | | |
796 | | impl From<HeadersFlag> for u8 { |
797 | 218k | fn from(src: HeadersFlag) -> u8 { |
798 | 218k | src.0 |
799 | 218k | } |
800 | | } |
801 | | |
802 | | impl fmt::Debug for HeadersFlag { |
803 | 0 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
804 | 0 | util::debug_flags(fmt, self.0) |
805 | 0 | .flag_if(self.is_end_headers(), "END_HEADERS") |
806 | 0 | .flag_if(self.is_end_stream(), "END_STREAM") |
807 | 0 | .flag_if(self.is_padded(), "PADDED") |
808 | 0 | .flag_if(self.is_priority(), "PRIORITY") |
809 | 0 | .finish() |
810 | 0 | } |
811 | | } |
812 | | |
813 | | // ===== impl PushPromiseFlag ===== |
814 | | |
815 | | impl PushPromiseFlag { |
816 | 0 | pub fn empty() -> PushPromiseFlag { |
817 | 0 | PushPromiseFlag(0) |
818 | 0 | } |
819 | | |
820 | 0 | pub fn load(bits: u8) -> PushPromiseFlag { |
821 | 0 | PushPromiseFlag(bits & ALL) |
822 | 0 | } |
823 | | |
824 | 2.48k | pub fn is_end_headers(&self) -> bool { |
825 | 2.48k | self.0 & END_HEADERS == END_HEADERS |
826 | 2.48k | } |
827 | | |
828 | 0 | pub fn set_end_headers(&mut self) { |
829 | 0 | self.0 |= END_HEADERS; |
830 | 0 | } |
831 | | |
832 | 2.52k | pub fn is_padded(&self) -> bool { |
833 | 2.52k | self.0 & PADDED == PADDED |
834 | 2.52k | } |
835 | | } |
836 | | |
837 | | impl Default for PushPromiseFlag { |
838 | | /// Returns a `PushPromiseFlag` value with `END_HEADERS` set. |
839 | 0 | fn default() -> Self { |
840 | 0 | PushPromiseFlag(END_HEADERS) |
841 | 0 | } |
842 | | } |
843 | | |
844 | | impl From<PushPromiseFlag> for u8 { |
845 | 0 | fn from(src: PushPromiseFlag) -> u8 { |
846 | 0 | src.0 |
847 | 0 | } |
848 | | } |
849 | | |
850 | | impl fmt::Debug for PushPromiseFlag { |
851 | 0 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
852 | 0 | util::debug_flags(fmt, self.0) |
853 | 0 | .flag_if(self.is_end_headers(), "END_HEADERS") |
854 | 0 | .flag_if(self.is_padded(), "PADDED") |
855 | 0 | .finish() |
856 | 0 | } |
857 | | } |
858 | | |
859 | | // ===== HeaderBlock ===== |
860 | | |
861 | | impl HeaderBlock { |
862 | 53.4k | fn load( |
863 | 53.4k | &mut self, |
864 | 53.4k | src: &mut BytesMut, |
865 | 53.4k | max_header_list_size: usize, |
866 | 53.4k | decoder: &mut hpack::Decoder, |
867 | 53.4k | ) -> Result<(), Error> { |
868 | 53.4k | let mut reg = !self.fields.is_empty(); |
869 | 53.4k | let mut malformed = false; |
870 | 53.4k | let mut header_list_way_too_large = false; |
871 | 53.4k | let mut headers_size = self.calculate_header_list_size(); |
872 | 53.4k | let max_header_list_abuse_size = |
873 | 53.4k | max_header_list_size.saturating_mul(MAX_HEADER_LIST_ABUSE_MULTIPLIER); |
874 | | |
875 | | macro_rules! check_size { |
876 | | () => {{ |
877 | | if headers_size > max_header_list_abuse_size { |
878 | | tracing::trace!("load_hpack; header list size over abuse max"); |
879 | | header_list_way_too_large = true; |
880 | | ControlFlow::Break(()) |
881 | | } else { |
882 | | if headers_size >= max_header_list_size && !self.is_over_size { |
883 | | tracing::trace!("load_hpack; header list size over max"); |
884 | | self.is_over_size = true; |
885 | | } |
886 | | ControlFlow::Continue(()) |
887 | | } |
888 | | }}; |
889 | | } |
890 | | |
891 | | macro_rules! set_pseudo { |
892 | | ($field:ident, $val:expr) => {{ |
893 | | if reg { |
894 | | tracing::trace!("load_hpack; header malformed -- pseudo not at head of block"); |
895 | | malformed = true; |
896 | | } else if self.pseudo.$field.is_some() { |
897 | | tracing::trace!("load_hpack; header malformed -- repeated pseudo"); |
898 | | malformed = true; |
899 | | } else { |
900 | | let __val = $val; |
901 | | headers_size += |
902 | | decoded_header_size(stringify!($field).len() + 1, __val.as_str().len()); |
903 | | if check_size!().is_break() { |
904 | | return ControlFlow::Break(()); |
905 | | } |
906 | | if !self.is_over_size { |
907 | | self.pseudo.$field = Some(__val); |
908 | | } |
909 | | } |
910 | | }}; |
911 | | } |
912 | | |
913 | 53.4k | let mut cursor = Cursor::new(src); |
914 | | |
915 | | // If the header frame is malformed, we still have to continue decoding |
916 | | // the headers. A malformed header frame is a stream level error, but |
917 | | // the hpack state is connection level. In order to maintain correct |
918 | | // state for other streams, the hpack decoding process must complete. |
919 | 726k | let res = decoder.decode(&mut cursor, |header| { |
920 | | use crate::hpack::Header::*; |
921 | | |
922 | 726k | match header { |
923 | 534k | Field { name, value } => { |
924 | | // Connection level header fields are not supported and must |
925 | | // result in a protocol error. |
926 | | |
927 | 534k | if name == header::CONNECTION |
928 | 533k | || name == header::TRANSFER_ENCODING |
929 | 528k | || name == header::UPGRADE |
930 | 528k | || name == "keep-alive" |
931 | 528k | || name == "proxy-connection" |
932 | | { |
933 | 6.31k | tracing::trace!("load_hpack; connection level header"); |
934 | 6.31k | malformed = true; |
935 | 528k | } else if name == header::TE && value != "trailers" { |
936 | 537 | tracing::trace!( |
937 | 0 | "load_hpack; TE header not set to trailers; val={:?}", |
938 | | value |
939 | | ); |
940 | 537 | malformed = true; |
941 | | } else { |
942 | 527k | reg = true; |
943 | | |
944 | 527k | let header_size = decoded_header_size(name.as_str().len(), value.len()); |
945 | 527k | headers_size += header_size; |
946 | 527k | if check_size!().is_break() { |
947 | 0 | return ControlFlow::Break(()); |
948 | 527k | } |
949 | 527k | if !self.is_over_size { |
950 | 527k | self.field_size += header_size; |
951 | 527k | if self.fields.try_append(name, value).is_err() { |
952 | 0 | // HeaderMap capacity exceeded — treat as over-size |
953 | 0 | // so the stream is rejected downstream (RST_STREAM / 431) |
954 | 0 | // instead of panicking on the 24,577th unique header. |
955 | 0 | self.is_over_size = true; |
956 | 527k | } |
957 | 0 | } |
958 | | } |
959 | | } |
960 | 18.5k | Authority(v) => set_pseudo!(authority, v), |
961 | 47.9k | Method(v) => set_pseudo!(method, v), |
962 | 24.4k | Scheme(v) => set_pseudo!(scheme, v), |
963 | 21.8k | Path(v) => set_pseudo!(path, v), |
964 | 883 | Protocol(v) => set_pseudo!(protocol, v), |
965 | 77.8k | Status(v) => set_pseudo!(status, v), |
966 | | } |
967 | | |
968 | 726k | ControlFlow::Continue(()) |
969 | 726k | }); |
970 | | |
971 | 53.4k | match res { |
972 | 37.7k | Ok(()) => {} |
973 | 15.7k | Err(e) => { |
974 | 15.7k | tracing::trace!("hpack decoding error; err={:?}", e); |
975 | 15.7k | return Err(e.into()); |
976 | | } |
977 | | } |
978 | | |
979 | 37.7k | if header_list_way_too_large { |
980 | 0 | tracing::trace!("header list way too large; aborting connection"); |
981 | 0 | return Err(Error::HeaderListWayTooLarge); |
982 | 37.7k | } |
983 | | |
984 | 37.7k | if malformed { |
985 | 28.0k | tracing::trace!("malformed message"); |
986 | 28.0k | return Err(Error::MalformedMessage); |
987 | 9.69k | } |
988 | | |
989 | 9.69k | Ok(()) |
990 | 53.4k | } |
991 | | |
992 | 218k | fn into_encoding(self, encoder: &mut hpack::Encoder) -> EncodingHeaderBlock { |
993 | 218k | let mut hpack = encoder.take_scratch(); |
994 | 218k | hpack.clear(); |
995 | 218k | let headers = Iter { |
996 | 218k | pseudo: Some(self.pseudo), |
997 | 218k | fields: self.fields.into_iter(), |
998 | 218k | }; |
999 | | |
1000 | 218k | encoder.encode(headers, &mut hpack); |
1001 | | |
1002 | 218k | EncodingHeaderBlock { hpack } |
1003 | 218k | } |
1004 | | |
1005 | | /// Calculates the size of the currently decoded header list. |
1006 | | /// |
1007 | | /// According to http://httpwg.org/specs/rfc7540.html#SETTINGS_MAX_HEADER_LIST_SIZE |
1008 | | /// |
1009 | | /// > The value is based on the uncompressed size of header fields, |
1010 | | /// > including the length of the name and value in octets plus an |
1011 | | /// > overhead of 32 octets for each header field. |
1012 | 53.4k | fn calculate_header_list_size(&self) -> usize { |
1013 | | macro_rules! pseudo_size { |
1014 | | ($name:ident) => {{ |
1015 | | self.pseudo |
1016 | | .$name |
1017 | | .as_ref() |
1018 | 6.99k | .map(|m| decoded_header_size(stringify!($name).len() + 1, m.as_str().len())) <h2::frame::headers::HeaderBlock>::calculate_header_list_size::{closure#0}Line | Count | Source | 1018 | 1.85k | .map(|m| decoded_header_size(stringify!($name).len() + 1, m.as_str().len())) |
<h2::frame::headers::HeaderBlock>::calculate_header_list_size::{closure#2}Line | Count | Source | 1018 | 81 | .map(|m| decoded_header_size(stringify!($name).len() + 1, m.as_str().len())) |
<h2::frame::headers::HeaderBlock>::calculate_header_list_size::{closure#3}Line | Count | Source | 1018 | 1.99k | .map(|m| decoded_header_size(stringify!($name).len() + 1, m.as_str().len())) |
<h2::frame::headers::HeaderBlock>::calculate_header_list_size::{closure#4}Line | Count | Source | 1018 | 2.02k | .map(|m| decoded_header_size(stringify!($name).len() + 1, m.as_str().len())) |
<h2::frame::headers::HeaderBlock>::calculate_header_list_size::{closure#1}Line | Count | Source | 1018 | 1.03k | .map(|m| decoded_header_size(stringify!($name).len() + 1, m.as_str().len())) |
|
1019 | | .unwrap_or(0) |
1020 | | }}; |
1021 | | } |
1022 | | |
1023 | 53.4k | pseudo_size!(method) |
1024 | 53.4k | + pseudo_size!(scheme) |
1025 | 53.4k | + pseudo_size!(status) |
1026 | 53.4k | + pseudo_size!(authority) |
1027 | 53.4k | + pseudo_size!(path) |
1028 | 53.4k | + self.field_size |
1029 | 53.4k | } |
1030 | | } |
1031 | | |
1032 | 400k | fn calculate_headermap_size(map: &HeaderMap) -> usize { |
1033 | 400k | map.iter() |
1034 | 400k | .map(|(name, value)| decoded_header_size(name.as_str().len(), value.len())) |
1035 | 400k | .sum::<usize>() |
1036 | 400k | } |
1037 | | |
1038 | 557k | fn decoded_header_size(name: usize, value: usize) -> usize { |
1039 | 557k | name + value + 32 |
1040 | 557k | } |
1041 | | |
1042 | | #[cfg(test)] |
1043 | | mod test { |
1044 | | use super::*; |
1045 | | use crate::frame; |
1046 | | use crate::hpack::{huffman, Encoder}; |
1047 | | |
1048 | | #[test] |
1049 | | fn test_nameless_header_at_resume() { |
1050 | | let mut encoder = Encoder::default(); |
1051 | | let mut dst = BytesMut::new(); |
1052 | | |
1053 | | let headers = Headers::new( |
1054 | | StreamId::ZERO, |
1055 | | Default::default(), |
1056 | | HeaderMap::from_iter(vec![ |
1057 | | ( |
1058 | | HeaderName::from_static("hello"), |
1059 | | HeaderValue::from_static("world"), |
1060 | | ), |
1061 | | ( |
1062 | | HeaderName::from_static("hello"), |
1063 | | HeaderValue::from_static("zomg"), |
1064 | | ), |
1065 | | ( |
1066 | | HeaderName::from_static("hello"), |
1067 | | HeaderValue::from_static("sup"), |
1068 | | ), |
1069 | | ]), |
1070 | | ); |
1071 | | |
1072 | | let continuation = headers |
1073 | | .encode(&mut encoder, &mut (&mut dst).limit(frame::HEADER_LEN + 8)) |
1074 | | .unwrap(); |
1075 | | |
1076 | | assert_eq!(17, dst.len()); |
1077 | | assert_eq!([0, 0, 8, 1, 0, 0, 0, 0, 0], &dst[0..9]); |
1078 | | assert_eq!(&[0x40, 0x80 | 4], &dst[9..11]); |
1079 | | assert_eq!("hello", huff_decode(&dst[11..15])); |
1080 | | assert_eq!(0x80 | 4, dst[15]); |
1081 | | |
1082 | | let mut world = dst[16..17].to_owned(); |
1083 | | |
1084 | | dst.clear(); |
1085 | | |
1086 | | assert!(continuation |
1087 | | .encode(&mut (&mut dst).limit(frame::HEADER_LEN + 16)) |
1088 | | .is_none()); |
1089 | | |
1090 | | world.extend_from_slice(&dst[9..12]); |
1091 | | assert_eq!("world", huff_decode(&world)); |
1092 | | |
1093 | | assert_eq!(24, dst.len()); |
1094 | | assert_eq!([0, 0, 15, 9, 4, 0, 0, 0, 0], &dst[0..9]); |
1095 | | |
1096 | | // // Next is not indexed |
1097 | | assert_eq!(&[15, 47, 0x80 | 3], &dst[12..15]); |
1098 | | assert_eq!("zomg", huff_decode(&dst[15..18])); |
1099 | | assert_eq!(&[15, 47, 0x80 | 3], &dst[18..21]); |
1100 | | assert_eq!("sup", huff_decode(&dst[21..])); |
1101 | | } |
1102 | | |
1103 | | fn huff_decode(src: &[u8]) -> BytesMut { |
1104 | | let mut buf = BytesMut::new(); |
1105 | | huffman::decode(src, &mut buf).unwrap() |
1106 | | } |
1107 | | |
1108 | | #[test] |
1109 | | fn test_connect_request_pseudo_headers_omits_path_and_scheme() { |
1110 | | // CONNECT requests MUST NOT include :scheme & :path pseudo-header fields |
1111 | | // See: https://datatracker.ietf.org/doc/html/rfc9113#section-8.5 |
1112 | | |
1113 | | assert_eq!( |
1114 | | Pseudo::request( |
1115 | | Method::CONNECT, |
1116 | | Uri::from_static("https://example.com:8443"), |
1117 | | None |
1118 | | ), |
1119 | | Pseudo { |
1120 | | method: Method::CONNECT.into(), |
1121 | | authority: BytesStr::from_static("example.com:8443").into(), |
1122 | | ..Default::default() |
1123 | | } |
1124 | | ); |
1125 | | |
1126 | | assert_eq!( |
1127 | | Pseudo::request( |
1128 | | Method::CONNECT, |
1129 | | Uri::from_static("https://example.com/test"), |
1130 | | None |
1131 | | ), |
1132 | | Pseudo { |
1133 | | method: Method::CONNECT.into(), |
1134 | | authority: BytesStr::from_static("example.com").into(), |
1135 | | ..Default::default() |
1136 | | } |
1137 | | ); |
1138 | | |
1139 | | assert_eq!( |
1140 | | Pseudo::request(Method::CONNECT, Uri::from_static("example.com:8443"), None), |
1141 | | Pseudo { |
1142 | | method: Method::CONNECT.into(), |
1143 | | authority: BytesStr::from_static("example.com:8443").into(), |
1144 | | ..Default::default() |
1145 | | } |
1146 | | ); |
1147 | | } |
1148 | | |
1149 | | #[test] |
1150 | | fn test_extended_connect_request_pseudo_headers_includes_path_and_scheme() { |
1151 | | // On requests that contain the :protocol pseudo-header field, the |
1152 | | // :scheme and :path pseudo-header fields of the target URI (see |
1153 | | // Section 5) MUST also be included. |
1154 | | // See: https://datatracker.ietf.org/doc/html/rfc8441#section-4 |
1155 | | |
1156 | | assert_eq!( |
1157 | | Pseudo::request( |
1158 | | Method::CONNECT, |
1159 | | Uri::from_static("https://example.com:8443"), |
1160 | | Protocol::from_static("the-bread-protocol").into() |
1161 | | ), |
1162 | | Pseudo { |
1163 | | method: Method::CONNECT.into(), |
1164 | | authority: BytesStr::from_static("example.com:8443").into(), |
1165 | | scheme: BytesStr::from_static("https").into(), |
1166 | | path: BytesStr::from_static("/").into(), |
1167 | | protocol: Protocol::from_static("the-bread-protocol").into(), |
1168 | | ..Default::default() |
1169 | | } |
1170 | | ); |
1171 | | |
1172 | | assert_eq!( |
1173 | | Pseudo::request( |
1174 | | Method::CONNECT, |
1175 | | Uri::from_static("https://example.com:8443/test"), |
1176 | | Protocol::from_static("the-bread-protocol").into() |
1177 | | ), |
1178 | | Pseudo { |
1179 | | method: Method::CONNECT.into(), |
1180 | | authority: BytesStr::from_static("example.com:8443").into(), |
1181 | | scheme: BytesStr::from_static("https").into(), |
1182 | | path: BytesStr::from_static("/test").into(), |
1183 | | protocol: Protocol::from_static("the-bread-protocol").into(), |
1184 | | ..Default::default() |
1185 | | } |
1186 | | ); |
1187 | | |
1188 | | assert_eq!( |
1189 | | Pseudo::request( |
1190 | | Method::CONNECT, |
1191 | | Uri::from_static("http://example.com/a/b/c"), |
1192 | | Protocol::from_static("the-bread-protocol").into() |
1193 | | ), |
1194 | | Pseudo { |
1195 | | method: Method::CONNECT.into(), |
1196 | | authority: BytesStr::from_static("example.com").into(), |
1197 | | scheme: BytesStr::from_static("http").into(), |
1198 | | path: BytesStr::from_static("/a/b/c").into(), |
1199 | | protocol: Protocol::from_static("the-bread-protocol").into(), |
1200 | | ..Default::default() |
1201 | | } |
1202 | | ); |
1203 | | } |
1204 | | |
1205 | | #[test] |
1206 | | fn test_options_request_with_empty_path_has_asterisk_as_pseudo_path() { |
1207 | | // an OPTIONS request for an "http" or "https" URI that does not include a path component; |
1208 | | // these MUST include a ":path" pseudo-header field with a value of '*' (see Section 7.1 of [HTTP]). |
1209 | | // See: https://datatracker.ietf.org/doc/html/rfc9113#section-8.3.1 |
1210 | | assert_eq!( |
1211 | | Pseudo::request(Method::OPTIONS, Uri::from_static("example.com:8080"), None,), |
1212 | | Pseudo { |
1213 | | method: Method::OPTIONS.into(), |
1214 | | authority: BytesStr::from_static("example.com:8080").into(), |
1215 | | path: BytesStr::from_static("*").into(), |
1216 | | ..Default::default() |
1217 | | } |
1218 | | ); |
1219 | | } |
1220 | | |
1221 | | #[test] |
1222 | | fn test_try_append_prevents_panic_on_max_size_reached() { |
1223 | | // Verify that decoding >24,577 unique headers sets `is_over_size` |
1224 | | // instead of panicking via HeaderMap::append(). |
1225 | | // |
1226 | | // HeaderMap::MAX_SIZE = 32,768. With 75% load factor, max entries = 24,576. |
1227 | | // try_append returns Err(MaxSizeReached) at entry 24,577. |
1228 | | // Before the fix (using append), this panicked. |
1229 | | // |
1230 | | // We manually construct HPACK bytes for 25,000 unique headers because |
1231 | | // creating a HeaderMap with that many entries also panics on construction. |
1232 | | |
1233 | | // Build HPACK-encoded block: |
1234 | | // Pseudo-headers (indexed refs to static table): |
1235 | | // :method GET → 0x82 (static index 2) |
1236 | | // :scheme http → 0x86 (static index 6) |
1237 | | // :path / → 0x84 (static index 4) |
1238 | | // :authority "localhost" → literal with indexing (name index 0) |
1239 | | // |
1240 | | // Then 25,000 unique headers: "literal without indexing, new name" |
1241 | | // 0x00 → literal without indexing, name index 0 |
1242 | | // <name_len> <name_bytes> |
1243 | | // <value_len> <value_bytes> |
1244 | | |
1245 | | let num_headers = 25_000; |
1246 | | |
1247 | | // Build the HPACK block |
1248 | | let mut hpack = Vec::new(); |
1249 | | |
1250 | | // Pseudo-headers |
1251 | | hpack.push(0x82u8); // :method GET (static index 2) |
1252 | | hpack.push(0x86); // :scheme http (static index 6) |
1253 | | hpack.push(0x84); // :path / (static index 4) |
1254 | | |
1255 | | // :authority "localhost" — literal with incremental indexing |
1256 | | hpack.push(0x41); // literal with indexing, name index 1 (= ":authority") |
1257 | | hpack.push(0x09); // value length 9 |
1258 | | hpack.extend_from_slice(b"localhost"); |
1259 | | |
1260 | | // 25,000 unique headers: "literal without indexing, new name" |
1261 | | // Format: 0x00 + name_len + name + value_len + value |
1262 | | for i in 0..num_headers { |
1263 | | let name = format!("x-h-{i}"); |
1264 | | hpack.push(0x00u8); // literal without indexing, name index 0 |
1265 | | hpack.push(name.len() as u8); |
1266 | | hpack.extend_from_slice(name.as_bytes()); |
1267 | | hpack.push(1u8); // value length 1 |
1268 | | hpack.push(b'v'); |
1269 | | } |
1270 | | |
1271 | | // Build the HTTP/2 HEADERS frame: 9-byte header + HPACK payload |
1272 | | let payload_len = hpack.len(); |
1273 | | let mut frame = BytesMut::with_capacity(9 + payload_len); |
1274 | | |
1275 | | // Frame header: 3 bytes length, 1 byte type (0x01=HEADERS), 1 byte flags, 4 bytes stream_id |
1276 | | frame.put_u8(((payload_len >> 16) & 0xFF) as u8); |
1277 | | frame.put_u8(((payload_len >> 8) & 0xFF) as u8); |
1278 | | frame.put_u8((payload_len & 0xFF) as u8); |
1279 | | frame.put_u8(0x01); // type: HEADERS |
1280 | | frame.put_u8(0x04); // flags: END_HEADERS |
1281 | | frame.put_u32(1); // stream_id: 1 |
1282 | | |
1283 | | frame.extend_from_slice(&hpack); |
1284 | | |
1285 | | // Parse the HEADERS frame |
1286 | | let head = Head::parse(&frame[..9]); |
1287 | | let payload = BytesMut::from(&frame[9..]); |
1288 | | let (mut headers, mut hpack_data) = Headers::load(head, payload).unwrap(); |
1289 | | // hpack_data contains the HPACK payload (no padding/priority in our frame) |
1290 | | |
1291 | | // Decode the HPACK block — this should NOT panic |
1292 | | let mut decoder = hpack::Decoder::new(4096); |
1293 | | const DEFAULT_MAX_HEADER_LIST_SIZE: usize = 16 << 20; // 16 MB |
1294 | | headers |
1295 | | .load_hpack(&mut hpack_data, DEFAULT_MAX_HEADER_LIST_SIZE, &mut decoder) |
1296 | | .expect("load_hpack should return Ok"); |
1297 | | |
1298 | | // Verify that is_over_size was set (try_append returned Err) |
1299 | | assert!( |
1300 | | headers.is_over_size(), |
1301 | | "is_over_size should be true when HeaderMap capacity is exceeded" |
1302 | | ); |
1303 | | } |
1304 | | |
1305 | | #[test] |
1306 | | fn test_non_option_and_non_connect_requests_include_path_and_scheme() { |
1307 | | let methods = [ |
1308 | | Method::GET, |
1309 | | Method::POST, |
1310 | | Method::PUT, |
1311 | | Method::DELETE, |
1312 | | Method::HEAD, |
1313 | | Method::PATCH, |
1314 | | Method::TRACE, |
1315 | | ]; |
1316 | | |
1317 | | for method in methods { |
1318 | | assert_eq!( |
1319 | | Pseudo::request( |
1320 | | method.clone(), |
1321 | | Uri::from_static("http://example.com:8080"), |
1322 | | None, |
1323 | | ), |
1324 | | Pseudo { |
1325 | | method: method.clone().into(), |
1326 | | authority: BytesStr::from_static("example.com:8080").into(), |
1327 | | scheme: BytesStr::from_static("http").into(), |
1328 | | path: BytesStr::from_static("/").into(), |
1329 | | ..Default::default() |
1330 | | } |
1331 | | ); |
1332 | | assert_eq!( |
1333 | | Pseudo::request( |
1334 | | method.clone(), |
1335 | | Uri::from_static("https://example.com/a/b/c"), |
1336 | | None, |
1337 | | ), |
1338 | | Pseudo { |
1339 | | method: method.into(), |
1340 | | authority: BytesStr::from_static("example.com").into(), |
1341 | | scheme: BytesStr::from_static("https").into(), |
1342 | | path: BytesStr::from_static("/a/b/c").into(), |
1343 | | ..Default::default() |
1344 | | } |
1345 | | ); |
1346 | | } |
1347 | | } |
1348 | | } |