1use core::num::NonZero;
4
5use crate::{
6 field::{
7 head::{Error, Flags, headers::Bounds},
8 scan::{Bytes, Outcome},
9 text::{Ascii, IntError},
10 },
11 h1::head::request,
12};
13
14pub(super) const fn is_whitespace(byte: u8) -> bool {
15 byte == b' ' || byte == b'\t'
16}
17
18pub(super) fn try_token_line_lit<const N: usize>(raw: &[u8], expected: &[u8; N]) -> Option<Bounds> {
19 let start = if raw.first() == Some(&b' ') { 1 } else { 0 };
20 let end = start + N;
21 if raw.len() < end + 2 {
22 return None;
23 }
24 if !raw[start..end].eq_ignore_ascii_case(expected) {
25 return None;
26 }
27 if raw[end] != b'\r' || raw[end + 1] != b'\n' {
28 return None;
29 }
30 Some(Bounds::new(end, start, end))
31}
32
33pub(super) fn clen_line(scan: &mut request::Fields, raw: &[u8]) -> Result<Option<Bounds>, Error> {
34 let Some((len, bounds)) = parse_clen(raw)? else {
35 return Ok(None);
36 };
37 scan.message = scan.message.try_length(len).map_err(Error::Message)?;
38 Ok(Some(bounds))
39}
40
41pub(super) fn te_line(scan: &mut request::Fields, raw: &[u8]) -> Result<Option<Bounds>, Error> {
42 let (bounds, value) = match try_token_line_lit(raw, b"chunked") {
43 Some(bounds) => (bounds, b"chunked".as_slice()),
44 None => {
45 let Some(bounds) = trim_line(raw)? else {
46 return Ok(None);
47 };
48 (bounds, bounds.value(raw))
49 }
50 };
51 scan.message = scan.message.try_coding(value).map_err(Error::Message)?;
52 Ok(Some(bounds))
53}
54
55fn scan_length(raw: &[u8]) -> Result<Option<(usize, usize, usize)>, Error> {
56 let mut idx = if raw.first() == Some(&b' ') { 1 } else { 0 };
57 while idx < raw.len() && is_whitespace(raw[idx]) {
58 idx += 1;
59 }
60 let start = idx;
61 if start == raw.len() {
62 return Ok(None);
63 }
64 let rest = unsafe { raw.get_unchecked(start..) };
66 let (value, len) = Ascii::new(rest)
67 .parse_prefix()
68 .map_err(Error::ContentLength)?;
69 let end = start + len;
70 Ok(Some((value, start, end)))
71}
72
73pub(super) fn trim_line(raw: &[u8]) -> Result<Option<Bounds>, Error> {
74 let mut idx = 0usize;
75 while idx < raw.len() && is_whitespace(raw[idx]) {
76 idx += 1;
77 }
78 let start = idx;
79 let cr = match Bytes::new(raw).value(start) {
80 Outcome::Found(cr) => cr,
81 Outcome::Partial => return Ok(None),
82 Outcome::Invalid { .. } => return Err(Error::Value),
83 };
84 let mut value = unsafe { raw.get_unchecked(start..cr) };
86 while matches!(value.last(), Some(b' ' | b'\t')) {
87 value = &value[..value.len() - 1];
88 }
89 let end = start + value.len();
90 Ok(Some(Bounds::new(cr, start, end)))
91}
92
93fn parse_clen(raw: &[u8]) -> Result<Option<(usize, Bounds)>, Error> {
94 let Some((value, start, end)) = scan_length(raw)? else {
95 return Ok(None);
96 };
97 let rest = unsafe { raw.get_unchecked(end..) };
99 if rest.starts_with(b"\r\n") {
100 return Ok(Some((value, Bounds::new(end, start, end))));
101 }
102 let mut idx = end;
103 while idx < raw.len() && is_whitespace(raw[idx]) {
104 idx += 1;
105 }
106 if idx >= raw.len() {
107 return Ok(None);
108 }
109 if raw[idx] != b'\r' {
110 return Err(Error::ContentLength(IntError::InvalidByte));
111 }
112 if idx + 1 >= raw.len() {
113 return Ok(None);
114 }
115 if raw[idx + 1] != b'\n' {
116 return Err(Error::ContentLength(IntError::InvalidByte));
117 }
118 Ok(Some((value, Bounds::new(idx, start, end))))
119}
120
121pub(super) fn ae_line(
122 scan: &mut request::Fields,
123 flags: &mut Flags,
124 raw: &[u8],
125) -> Result<Option<Bounds>, Error> {
126 let Some(bounds) = trim_line(raw)? else {
127 return Ok(None);
128 };
129 ae_value(scan, flags, bounds.value(raw))?;
130 Ok(Some(bounds))
131}
132
133const GZIP_SEEN: u16 = 1 << 3;
134const GZIP_ALLOWED: u16 = 1 << 4;
135const GZIP_EXCLUDED: u16 = 1 << 5;
136const WILDCARD_ALLOWED: u16 = 1 << 6;
137const WILDCARD_EXCLUDED: u16 = 1 << 7;
138
139const GZIP: usize = 0;
140const BR: usize = 1;
141const IDENTITY: usize = 2;
142const WILDCARD: usize = 3;
143
144#[derive(Clone, Copy, Debug, PartialEq, Eq)]
146pub struct AcceptEncoding([NonZero<u16>; 4]);
147
148impl Default for AcceptEncoding {
149 fn default() -> Self {
150 Self([NonZero::<u16>::MAX; 4])
151 }
152}
153
154impl AcceptEncoding {
155 pub fn try_parse(raw: &[u8]) -> Result<Self, Error> {
156 let (_, encoding) = scan::<true>(raw)?;
157 Ok(encoding)
158 }
159
160 pub fn try_apply(
161 fields: &mut request::Fields,
162 flags: &mut Flags,
163 raw: &[u8],
164 ) -> Result<Self, Error> {
165 let (next, encoding) = scan::<true>(raw)?;
166 apply(fields, flags, next);
167 Ok(encoding)
168 }
169
170 pub fn scan_line(
171 fields: &mut request::Fields,
172 flags: &mut Flags,
173 raw: &[u8],
174 ) -> Result<Option<(Bounds, Self)>, Error> {
175 let Some(bounds) = trim_line(raw)? else {
176 return Ok(None);
177 };
178 let encoding = Self::try_apply(fields, flags, bounds.value(raw))?;
179 Ok(Some((bounds, encoding)))
180 }
181
182 pub fn append(&mut self, other: Self) {
183 for (target, value) in self.0.iter_mut().zip(other.0) {
184 *target = (*target).min(value);
185 }
186 }
187
188 pub fn gzip(self) -> u16 {
189 Self::resolve(self.0[GZIP], self.0[WILDCARD])
190 }
191
192 pub fn br(self) -> u16 {
193 Self::resolve(self.0[BR], self.0[WILDCARD])
194 }
195
196 fn resolve(value: NonZero<u16>, wildcard: NonZero<u16>) -> u16 {
197 match value {
198 value if value != NonZero::<u16>::MAX => value.get() - 1,
199 _ if wildcard != NonZero::<u16>::MAX => wildcard.get() - 1,
200 _ => 0,
201 }
202 }
203
204 pub fn identity(self) -> u16 {
205 match self.0[IDENTITY] {
206 q if q != NonZero::<u16>::MAX => q.get() - 1,
207 _ if self.0[WILDCARD] == NonZero::<u16>::MIN => 0,
208 _ => 1000,
209 }
210 }
211}
212
213pub(super) fn ae_value(
216 fields: &mut request::Fields,
217 flags: &mut Flags,
218 raw: &[u8],
219) -> Result<(), Error> {
220 let (next, _) = scan::<false>(raw)?;
221 apply(fields, flags, next);
222 Ok(())
223}
224
225fn scan<const FULL: bool>(raw: &[u8]) -> Result<(Flags, AcceptEncoding), Error> {
226 let mut next = Flags::default();
227 let mut encoding = AcceptEncoding::default();
228 for member in raw.split(|&byte| byte == b',') {
229 let member = crate::field::Error::trim_ows(member);
230 if member.is_empty() {
231 continue;
232 }
233 let end = member
234 .iter()
235 .position(|&byte| !crate::field::Error::is_name_byte(byte))
236 .unwrap_or(member.len());
237 if end == 0 {
238 return Err(Error::Value);
239 }
240 let token = &member[..end];
241 let quality = quality::<FULL>(&member[end..])?;
242 let slot = if token.eq_ignore_ascii_case(b"gzip") {
243 next.set(GZIP_SEEN);
244 next.set(if quality != 0 {
245 GZIP_ALLOWED
246 } else {
247 GZIP_EXCLUDED
248 });
249 GZIP
250 } else if token == b"*" {
251 next.set(if quality != 0 {
252 WILDCARD_ALLOWED
253 } else {
254 WILDCARD_EXCLUDED
255 });
256 WILDCARD
257 } else if FULL && token.eq_ignore_ascii_case(b"br") {
258 BR
259 } else if FULL && token.eq_ignore_ascii_case(b"identity") {
260 IDENTITY
261 } else {
262 continue;
263 };
264 if FULL && let Some(value) = NonZero::new(quality + 1) {
265 match slot {
266 GZIP => encoding.0[GZIP] = encoding.0[GZIP].min(value),
267 BR => encoding.0[BR] = encoding.0[BR].min(value),
268 IDENTITY => encoding.0[IDENTITY] = encoding.0[IDENTITY].min(value),
269 _ => encoding.0[WILDCARD] = encoding.0[WILDCARD].min(value),
270 }
271 }
272 }
273 Ok((next, encoding))
274}
275
276fn apply(fields: &mut request::Fields, flags: &mut Flags, next: Flags) {
277 flags.0 |= next.0;
278 let allowed = if flags.is_set(GZIP_SEEN) {
279 flags.is_set(GZIP_ALLOWED) && !flags.is_set(GZIP_EXCLUDED)
280 } else {
281 flags.is_set(WILDCARD_ALLOWED) && !flags.is_set(WILDCARD_EXCLUDED)
282 };
283 fields.message.set_coding(if allowed {
284 request::Coding::Gzip
285 } else {
286 request::Coding::Plain
287 });
288}
289
290fn quality<const FULL: bool>(raw: &[u8]) -> Result<u16, Error> {
293 let raw = crate::field::Error::trim_ows(raw);
294 if raw.is_empty() {
295 return Ok(if FULL { 1000 } else { 1 });
296 }
297 let Some(raw) = raw.strip_prefix(b";") else {
298 return Err(Error::Value);
299 };
300 let raw = crate::field::Error::trim_ows(raw);
301 let Some((&name, raw)) = raw.split_first() else {
302 return Err(Error::Value);
303 };
304 if !name.eq_ignore_ascii_case(&b'q') {
305 return Err(Error::Value);
306 }
307 let Some(raw) = raw.strip_prefix(b"=") else {
308 return Err(Error::Value);
309 };
310 let Some((&whole, rest)) = raw.split_first() else {
311 return Err(Error::Value);
312 };
313 if !matches!(whole, b'0' | b'1') {
314 return Err(Error::Value);
315 }
316 let fraction = if rest.is_empty() {
317 rest
318 } else {
319 rest.strip_prefix(b".").ok_or(Error::Value)?
320 };
321 if fraction.len() > 3 {
322 return Err(Error::Value);
323 }
324 let mut quality = u16::from(whole == b'1');
325 if FULL {
326 quality *= 1000;
327 }
328 let mut scale = 100;
329 for &byte in fraction {
330 if !byte.is_ascii_digit() || (whole == b'1' && byte != b'0') {
331 return Err(Error::Value);
332 }
333 if FULL {
334 quality += u16::from(byte - b'0') * scale;
335 scale /= 10;
336 } else {
337 quality |= u16::from(byte != b'0');
338 }
339 }
340 Ok(quality)
341}