Skip to main content

sark_http/h1/codec/decode/response/
mod.rs

1use core::convert::Infallible;
2use std::borrow::Cow;
3
4use o3::alloc;
5
6use crate::{
7    field::Lower,
8    h1::{
9        codec::decode::{chunks, fields},
10        framing,
11        head::{Limit, ParseError, response},
12    },
13    payload::response::{Method, Response, headers::List, status},
14};
15
16mod head;
17
18pub use head::Head;
19
20pub type Collected<'field, B = Vec<u8>> =
21    Response<'field, B, List<'field>, status::Code, Lower<Cow<'field, str>>>;
22
23type Parsed<T, E> = Result<Option<T>, Error<E>>;
24
25#[derive(Debug)]
26pub enum Error<E> {
27    Head(ParseError<E>),
28    Framing(framing::Error),
29    Chunk(chunks::Error),
30    Allocation(alloc::Exhausted),
31    IncompleteHead,
32    IncompleteBody,
33    ForbiddenContent { actual: usize },
34}
35
36pub enum Progress<'field> {
37    More,
38    Complete(Collected<'field>),
39}
40
41const _: () =
42    assert!(core::mem::size_of::<Progress<'_>>() == core::mem::size_of::<Option<Collected<'_>>>());
43
44pub struct Decoder {
45    request: Method,
46}
47
48impl Decoder {
49    pub const fn new(request: Method) -> Self {
50        Self { request }
51    }
52
53    pub fn scan(&self, buf: &[u8], max: usize) -> Parsed<Head<()>, Infallible> {
54        let Some(plan) =
55            response::Parts::scan(buf, Limit::RESPONSE.with_bytes(max)).map_err(Error::Head)?
56        else {
57            return Ok(None);
58        };
59        let head = plan.admit(self.request).map_err(Error::Framing)?;
60        let head = Head::from(head, ());
61        Ok(Some(head))
62    }
63
64    pub fn parse<'input, F: fields::Sink<'input>>(
65        &self,
66        buf: &'input [u8],
67        max: usize,
68        mut fields: F,
69    ) -> Parsed<Head<F>, F::Error> {
70        let Some(plan) = response::Parts::parse(buf, Limit::RESPONSE.with_bytes(max), |field| {
71            fields.entry(field)
72        })
73        .map_err(Error::Head)?
74        else {
75            return Ok(None);
76        };
77        let head = plan.admit(self.request).map_err(Error::Framing)?;
78        let head = Head::from(head, fields);
79        Ok(Some(head))
80    }
81
82    pub fn collect<'field>(&self, buf: &[u8]) -> Parsed<Head<List<'field>>, fields::Error> {
83        self.parse(buf, buf.len(), List::new())
84    }
85
86    fn collect_body<'input, 'field>(
87        &self,
88        buf: &'input [u8],
89    ) -> Parsed<(Head<List<'field>>, &'input [u8]), fields::Error> {
90        let Some(head) = self.collect(buf)? else {
91            return Ok(None);
92        };
93        // SAFETY: collect admitted this head's complete wire prefix on this
94        // unchanged buffer; header_len therefore bounds its exact body suffix.
95        let body = unsafe { buf.get_unchecked(head.header_len.get() as usize..) };
96        Ok(Some((head, body)))
97    }
98
99    pub fn decode<'field>(&self, buf: &[u8]) -> Result<Progress<'field>, Error<fields::Error>> {
100        let (head, body) = match self.collect_body(buf)? {
101            Some(parts) => parts,
102            None => return Ok(Progress::More),
103        };
104
105        match head.payload {
106            framing::ResponseBody::None | framing::ResponseBody::Empty(framing::Empty::Fixed) => {
107                Ok(Progress::Complete(head.complete(Vec::new(), List::new())))
108            }
109            framing::ResponseBody::Chunked
110            | framing::ResponseBody::Empty(framing::Empty::Chunked) => {
111                let Some(result) = chunks::Output::drain(body, chunks::Decoder::DEFAULT_MAX_BODY)
112                    .map_err(Error::Chunk)?
113                else {
114                    return Ok(Progress::More);
115                };
116                if matches!(head.payload, framing::ResponseBody::Empty(_))
117                    && !result.body.is_empty()
118                {
119                    return Err(Error::ForbiddenContent {
120                        actual: result.body.len(),
121                    });
122                }
123                Ok(Progress::Complete(
124                    head.complete(result.body, result.trailers),
125                ))
126            }
127            framing::ResponseBody::Fixed(len) if body.len() < len => Ok(Progress::More),
128            framing::ResponseBody::Fixed(len) => head
129                .try_copy(&body[..len])
130                .map(Progress::Complete)
131                .map_err(Error::Allocation),
132            framing::ResponseBody::UntilEof
133            | framing::ResponseBody::Empty(framing::Empty::UntilEof) => Ok(Progress::More),
134        }
135    }
136
137    pub fn finish<'field>(&self, buf: &[u8]) -> Result<Collected<'field>, Error<fields::Error>> {
138        let (head, body) = self.collect_body(buf)?.ok_or(Error::IncompleteHead)?;
139
140        match head.payload {
141            framing::ResponseBody::Chunked
142            | framing::ResponseBody::Empty(framing::Empty::Chunked) => {
143                let result = chunks::Decoder::require(body, chunks::Decoder::DEFAULT_MAX_BODY)
144                    .map_err(Error::Chunk)?;
145                if matches!(head.payload, framing::ResponseBody::Empty(_))
146                    && !result.body.is_empty()
147                {
148                    return Err(Error::ForbiddenContent {
149                        actual: result.body.len(),
150                    });
151                }
152                Ok(head.complete(result.body, result.trailers))
153            }
154            framing::ResponseBody::Fixed(len) if body.len() < len => Err(Error::IncompleteBody),
155            framing::ResponseBody::Fixed(len) => {
156                head.try_copy(&body[..len]).map_err(Error::Allocation)
157            }
158            framing::ResponseBody::None => Ok(head.complete(Vec::new(), List::new())),
159            framing::ResponseBody::UntilEof => head.try_copy(body).map_err(Error::Allocation),
160            framing::ResponseBody::Empty(framing::Empty::Fixed)
161            | framing::ResponseBody::Empty(framing::Empty::UntilEof) => {
162                if body.is_empty() {
163                    Ok(head.complete(Vec::new(), List::new()))
164                } else {
165                    Err(Error::ForbiddenContent { actual: body.len() })
166                }
167            }
168        }
169    }
170}