1use crate::bytes::BufferReader;
2use crate::bytes::BufferWriter;
3use crate::bytes::BytesReader;
4use crate::bytes::BytesWriter;
5use crate::bytes::EndOfBuffer;
6use crate::ids::InvalidSessionId;
7use crate::ids::SessionId;
8use crate::varint::VarInt;
9use std::borrow::Cow;
10
11#[cfg(feature = "async")]
12use crate::bytes::AsyncRead;
13
14#[cfg(feature = "async")]
15use crate::bytes::AsyncWrite;
16
17#[cfg(feature = "async")]
18use crate::bytes;
19
20#[derive(Debug, thiserror::Error)]
22pub enum ParseError {
23 #[error("cannot parse HTTP3 frame as ID is unknown")]
25 UnknownFrame,
26
27 #[error("cannot parse HTTP3 frame as session ID is invalid")]
29 InvalidSessionId,
30
31 #[error("cannot parse HTTP3 frame as payload limit is reached")]
33 PayloadTooBig,
34}
35
36#[cfg(feature = "async")]
38#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
39#[derive(Debug, thiserror::Error)]
40pub enum IoReadError {
41 #[error(transparent)]
43 Parse(ParseError),
44
45 #[error(transparent)]
47 IO(bytes::IoReadError),
48}
49
50#[cfg(feature = "async")]
51impl From<bytes::IoReadError> for IoReadError {
52 #[inline(always)]
53 fn from(io_error: bytes::IoReadError) -> Self {
54 IoReadError::IO(io_error)
55 }
56}
57
58#[cfg(feature = "async")]
60pub type IoWriteError = bytes::IoWriteError;
61
62pub type FrameOwned = Frame<'static>;
64
65#[derive(Copy, Clone, Debug)]
67pub enum FrameKind {
68 Data,
70
71 Headers,
73
74 Settings,
76
77 WebTransport,
79
80 Exercise(VarInt),
82}
83
84impl FrameKind {
85 #[inline(always)]
87 pub const fn is_id_exercise(id: VarInt) -> bool {
88 id.into_inner() >= 0x21 && ((id.into_inner() - 0x21) % 0x1f == 0)
89 }
90
91 const fn parse(id: VarInt) -> Option<Self> {
92 match id {
93 frame_kind_ids::DATA => Some(FrameKind::Data),
94 frame_kind_ids::HEADERS => Some(FrameKind::Headers),
95 frame_kind_ids::SETTINGS => Some(FrameKind::Settings),
96 frame_kind_ids::WEBTRANSPORT_STREAM => Some(FrameKind::WebTransport),
97 id if FrameKind::is_id_exercise(id) => Some(FrameKind::Exercise(id)),
98 _ => None,
99 }
100 }
101
102 const fn id(self) -> VarInt {
103 match self {
104 FrameKind::Data => frame_kind_ids::DATA,
105 FrameKind::Headers => frame_kind_ids::HEADERS,
106 FrameKind::Settings => frame_kind_ids::SETTINGS,
107 FrameKind::WebTransport => frame_kind_ids::WEBTRANSPORT_STREAM,
108 FrameKind::Exercise(id) => id,
109 }
110 }
111}
112
113#[derive(Debug)]
115pub struct Frame<'a> {
116 kind: FrameKind,
117 payload: Cow<'a, [u8]>,
118 session_id: Option<SessionId>,
119}
120
121impl<'a> Frame<'a> {
122 const MAX_PARSE_PAYLOAD_ALLOWED: usize = 4096;
123
124 #[inline(always)]
130 pub fn new_headers(payload: Cow<'a, [u8]>) -> Self {
131 Self::new(FrameKind::Headers, payload, None)
132 }
133
134 #[inline(always)]
140 pub fn new_settings(payload: Cow<'a, [u8]>) -> Self {
141 Self::new(FrameKind::Settings, payload, None)
142 }
143
144 #[inline(always)]
146 pub fn new_webtransport(session_id: SessionId) -> Self {
147 Self::new(
148 FrameKind::WebTransport,
149 Cow::Owned(Default::default()),
150 Some(session_id),
151 )
152 }
153
154 #[inline(always)]
160 pub fn new_data(payload: Cow<'a, [u8]>) -> Self {
161 Self::new(FrameKind::Data, payload, None)
162 }
163
164 #[inline(always)]
171 pub fn new_exercise(id: VarInt, payload: Cow<'a, [u8]>) -> Self {
172 assert!(FrameKind::is_id_exercise(id));
173 Self::new(FrameKind::Exercise(id), payload, None)
174 }
175
176 pub fn read<R>(bytes_reader: &mut R) -> Result<Option<Self>, ParseError>
183 where
184 R: BytesReader<'a>,
185 {
186 let kind = match bytes_reader.get_varint() {
187 Some(kind_id) => FrameKind::parse(kind_id).ok_or(ParseError::UnknownFrame)?,
188 None => return Ok(None),
189 };
190
191 if matches!(kind, FrameKind::WebTransport) {
192 let session_id = match bytes_reader.get_varint() {
193 Some(session_id) => SessionId::try_from_varint(session_id)
194 .map_err(|InvalidSessionId| ParseError::InvalidSessionId)?,
195 None => return Ok(None),
196 };
197
198 Ok(Some(Self::new_webtransport(session_id)))
199 } else {
200 let payload_len = match bytes_reader.get_varint() {
201 Some(payload_len) => payload_len.into_inner() as usize,
202 None => return Ok(None),
203 };
204
205 if payload_len > Self::MAX_PARSE_PAYLOAD_ALLOWED {
206 return Err(ParseError::PayloadTooBig);
207 }
208
209 let payload = match bytes_reader.get_bytes(payload_len) {
210 Some(payload) => payload,
211 None => return Ok(None),
212 };
213
214 Ok(Some(Self::new(kind, Cow::Borrowed(payload), None)))
215 }
216 }
217
218 #[cfg(feature = "async")]
220 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
221 pub async fn read_async<R>(reader: &mut R) -> Result<Frame<'a>, IoReadError>
222 where
223 R: AsyncRead + Unpin + ?Sized,
224 {
225 use crate::bytes::BytesReaderAsync;
226
227 let kind_id = reader.get_varint().await?;
228 let kind = FrameKind::parse(kind_id).ok_or(IoReadError::Parse(ParseError::UnknownFrame))?;
229
230 if matches!(kind, FrameKind::WebTransport) {
231 let session_id =
232 SessionId::try_from_varint(reader.get_varint().await.map_err(|e| match e {
233 bytes::IoReadError::ImmediateFin => bytes::IoReadError::UnexpectedFin,
234 _ => e,
235 })?)
236 .map_err(|InvalidSessionId| IoReadError::Parse(ParseError::InvalidSessionId))?;
237
238 Ok(Self::new_webtransport(session_id))
239 } else {
240 let payload_len = reader
241 .get_varint()
242 .await
243 .map_err(|e| match e {
244 bytes::IoReadError::ImmediateFin => bytes::IoReadError::UnexpectedFin,
245 _ => e,
246 })?
247 .into_inner() as usize;
248
249 if payload_len > Self::MAX_PARSE_PAYLOAD_ALLOWED {
250 return Err(IoReadError::Parse(ParseError::PayloadTooBig));
251 }
252
253 let mut payload = vec![0; payload_len];
254
255 reader.get_buffer(&mut payload).await.map_err(|e| match e {
256 bytes::IoReadError::ImmediateFin => bytes::IoReadError::UnexpectedFin,
257 _ => e,
258 })?;
259
260 payload.shrink_to_fit();
261
262 Ok(Self::new(kind, Cow::Owned(payload), None))
263 }
264 }
265
266 pub fn read_from_buffer(
273 buffer_reader: &mut BufferReader<'a>,
274 ) -> Result<Option<Self>, ParseError> {
275 let mut buffer_reader_child = buffer_reader.child();
276
277 match Self::read(&mut *buffer_reader_child)? {
278 Some(frame) => {
279 buffer_reader_child.commit();
280 Ok(Some(frame))
281 }
282 None => Ok(None),
283 }
284 }
285
286 pub fn write<W>(&self, bytes_writer: &mut W) -> Result<(), EndOfBuffer>
298 where
299 W: BytesWriter,
300 {
301 bytes_writer.put_varint(self.kind.id())?;
302
303 if let Some(session_id) = self.session_id() {
304 bytes_writer.put_varint(session_id.into_varint())?;
305 } else {
306 bytes_writer.put_varint(
307 VarInt::try_from(self.payload.len() as u64)
308 .expect("Payload cannot be larger than varint max"),
309 )?;
310 bytes_writer.put_bytes(&self.payload)?;
311 }
312
313 Ok(())
314 }
315
316 #[cfg(feature = "async")]
322 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
323 pub async fn write_async<W>(&self, writer: &mut W) -> Result<(), IoWriteError>
324 where
325 W: AsyncWrite + Unpin + ?Sized,
326 {
327 use crate::bytes::BytesWriterAsync;
328
329 writer.put_varint(self.kind.id()).await?;
330
331 if let Some(session_id) = self.session_id() {
332 writer.put_varint(session_id.into_varint()).await?;
333 } else {
334 writer
335 .put_varint(
336 VarInt::try_from(self.payload.len() as u64)
337 .expect("Payload cannot be larger than varint max"),
338 )
339 .await?;
340 writer.put_buffer(&self.payload).await?;
341 }
342
343 Ok(())
344 }
345
346 pub fn write_to_buffer(&self, buffer_writer: &mut BufferWriter) -> Result<(), EndOfBuffer> {
354 if buffer_writer.capacity() < self.write_size() {
355 return Err(EndOfBuffer);
356 }
357
358 self.write(buffer_writer)
359 .expect("Enough capacity for frame");
360
361 Ok(())
362 }
363
364 pub fn write_size(&self) -> usize {
366 if let Some(session_id) = self.session_id() {
367 self.kind.id().size() + session_id.into_varint().size()
368 } else {
369 self.kind.id().size()
370 + VarInt::try_from(self.payload.len() as u64)
371 .expect("Payload cannot be larger than varint max")
372 .size()
373 + self.payload.len()
374 }
375 }
376
377 #[inline(always)]
379 pub const fn kind(&self) -> FrameKind {
380 self.kind
381 }
382
383 #[inline(always)]
385 pub fn payload(&self) -> &[u8] {
386 &self.payload
387 }
388
389 #[inline(always)]
392 pub fn session_id(&self) -> Option<SessionId> {
393 matches!(self.kind, FrameKind::WebTransport).then(|| {
394 self.session_id
395 .expect("WebTransport frame contains session id")
396 })
397 }
398
399 fn new(kind: FrameKind, payload: Cow<'a, [u8]>, session_id: Option<SessionId>) -> Self {
403 if let FrameKind::Exercise(id) = kind {
404 debug_assert!(FrameKind::is_id_exercise(id));
405 } else if let FrameKind::WebTransport = kind {
406 debug_assert!(payload.is_empty());
407 debug_assert!(session_id.is_some());
408 }
409
410 assert!(payload.len() <= VarInt::MAX.into_inner() as usize);
411
412 Self {
413 kind,
414 payload,
415 session_id,
416 }
417 }
418
419 #[cfg(test)]
420 pub(crate) fn into_owned<'b>(self) -> Frame<'b> {
421 Frame {
422 kind: self.kind,
423 payload: Cow::Owned(self.payload.into_owned()),
424 session_id: self.session_id,
425 }
426 }
427
428 #[cfg(test)]
429 pub(crate) fn serialize_any(kind: VarInt, payload: &[u8]) -> Vec<u8> {
430 let mut buffer = Vec::new();
431
432 Self {
433 kind: FrameKind::Exercise(kind),
434 payload: Cow::Owned(payload.to_vec()),
435 session_id: None,
436 }
437 .write(&mut buffer)
438 .unwrap();
439
440 buffer
441 }
442
443 #[cfg(test)]
444 pub(crate) fn serialize_webtransport(session_id: SessionId) -> Vec<u8> {
445 let mut buffer = Vec::new();
446
447 Self {
448 kind: FrameKind::WebTransport,
449 payload: Cow::Owned(Default::default()),
450 session_id: Some(session_id),
451 }
452 .write(&mut buffer)
453 .unwrap();
454
455 buffer
456 }
457}
458
459mod frame_kind_ids {
460 use crate::varint::VarInt;
461
462 pub const DATA: VarInt = VarInt::from_u32(0x00);
463 pub const HEADERS: VarInt = VarInt::from_u32(0x01);
464 pub const SETTINGS: VarInt = VarInt::from_u32(0x04);
465 pub const WEBTRANSPORT_STREAM: VarInt = VarInt::from_u32(0x41);
466}
467
468#[cfg(test)]
469mod tests {
470 use super::*;
471 use crate::headers::Headers;
472 use crate::settings::Settings;
473
474 #[test]
475 fn settings() {
476 let settings = Settings::builder()
477 .qpack_blocked_streams(VarInt::from_u32(1))
478 .qpack_max_table_capacity(VarInt::from_u32(2))
479 .enable_h3_datagrams()
480 .enable_webtransport()
481 .webtransport_max_sessions(VarInt::from_u32(3))
482 .build();
483
484 let frame = settings.generate_frame();
485 assert!(frame.session_id().is_none());
486 assert!(matches!(frame.kind(), FrameKind::Settings));
487
488 let frame = utils::assert_serde(frame);
489 Settings::with_frame(&frame).unwrap();
490 }
491
492 #[tokio::test]
493 async fn settings_async() {
494 let settings = Settings::builder()
495 .qpack_blocked_streams(VarInt::from_u32(1))
496 .qpack_max_table_capacity(VarInt::from_u32(2))
497 .enable_h3_datagrams()
498 .enable_webtransport()
499 .webtransport_max_sessions(VarInt::from_u32(3))
500 .build();
501
502 let frame = settings.generate_frame();
503 assert!(frame.session_id().is_none());
504 assert!(matches!(frame.kind(), FrameKind::Settings));
505
506 let frame = utils::assert_serde_async(frame).await;
507 Settings::with_frame(&frame).unwrap();
508 }
509
510 #[test]
511 fn headers() {
512 let headers = Headers::from_iter([("key1", "value1")]);
513
514 let frame = headers.generate_frame();
515 assert!(frame.session_id().is_none());
516 assert!(matches!(frame.kind(), FrameKind::Headers));
517
518 let frame = utils::assert_serde(frame);
519 Headers::with_frame(&frame).unwrap();
520 }
521
522 #[tokio::test]
523 async fn headers_async() {
524 let headers = Headers::from_iter([("key1", "value1")]);
525
526 let frame = headers.generate_frame();
527 assert!(frame.session_id().is_none());
528 assert!(matches!(frame.kind(), FrameKind::Headers));
529
530 let frame = utils::assert_serde_async(frame).await;
531 Headers::with_frame(&frame).unwrap();
532 }
533
534 #[test]
535 fn webtransport() {
536 let session_id = SessionId::try_from_varint(VarInt::from_u32(0)).unwrap();
537 let frame = Frame::new_webtransport(session_id);
538
539 assert!(frame.payload().is_empty());
540 assert!(matches!(frame.session_id(), Some(x) if x == session_id));
541 assert!(matches!(frame.kind(), FrameKind::WebTransport));
542
543 let frame = utils::assert_serde(frame);
544
545 assert!(frame.payload().is_empty());
546 assert!(matches!(frame.session_id(), Some(x) if x == session_id));
547 assert!(matches!(frame.kind(), FrameKind::WebTransport));
548 }
549
550 #[tokio::test]
551 async fn webtransport_async() {
552 let session_id = SessionId::try_from_varint(VarInt::from_u32(0)).unwrap();
553 let frame = Frame::new_webtransport(session_id);
554
555 assert!(frame.payload().is_empty());
556 assert!(matches!(frame.session_id(), Some(x) if x == session_id));
557 assert!(matches!(frame.kind(), FrameKind::WebTransport));
558
559 let frame = utils::assert_serde_async(frame).await;
560
561 assert!(frame.payload().is_empty());
562 assert!(matches!(frame.session_id(), Some(x) if x == session_id));
563 assert!(matches!(frame.kind(), FrameKind::WebTransport));
564 }
565
566 #[test]
567 fn read_eof() {
568 let buffer = Frame::serialize_any(FrameKind::Data.id(), b"This is a test payload");
569 assert!(Frame::read(&mut &buffer[..buffer.len() - 1])
570 .unwrap()
571 .is_none());
572 }
573
574 #[tokio::test]
575 async fn read_eof_async() {
576 let buffer = Frame::serialize_any(FrameKind::Data.id(), b"This is a test payload");
577
578 for len in 0..buffer.len() {
579 let result = Frame::read_async(&mut &buffer[..len]).await;
580
581 match len {
582 0 => assert!(matches!(
583 result,
584 Err(IoReadError::IO(bytes::IoReadError::ImmediateFin))
585 )),
586 _ => assert!(matches!(
587 result,
588 Err(IoReadError::IO(bytes::IoReadError::UnexpectedFin))
589 )),
590 }
591 }
592 }
593
594 #[tokio::test]
595 async fn read_eof_webtransport_async() {
596 let session_id = SessionId::try_from_varint(VarInt::from_u32(0)).unwrap();
597 let buffer = Frame::serialize_webtransport(session_id);
598
599 for len in 0..buffer.len() {
600 let result = Frame::read_async(&mut &buffer[..len]).await;
601
602 match len {
603 0 => assert!(matches!(
604 result,
605 Err(IoReadError::IO(bytes::IoReadError::ImmediateFin))
606 )),
607 _ => assert!(matches!(
608 result,
609 Err(IoReadError::IO(bytes::IoReadError::UnexpectedFin))
610 )),
611 }
612 }
613 }
614
615 #[test]
616 fn unknown_frame() {
617 let buffer = Frame::serialize_any(VarInt::from_u32(0x0042_4242), b"This is a test payload");
618
619 assert!(matches!(
620 Frame::read(&mut buffer.as_slice()),
621 Err(ParseError::UnknownFrame)
622 ));
623 }
624
625 #[tokio::test]
626 async fn unknown_frame_async() {
627 let buffer = Frame::serialize_any(VarInt::from_u32(0x0042_4242), b"This is a test payload");
628
629 assert!(matches!(
630 Frame::read_async(&mut buffer.as_slice()).await,
631 Err(IoReadError::Parse(ParseError::UnknownFrame))
632 ));
633 }
634
635 #[test]
636 fn invalid_session_id() {
637 let invalid_session_id = SessionId::maybe_invalid(VarInt::from_u32(1));
638 let buffer = Frame::serialize_webtransport(invalid_session_id);
639
640 assert!(matches!(
641 Frame::read(&mut buffer.as_slice()),
642 Err(ParseError::InvalidSessionId)
643 ));
644 }
645
646 #[tokio::test]
647 async fn invalid_session_id_async() {
648 let invalid_session_id = SessionId::maybe_invalid(VarInt::from_u32(1));
649 let buffer = Frame::serialize_webtransport(invalid_session_id);
650
651 assert!(matches!(
652 Frame::read_async(&mut buffer.as_slice()).await,
653 Err(IoReadError::Parse(ParseError::InvalidSessionId))
654 ));
655 }
656
657 #[test]
658 fn payload_too_big() {
659 let mut buffer = Vec::new();
660 buffer.put_varint(FrameKind::Data.id()).unwrap();
661 buffer
662 .put_varint(VarInt::from_u32(
663 Frame::MAX_PARSE_PAYLOAD_ALLOWED as u32 + 1,
664 ))
665 .unwrap();
666
667 assert!(matches!(
668 Frame::read_from_buffer(&mut BufferReader::new(&buffer)),
669 Err(ParseError::PayloadTooBig)
670 ));
671 }
672
673 #[tokio::test]
674 async fn payload_too_big_async() {
675 let mut buffer = Vec::new();
676 buffer.put_varint(FrameKind::Data.id()).unwrap();
677 buffer
678 .put_varint(VarInt::from_u32(
679 Frame::MAX_PARSE_PAYLOAD_ALLOWED as u32 + 1,
680 ))
681 .unwrap();
682
683 assert!(matches!(
684 Frame::read_async(&mut &*buffer).await,
685 Err(IoReadError::Parse(ParseError::PayloadTooBig)),
686 ));
687 }
688
689 mod utils {
690 use super::*;
691
692 pub fn assert_serde(frame: Frame) -> Frame {
693 let mut buffer = Vec::new();
694
695 frame.write(&mut buffer).unwrap();
696 assert_eq!(buffer.len(), frame.write_size());
697
698 let mut buffer = buffer.as_slice();
699 let frame = Frame::read(&mut buffer).unwrap().unwrap();
700 assert!(buffer.is_empty());
701
702 frame.into_owned()
703 }
704
705 #[cfg(feature = "async")]
706 pub async fn assert_serde_async(frame: Frame<'_>) -> Frame {
707 let mut buffer = Vec::new();
708
709 frame.write_async(&mut buffer).await.unwrap();
710 assert_eq!(buffer.len(), frame.write_size());
711
712 let mut buffer = buffer.as_slice();
713 let frame = Frame::read_async(&mut buffer).await.unwrap();
714 assert!(buffer.is_empty());
715
716 frame.into_owned()
717 }
718 }
719}