sark_http/field/metadata/block/
framing.rs1use core::num::NonZero;
2
3use crate::payload::response;
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub struct Framing {
8 length: u64,
9 status: Option<NonZero<u16>>,
10 method: Option<response::Method>,
11 present: bool,
12}
13
14impl Framing {
15 pub(super) const fn new(
16 length: u64,
17 status: Option<NonZero<u16>>,
18 method: Option<response::Method>,
19 present: bool,
20 ) -> Self {
21 Self {
22 length,
23 status,
24 method,
25 present,
26 }
27 }
28
29 pub const fn content_length(self) -> Option<u64> {
30 if self.present {
31 Some(self.length)
32 } else {
33 None
34 }
35 }
36
37 pub const fn status(self) -> Option<NonZero<u16>> {
38 self.status
39 }
40
41 pub const fn method(self) -> Option<response::Method> {
42 self.method
43 }
44
45 pub fn content(self, method: response::Method) -> Result<(Content, u64), ContentError> {
47 let length = self.content_length();
48 if let Some(status) = self.status {
49 match response::Content::new(method, status.get()) {
50 response::Content::Tunnel => return Ok((Content::Tunnel, 0)),
51 response::Content::Forbidden => {
52 if let Some(length) = length {
53 return Err(ContentError::ForbiddenLength { length });
54 }
55 return Ok((Content::Empty, 0));
56 }
57 response::Content::Suppressed => return Ok((Content::Empty, 0)),
58 response::Content::Empty => {
59 if let Some(length) = length.filter(|&length| length != 0) {
60 return Err(ContentError::NonemptyLength { length });
61 }
62 return Ok((Content::Exact, 0));
63 }
64 response::Content::Allowed => {}
65 }
66 } else if self.method == Some(response::Method::Connect) {
67 return Ok((Content::Tunnel, 0));
68 }
69 Ok(match length {
70 Some(length) => (Content::Exact, length),
71 None => (Content::Unbounded, 0),
72 })
73 }
74}
75
76#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
78pub enum Content {
79 #[default]
80 Unbounded,
81 Exact,
82 Empty,
83 Tunnel,
84}
85
86#[derive(Clone, Copy, Debug, PartialEq, Eq)]
87pub enum ContentError {
88 ForbiddenLength { length: u64 },
89 NonemptyLength { length: u64 },
90 Exceeded { remaining: u64, actual: usize },
91 Incomplete { remaining: u64 },
92 Trailers,
93}
94
95impl Content {
96 pub fn receive(
97 self,
98 remaining: u64,
99 bytes: usize,
100 finished: bool,
101 ) -> Result<u64, ContentError> {
102 let next = match self {
103 Self::Unbounded | Self::Tunnel => return Ok(remaining),
104 Self::Empty if bytes == 0 => return Ok(0),
105 Self::Empty => {
106 return Err(ContentError::Exceeded {
107 remaining: 0,
108 actual: bytes,
109 });
110 }
111 Self::Exact => remaining
112 .checked_sub(bytes as u64)
113 .ok_or(ContentError::Exceeded {
114 remaining,
115 actual: bytes,
116 })?,
117 };
118 if finished && next != 0 {
119 return Err(ContentError::Incomplete { remaining: next });
120 }
121 Ok(next)
122 }
123
124 pub fn trailers(self, remaining: u64) -> Result<(), ContentError> {
125 match self {
126 Self::Unbounded => Ok(()),
127 Self::Exact if remaining == 0 => Ok(()),
128 Self::Exact => Err(ContentError::Incomplete { remaining }),
129 Self::Empty | Self::Tunnel => Err(ContentError::Trailers),
130 }
131 }
132}
133
134const _: () = {
135 assert!(core::mem::size_of::<Framing>() == 16);
136 assert!(core::mem::size_of::<Option<Framing>>() == 16);
137};