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) = response::Parts::scan(buf, self.request, Limit::RESPONSE.with_bytes(max))
55            .map_err(Error::Head)?
56        else {
57            return Ok(None);
58        };
59        let head = plan.admit().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(
71            buf,
72            self.request,
73            Limit::RESPONSE.with_bytes(max),
74            |field| fields.entry(field),
75        )
76        .map_err(Error::Head)?
77        else {
78            return Ok(None);
79        };
80        let head = plan.admit().map_err(Error::Framing)?;
81        let head = Head::from(head, fields);
82        Ok(Some(head))
83    }
84
85    pub fn collect<'field>(&self, buf: &[u8]) -> Parsed<Head<List<'field>>, fields::Error> {
86        self.parse(buf, buf.len(), List::new())
87    }
88
89    fn collect_body<'input, 'field>(
90        &self,
91        buf: &'input [u8],
92    ) -> Parsed<(Head<List<'field>>, &'input [u8]), fields::Error> {
93        let Some(head) = self.collect(buf)? else {
94            return Ok(None);
95        };
96        // SAFETY: collect admitted this head's complete wire prefix on this
97        // unchanged buffer; header_len therefore bounds its exact body suffix.
98        let body = unsafe { buf.get_unchecked(head.header_len.get() as usize..) };
99        Ok(Some((head, body)))
100    }
101
102    pub fn decode<'field>(&self, buf: &[u8]) -> Result<Progress<'field>, Error<fields::Error>> {
103        let (head, body) = match self.collect_body(buf)? {
104            Some(parts) => parts,
105            None => return Ok(Progress::More),
106        };
107
108        match head.payload {
109            framing::ResponseBody::None | framing::ResponseBody::Empty(framing::Empty::Fixed) => {
110                Ok(Progress::Complete(head.complete(Vec::new(), List::new())))
111            }
112            framing::ResponseBody::Chunked
113            | framing::ResponseBody::Empty(framing::Empty::Chunked) => {
114                let Some(result) = chunks::Output::drain(body, chunks::Decoder::DEFAULT_MAX_BODY)
115                    .map_err(Error::Chunk)?
116                else {
117                    return Ok(Progress::More);
118                };
119                if matches!(head.payload, framing::ResponseBody::Empty(_))
120                    && !result.body.is_empty()
121                {
122                    return Err(Error::ForbiddenContent {
123                        actual: result.body.len(),
124                    });
125                }
126                Ok(Progress::Complete(
127                    head.complete(result.body, result.trailers),
128                ))
129            }
130            framing::ResponseBody::Fixed(len) if body.len() < len => Ok(Progress::More),
131            framing::ResponseBody::Fixed(len) => head
132                .try_copy(&body[..len])
133                .map(Progress::Complete)
134                .map_err(Error::Allocation),
135            framing::ResponseBody::UntilEof
136            | framing::ResponseBody::Empty(framing::Empty::UntilEof) => Ok(Progress::More),
137        }
138    }
139
140    pub fn finish<'field>(&self, buf: &[u8]) -> Result<Collected<'field>, Error<fields::Error>> {
141        let (head, body) = self.collect_body(buf)?.ok_or(Error::IncompleteHead)?;
142
143        match head.payload {
144            framing::ResponseBody::Chunked
145            | framing::ResponseBody::Empty(framing::Empty::Chunked) => {
146                let result = chunks::Decoder::require(body, chunks::Decoder::DEFAULT_MAX_BODY)
147                    .map_err(Error::Chunk)?;
148                if matches!(head.payload, framing::ResponseBody::Empty(_))
149                    && !result.body.is_empty()
150                {
151                    return Err(Error::ForbiddenContent {
152                        actual: result.body.len(),
153                    });
154                }
155                Ok(head.complete(result.body, result.trailers))
156            }
157            framing::ResponseBody::Fixed(len) if body.len() < len => Err(Error::IncompleteBody),
158            framing::ResponseBody::Fixed(len) => {
159                head.try_copy(&body[..len]).map_err(Error::Allocation)
160            }
161            framing::ResponseBody::None => Ok(head.complete(Vec::new(), List::new())),
162            framing::ResponseBody::UntilEof => head.try_copy(body).map_err(Error::Allocation),
163            framing::ResponseBody::Empty(framing::Empty::Fixed)
164            | framing::ResponseBody::Empty(framing::Empty::UntilEof) => {
165                if body.is_empty() {
166                    Ok(head.complete(Vec::new(), List::new()))
167                } else {
168                    Err(Error::ForbiddenContent { actual: body.len() })
169                }
170            }
171        }
172    }
173}