Skip to main content

sark_core/http/codec/decode/
head.rs

1use 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}