sark_core/http/codec/decode/
head.rs1use http::{HeaderName, HeaderValue, StatusCode};
2use o3::buffer::Owned;
3
4use crate::error::{Error, Result};
5use crate::http::Response;
6use crate::http::codec::Header;
7
8const MAX_HEADERS: usize = 100;
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11pub enum DecodeMode {
12 Response,
13 Head,
14}
15
16impl DecodeMode {
17 const fn is_head(self) -> bool {
18 matches!(self, Self::Head)
19 }
20}
21
22pub(super) struct ParsedHead {
23 pub(super) status: StatusCode,
24 pub(super) headers: Vec<(HeaderName, HeaderValue)>,
25 pub(super) header_len: usize,
26 pub(super) content_length: Option<usize>,
27 pub(super) is_chunked: bool,
28}
29
30pub enum BodyKind {
31 NoBody,
32 ContentLength(usize),
33 Chunked,
34 UntilEof,
35}
36
37pub struct DecodedHead {
38 pub status: StatusCode,
39 pub headers: Vec<(HeaderName, HeaderValue)>,
40 pub header_len: usize,
41 pub body_kind: BodyKind,
42}
43
44impl crate::http::codec::Parse {
45 pub fn status_has_no_body(status: StatusCode) -> bool {
46 let code = status.as_u16();
47 code < 200 || code == 204 || code == 304
48 }
49
50 pub(super) fn parse(buf: &[u8]) -> Result<Option<ParsedHead>> {
51 let mut raw = [httparse::EMPTY_HEADER; MAX_HEADERS];
52 let mut parsed = httparse::Response::new(&mut raw);
53
54 match parsed.parse(buf)? {
55 httparse::Status::Partial => Ok(None),
56 httparse::Status::Complete(header_len) => {
57 let status_code = parsed
58 .code
59 .ok_or_else(|| Error::BadRequest("Missing status code".into()))?;
60 let status = StatusCode::from_u16(status_code)
61 .map_err(|_| Error::BadRequest("Invalid status code".into()))?;
62
63 let mut headers = Vec::with_capacity(parsed.headers.len());
64 let mut content_length = None;
65 let mut is_chunked = false;
66 let mut has_transfer_encoding = false;
67
68 for h in parsed.headers.iter() {
69 let name = HeaderName::from_bytes(h.name.as_bytes()).map_err(|_| {
70 Error::BadRequest(format!("Invalid header name: {}", h.name).into())
71 })?;
72 let value = HeaderValue::from_bytes(h.value).map_err(|_| {
73 Error::BadRequest(format!("Invalid header value for: {}", h.name).into())
74 })?;
75
76 if name == "content-length" {
77 let len = Header::content_length(value.as_bytes())?;
78 if let Some(existing) = content_length
79 && existing != len
80 {
81 return Err(Error::BadRequest("Conflicting Content-Length".into()));
82 }
83 content_length = Some(len);
84 }
85 if name == "transfer-encoding" {
86 let te = Self::parse_transfer_encoding_value(value.as_bytes())?;
87 has_transfer_encoding = has_transfer_encoding || te.has_transfer_encoding;
88 if te.is_chunked {
89 is_chunked = true;
90 }
91 }
92 headers.push((name, value));
93 }
94
95 if has_transfer_encoding {
96 content_length = None;
97 }
98
99 Ok(Some(ParsedHead {
100 status,
101 headers,
102 header_len,
103 content_length,
104 is_chunked,
105 }))
106 }
107 }
108 }
109
110 pub(super) fn build_response(
111 head: ParsedHead,
112 body: &[u8],
113 trailers: &[(HeaderName, HeaderValue)],
114 ) -> Response {
115 let mut resp = Response::new(head.status);
116 for (name, value) in head.headers {
117 resp.headers_mut().insert(name, value);
118 }
119 for (name, value) in trailers {
120 resp.headers_mut().insert(name.clone(), value.clone());
121 }
122 if !body.is_empty() {
123 resp.set_body(Owned::from(body));
124 }
125 resp
126 }
127
128 pub fn head(buf: &[u8], mode: DecodeMode) -> Result<Option<DecodedHead>> {
129 let head = match Self::parse(buf)? {
130 Some(h) => h,
131 None => return Ok(None),
132 };
133
134 let body_kind = if mode.is_head() || Self::status_has_no_body(head.status) {
135 BodyKind::NoBody
136 } else if head.is_chunked {
137 BodyKind::Chunked
138 } else if let Some(len) = head.content_length {
139 BodyKind::ContentLength(len)
140 } else {
141 BodyKind::UntilEof
142 };
143
144 Ok(Some(DecodedHead {
145 status: head.status,
146 headers: head.headers,
147 header_len: head.header_len,
148 body_kind,
149 }))
150 }
151
152 pub fn response(buf: &[u8], mode: DecodeMode) -> Result<Option<Response>> {
153 let head = match Self::parse(buf)? {
154 Some(h) => h,
155 None => return Ok(None),
156 };
157
158 if mode.is_head() || Self::status_has_no_body(head.status) {
159 return Ok(Some(Self::build_response(head, &[], &[])));
160 }
161
162 let body_data = &buf[head.header_len..];
163
164 if head.is_chunked {
165 match Self::try_decode_chunked(body_data)? {
166 Some(result) => Ok(Some(Self::build_response(
167 head,
168 &result.body,
169 &result.trailers,
170 ))),
171 None => Ok(None),
172 }
173 } else {
174 match head.content_length {
175 Some(len) if body_data.len() < len => Ok(None),
176 Some(len) => Ok(Some(Self::build_response(head, &body_data[..len], &[]))),
177 None => Ok(None),
178 }
179 }
180 }
181
182 pub fn response_after_eof(buf: &[u8]) -> Result<Response> {
183 let head = Self::parse(buf)?
184 .ok_or_else(|| Error::BadRequest("Incomplete HTTP response".into()))?;
185 let body_data = &buf[head.header_len..];
186
187 if head.is_chunked {
188 let result = Self::try_decode_chunked(body_data)?
189 .ok_or_else(|| Error::BadRequest("Incomplete chunked response".into()))?;
190 Ok(Self::build_response(head, &result.body, &result.trailers))
191 } else {
192 Ok(Self::build_response(head, body_data, &[]))
193 }
194 }
195}