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) = 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 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}