1use core::fmt;
4
5use super::codepoints::Http3ErrorCode;
6use super::enums::message::Http3HeadersKind;
7use super::frame::Http3Data;
8use super::headers::Http3DecodedHeaderSection;
9
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12pub enum Http3ResponseContext {
13 Ordinary,
15 Head,
17 Connect,
19}
20
21#[derive(Clone, Copy, Debug, Eq, PartialEq)]
23pub enum Http3ContentKind {
24 Request,
26 Response(Http3ResponseContext),
28 Push,
30}
31
32#[derive(Clone, Copy, Debug, Eq, PartialEq)]
34pub enum Http3ContentDisposition {
35 AwaitingFinalResponse,
37 HttpMessage,
39 ConnectTunnel,
41}
42
43#[derive(Clone, Copy, Debug, Eq, PartialEq)]
45pub enum Http3ContentOperation {
46 Data,
48 InitialRequestHeaders,
50 ResponseHeaders,
52 Trailers,
54}
55
56#[derive(Clone, Copy, Debug, Eq, PartialEq)]
61pub enum Http3MessageContentError {
62 InvalidContentLength {
64 field_index: usize,
66 member_index: usize,
68 },
69 ContentLengthOverflow {
71 field_index: usize,
73 member_index: usize,
75 },
76 ConflictingContentLength {
78 field_index: usize,
80 member_index: usize,
82 expected: u64,
84 actual: u64,
86 },
87 ContentLengthInTrailers {
89 field_index: usize,
91 },
92 ProhibitedContentLength {
94 field_index: usize,
96 },
97 UnexpectedHeaderSection {
99 expected: Http3ContentOperation,
101 actual: Http3HeadersKind,
103 },
104 OperationNotReady {
106 operation: Http3ContentOperation,
108 },
109 ContentNotAllowed {
111 data_length: usize,
113 },
114 DataLengthNotRepresentable {
116 length: usize,
118 },
119 ReceivedLengthOverflow {
121 received_length: u64,
123 frame_length: u64,
125 },
126 ContentLengthMismatch {
128 declared: u64,
130 received: u64,
132 },
133 NotHttpMessageContent,
135 IncompleteMessage,
137}
138
139impl Http3MessageContentError {
140 pub const fn error_code(self) -> Option<Http3ErrorCode> {
142 match self {
143 Self::UnexpectedHeaderSection { .. }
144 | Self::OperationNotReady { .. }
145 | Self::NotHttpMessageContent => None,
146 Self::InvalidContentLength { .. }
147 | Self::ContentLengthOverflow { .. }
148 | Self::ConflictingContentLength { .. }
149 | Self::ContentLengthInTrailers { .. }
150 | Self::ProhibitedContentLength { .. }
151 | Self::ContentNotAllowed { .. }
152 | Self::DataLengthNotRepresentable { .. }
153 | Self::ReceivedLengthOverflow { .. }
154 | Self::ContentLengthMismatch { .. }
155 | Self::IncompleteMessage => Some(Http3ErrorCode::MESSAGE_ERROR),
156 }
157 }
158}
159
160impl fmt::Display for Http3MessageContentError {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 match self {
163 Self::InvalidContentLength {
164 field_index,
165 member_index,
166 } => write!(
167 f,
168 "HTTP/3 Content-Length field {field_index} member {member_index} is invalid"
169 ),
170 Self::ContentLengthOverflow {
171 field_index,
172 member_index,
173 } => write!(
174 f,
175 "HTTP/3 Content-Length field {field_index} member {member_index} overflows"
176 ),
177 Self::ConflictingContentLength {
178 field_index,
179 member_index,
180 expected,
181 actual,
182 } => write!(
183 f,
184 "HTTP/3 Content-Length field {field_index} member {member_index} is {actual}, expected {expected}"
185 ),
186 Self::ContentLengthInTrailers { field_index } => {
187 write!(
188 f,
189 "HTTP/3 trailer field {field_index} contains Content-Length"
190 )
191 }
192 Self::ProhibitedContentLength { field_index } => {
193 write!(
194 f,
195 "HTTP/3 field {field_index} has prohibited Content-Length"
196 )
197 }
198 Self::UnexpectedHeaderSection { expected, actual } => write!(
199 f,
200 "HTTP/3 {actual:?} header section is unexpected while accepting {expected:?}"
201 ),
202 Self::OperationNotReady { operation } => {
203 write!(
204 f,
205 "HTTP/3 {operation:?} operation is not ready in the current message phase"
206 )
207 }
208 Self::ContentNotAllowed { data_length } => {
209 write!(
210 f,
211 "HTTP/3 DATA payload of {data_length} bytes is not allowed"
212 )
213 }
214 Self::DataLengthNotRepresentable { length } => {
215 write!(
216 f,
217 "HTTP/3 DATA payload length {length} cannot be represented as u64"
218 )
219 }
220 Self::ReceivedLengthOverflow {
221 received_length,
222 frame_length,
223 } => write!(
224 f,
225 "HTTP/3 received content length overflows: {received_length} plus {frame_length}"
226 ),
227 Self::ContentLengthMismatch { declared, received } => write!(
228 f,
229 "HTTP/3 Content-Length mismatch: declared {declared}, received {received}"
230 ),
231 Self::NotHttpMessageContent => {
232 f.write_str("HTTP/3 operation belongs to CONNECT tunnel handling, not HTTP content")
233 }
234 Self::IncompleteMessage => f.write_str("HTTP/3 message content headers are incomplete"),
235 }
236 }
237}
238
239impl core::error::Error for Http3MessageContentError {}
240
241#[derive(Clone, Copy, Debug, Eq, PartialEq)]
242enum Phase {
243 BeforeInitial,
244 BeforeFinal,
245 Content,
246 NoContent,
247 NoContentTrailers,
248 Tunnel,
249 Trailers,
250}
251
252#[derive(Clone, Copy, Debug, Eq, PartialEq)]
258pub struct Http3MessageContentState {
259 kind: Http3ContentKind,
260 phase: Phase,
261 declared_length: Option<u64>,
262 received_length: u64,
263}
264
265impl Http3MessageContentState {
266 pub const fn new(kind: Http3ContentKind) -> Self {
268 let phase = match kind {
269 Http3ContentKind::Request => Phase::BeforeInitial,
270 Http3ContentKind::Response(_) | Http3ContentKind::Push => Phase::BeforeFinal,
271 };
272 Self {
273 kind,
274 phase,
275 declared_length: None,
276 received_length: 0,
277 }
278 }
279
280 pub const fn kind(self) -> Http3ContentKind {
282 self.kind
283 }
284
285 pub const fn declared_length(self) -> Option<u64> {
287 self.declared_length
288 }
289
290 pub const fn received_length(self) -> u64 {
294 self.received_length
295 }
296
297 pub fn accept_initial_headers(
299 &mut self,
300 section: Http3DecodedHeaderSection<'_>,
301 ) -> Result<Http3ContentDisposition, Http3MessageContentError> {
302 match self.phase {
303 Phase::BeforeInitial => self.accept_request(section),
304 Phase::BeforeFinal => self.accept_response(section),
305 _ => Err(Http3MessageContentError::OperationNotReady {
306 operation: self.initial_headers_operation(),
307 }),
308 }
309 }
310
311 pub fn receive_data(&mut self, data: Http3Data<'_>) -> Result<(), Http3MessageContentError> {
313 match self.phase {
314 Phase::Tunnel => Err(Http3MessageContentError::NotHttpMessageContent),
315 Phase::NoContent | Phase::NoContentTrailers if data.data().is_empty() => Ok(()),
316 Phase::NoContent | Phase::NoContentTrailers => {
317 Err(Http3MessageContentError::ContentNotAllowed {
318 data_length: data.data().len(),
319 })
320 }
321 Phase::Content => {
322 let frame_length = u64::try_from(data.data().len()).map_err(|_| {
323 Http3MessageContentError::DataLengthNotRepresentable {
324 length: data.data().len(),
325 }
326 })?;
327 let received_length = self.received_length.checked_add(frame_length).ok_or(
328 Http3MessageContentError::ReceivedLengthOverflow {
329 received_length: self.received_length,
330 frame_length,
331 },
332 )?;
333 if let Some(declared) = self.declared_length
334 && received_length > declared
335 {
336 return Err(Http3MessageContentError::ContentLengthMismatch {
337 declared,
338 received: received_length,
339 });
340 }
341 self.received_length = received_length;
342 Ok(())
343 }
344 Phase::BeforeInitial | Phase::BeforeFinal | Phase::Trailers => {
345 Err(Http3MessageContentError::OperationNotReady {
346 operation: Http3ContentOperation::Data,
347 })
348 }
349 }
350 }
351
352 pub fn accept_trailers(
354 &mut self,
355 section: Http3DecodedHeaderSection<'_>,
356 ) -> Result<(), Http3MessageContentError> {
357 if section.kind() != Http3HeadersKind::Trailers {
358 return Err(Http3MessageContentError::UnexpectedHeaderSection {
359 expected: Http3ContentOperation::Trailers,
360 actual: section.kind(),
361 });
362 }
363 match self.phase {
364 Phase::Tunnel => return Err(Http3MessageContentError::NotHttpMessageContent),
365 Phase::Content | Phase::NoContentTrailers => {}
366 _ => {
367 return Err(Http3MessageContentError::OperationNotReady {
368 operation: Http3ContentOperation::Trailers,
369 });
370 }
371 }
372 if let Some(field_index) = first_content_length(section) {
373 return Err(Http3MessageContentError::ContentLengthInTrailers { field_index });
374 }
375 if self.phase == Phase::Content {
376 self.length_matches()?;
377 }
378 self.phase = Phase::Trailers;
379 Ok(())
380 }
381
382 pub fn finish(&self) -> Result<(), Http3MessageContentError> {
384 match self.phase {
385 Phase::BeforeInitial | Phase::BeforeFinal => {
386 Err(Http3MessageContentError::IncompleteMessage)
387 }
388 Phase::Content => self.length_matches(),
389 Phase::NoContent | Phase::NoContentTrailers | Phase::Tunnel | Phase::Trailers => Ok(()),
390 }
391 }
392
393 fn accept_request(
394 &mut self,
395 section: Http3DecodedHeaderSection<'_>,
396 ) -> Result<Http3ContentDisposition, Http3MessageContentError> {
397 if section.kind() != Http3HeadersKind::Request {
398 return Err(Http3MessageContentError::UnexpectedHeaderSection {
399 expected: Http3ContentOperation::InitialRequestHeaders,
400 actual: section.kind(),
401 });
402 }
403 let declared_length = parse_content_length(section)?;
404 let connect = section
405 .section()
406 .iter()
407 .any(|field| field.name() == b":method" && field.value() == b"CONNECT");
408 self.declared_length = declared_length;
409 self.phase = if connect {
410 Phase::Tunnel
411 } else {
412 Phase::Content
413 };
414 Ok(if connect {
415 Http3ContentDisposition::ConnectTunnel
416 } else {
417 Http3ContentDisposition::HttpMessage
418 })
419 }
420
421 fn accept_response(
422 &mut self,
423 section: Http3DecodedHeaderSection<'_>,
424 ) -> Result<Http3ContentDisposition, Http3MessageContentError> {
425 match section.kind() {
426 Http3HeadersKind::InformationalResponse => {
427 if let Some(field_index) = first_content_length(section) {
428 return Err(Http3MessageContentError::ProhibitedContentLength { field_index });
429 }
430 Ok(Http3ContentDisposition::AwaitingFinalResponse)
431 }
432 Http3HeadersKind::FinalResponse => self.accept_final_response(section),
433 _ => Err(Http3MessageContentError::UnexpectedHeaderSection {
434 expected: Http3ContentOperation::ResponseHeaders,
435 actual: section.kind(),
436 }),
437 }
438 }
439
440 fn accept_final_response(
441 &mut self,
442 section: Http3DecodedHeaderSection<'_>,
443 ) -> Result<Http3ContentDisposition, Http3MessageContentError> {
444 let status = response_status(section);
445 let prohibited = match self.kind {
446 Http3ContentKind::Response(Http3ResponseContext::Connect) => {
447 (200..=299).contains(&status)
448 }
449 Http3ContentKind::Response(Http3ResponseContext::Head)
450 | Http3ContentKind::Response(Http3ResponseContext::Ordinary)
451 | Http3ContentKind::Push => status == 204,
452 Http3ContentKind::Request => false,
453 };
454 if prohibited && let Some(field_index) = first_content_length(section) {
455 return Err(Http3MessageContentError::ProhibitedContentLength { field_index });
456 }
457 let declared_length = parse_content_length(section)?;
458 let (phase, disposition) = match self.kind {
459 Http3ContentKind::Response(Http3ResponseContext::Connect)
460 if (200..=299).contains(&status) =>
461 {
462 (Phase::Tunnel, Http3ContentDisposition::ConnectTunnel)
463 }
464 _ if status == 204 => (Phase::NoContent, Http3ContentDisposition::HttpMessage),
465 Http3ContentKind::Response(Http3ResponseContext::Ordinary)
466 | Http3ContentKind::Response(Http3ResponseContext::Head)
467 | Http3ContentKind::Push
468 if status == 205 =>
469 {
470 (
471 Phase::NoContentTrailers,
472 Http3ContentDisposition::HttpMessage,
473 )
474 }
475 Http3ContentKind::Response(Http3ResponseContext::Head) => (
476 Phase::NoContentTrailers,
477 Http3ContentDisposition::HttpMessage,
478 ),
479 Http3ContentKind::Response(Http3ResponseContext::Ordinary) | Http3ContentKind::Push
480 if status == 304 =>
481 {
482 (
483 Phase::NoContentTrailers,
484 Http3ContentDisposition::HttpMessage,
485 )
486 }
487 _ => (Phase::Content, Http3ContentDisposition::HttpMessage),
488 };
489 self.declared_length = declared_length;
490 self.phase = phase;
491 Ok(disposition)
492 }
493
494 fn length_matches(&self) -> Result<(), Http3MessageContentError> {
495 match self.declared_length {
496 Some(declared) if declared != self.received_length => {
497 Err(Http3MessageContentError::ContentLengthMismatch {
498 declared,
499 received: self.received_length,
500 })
501 }
502 _ => Ok(()),
503 }
504 }
505
506 const fn initial_headers_operation(&self) -> Http3ContentOperation {
507 match self.kind {
508 Http3ContentKind::Request => Http3ContentOperation::InitialRequestHeaders,
509 Http3ContentKind::Response(_) | Http3ContentKind::Push => {
510 Http3ContentOperation::ResponseHeaders
511 }
512 }
513 }
514}
515
516fn first_content_length(section: Http3DecodedHeaderSection<'_>) -> Option<usize> {
517 section
518 .section()
519 .iter()
520 .enumerate()
521 .find_map(|(index, field)| (field.name() == b"content-length").then_some(index))
522}
523
524fn parse_content_length(
525 section: Http3DecodedHeaderSection<'_>,
526) -> Result<Option<u64>, Http3MessageContentError> {
527 let mut declared = None;
528 for (field_index, field) in section.section().iter().enumerate() {
529 if field.name() != b"content-length" {
530 continue;
531 }
532 for (member_index, member) in field.value().split(|byte| *byte == b',').enumerate() {
533 let value = trim_ows(member);
534 let actual = parse_decimal(value, field_index, member_index)?;
535 if let Some(expected) = declared
536 && expected != actual
537 {
538 return Err(Http3MessageContentError::ConflictingContentLength {
539 field_index,
540 member_index,
541 expected,
542 actual,
543 });
544 }
545 declared = Some(actual);
546 }
547 }
548 Ok(declared)
549}
550
551fn parse_decimal(
552 value: &[u8],
553 field_index: usize,
554 member_index: usize,
555) -> Result<u64, Http3MessageContentError> {
556 if value.is_empty() || !value.iter().all(|byte| byte.is_ascii_digit()) {
557 return Err(Http3MessageContentError::InvalidContentLength {
558 field_index,
559 member_index,
560 });
561 }
562 let mut result = 0_u64;
563 for byte in value {
564 result = result
565 .checked_mul(10)
566 .and_then(|number| number.checked_add(u64::from(*byte - b'0')))
567 .ok_or(Http3MessageContentError::ContentLengthOverflow {
568 field_index,
569 member_index,
570 })?;
571 }
572 Ok(result)
573}
574
575fn trim_ows(mut value: &[u8]) -> &[u8] {
576 while matches!(value.first(), Some(b' ' | b'\t')) {
577 value = &value[1..];
578 }
579 while matches!(value.last(), Some(b' ' | b'\t')) {
580 value = &value[..value.len() - 1];
581 }
582 value
583}
584
585fn response_status(section: Http3DecodedHeaderSection<'_>) -> u16 {
586 for field in section.section().iter() {
587 if field.name() == b":status" {
588 let value = field.value();
589 if let (Some(hundreds), Some(tens), Some(ones)) =
590 (value.first(), value.get(1), value.get(2))
591 {
592 return u16::from(*hundreds - b'0') * 100
593 + u16::from(*tens - b'0') * 10
594 + u16::from(*ones - b'0');
595 }
596 }
597 }
598 0
599}