sark_http/h1/codec/decode/response/
mod.rs1use 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 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}