1use crate::{
24 Field, FieldError, Fields,
25 syntax::{is_token, trim_ows},
26};
27use std::collections::HashMap;
28
29#[derive(Clone, Copy, Debug, Eq, PartialEq)]
31pub enum Combination {
32 Singleton,
33 Comma,
34 LowercaseComma,
35 Cookie,
36 Separate,
37 Last,
38}
39
40#[derive(Clone, Copy, Debug, Eq, PartialEq)]
41pub struct FieldPolicy {
42 pub combination: Combination,
43 pub trailer: bool,
44}
45
46impl FieldPolicy {
47 pub const fn new(combination: Combination, trailer: bool) -> Self {
48 Self {
49 combination,
50 trailer,
51 }
52 }
53}
54
55#[derive(Clone, Debug, Default)]
58pub struct Policy {
59 #[allow(clippy::box_collection)]
62 overrides: Option<Box<HashMap<String, FieldPolicy>>>,
63}
64
65impl Policy {
66 pub fn set(&mut self, name: &str, policy: FieldPolicy) -> Result<(), FieldError> {
67 if !is_token(name.as_bytes()) {
68 return Err(FieldError::InvalidName);
69 }
70 self.overrides
71 .get_or_insert_with(Default::default)
72 .insert(name.to_ascii_lowercase(), policy);
73 Ok(())
74 }
75
76 pub fn get(&self, name: &str) -> FieldPolicy {
77 let name = name.to_ascii_lowercase();
78 if let Some(policy) = self
79 .overrides
80 .as_ref()
81 .and_then(|overrides| overrides.get(&name))
82 {
83 return *policy;
84 }
85 use Combination::*;
86 match name.as_str() {
87 "content-disposition"
88 | "content-length"
89 | "content-type"
90 | "expect"
91 | "from"
92 | "host"
93 | "location"
94 | "max-forwards"
95 | "referer"
96 | "retry-after"
97 | "server"
98 | "user-agent"
99 | "if-range"
100 | "content-range" => FieldPolicy::new(Singleton, false),
101 "connection" => FieldPolicy::new(LowercaseComma, false),
102 "cookie" => FieldPolicy::new(Cookie, false),
103 "set-cookie" | "www-authenticate" | "proxy-authenticate" => {
104 FieldPolicy::new(Separate, false)
105 }
106 "authorization" | "proxy-authorization" => FieldPolicy::new(Singleton, false),
107 "date"
108 | "expires"
109 | "last-modified"
110 | "if-modified-since"
111 | "if-unmodified-since"
112 | "etag" => FieldPolicy::new(Last, true),
113 "range" | "transfer-encoding" | "trailer" | "upgrade" | "cache-control" | "te"
114 | "vary" | "priority" | "via" | "x-forwarded-for" | "if-match" | "if-none-match"
115 | "accept" | "accept-ranges" | "accept-charset" | "accept-encoding"
116 | "accept-language" | "content-encoding" => FieldPolicy::new(Comma, false),
117 _ => FieldPolicy::new(Comma, true),
118 }
119 }
120}
121
122#[derive(Clone, Debug, Eq, PartialEq)]
124pub struct FieldValue {
125 combination: Combination,
126 parts: Vec<Vec<u8>>,
127}
128
129impl FieldValue {
130 pub fn combination(&self) -> Combination {
131 self.combination
132 }
133 pub fn parts(&self) -> &[Vec<u8>] {
134 &self.parts
135 }
136
137 pub fn single(&self) -> Option<&[u8]> {
139 if self.parts.len() == 1 {
140 Some(&self.parts[0])
141 } else {
142 None
143 }
144 }
145
146 pub fn combined(&self) -> Option<Vec<u8>> {
149 let separator: &[u8] = match self.combination {
150 Combination::Separate => return None,
151 Combination::Cookie => b"; ",
152 _ => b",",
153 };
154 Some(self.parts.join(separator))
155 }
156
157 fn parse(name: &str, value: &[u8], combination: Combination) -> Result<Self, FieldError> {
158 let mut parts = match combination {
159 Combination::Comma | Combination::LowercaseComma => split_list(name, value)?,
160 _ => vec![value.to_vec()],
161 };
162 if combination == Combination::LowercaseComma {
163 for part in &mut parts {
164 part.make_ascii_lowercase();
165 }
166 }
167 Ok(Self { combination, parts })
168 }
169}
170
171fn split_list(name: &str, value: &[u8]) -> Result<Vec<Vec<u8>>, FieldError> {
172 let mut parts = Vec::new();
173 let (mut start, mut quoted, mut escaped) = (0, false, false);
174 for (position, byte) in value.iter().enumerate() {
175 if escaped {
176 escaped = false;
177 } else if quoted && *byte == b'\\' {
178 escaped = true;
179 } else if *byte == b'"' {
180 quoted = !quoted;
181 } else if !quoted && *byte == b',' {
182 let part = trim_ows(&value[start..position]);
183 if !part.is_empty() {
184 parts.push(part.to_vec());
185 }
186 start = position + 1;
187 }
188 }
189 if quoted {
190 return Err(FieldError::InvalidList(name.to_owned()));
191 }
192 let part = trim_ows(&value[start..]);
193 if !part.is_empty() {
194 parts.push(part.to_vec());
195 }
196 Ok(parts)
197}
198
199pub type Index = HashMap<String, FieldValue>;
200
201#[derive(Clone, Debug, Default)]
205pub struct HttpHeaders {
206 fields: Fields,
207 tail: Option<usize>,
208 indexed: Option<Box<Index>>,
209 policy: Policy,
210}
211
212impl HttpHeaders {
213 pub fn new(fields: Fields) -> Self {
214 Self {
215 fields,
216 ..Self::default()
217 }
218 }
219 pub fn try_from_pairs<N: AsRef<str>, V: AsRef<[u8]>>(
220 pairs: impl IntoIterator<Item = (N, V)>,
221 ) -> Result<Self, FieldError> {
222 Ok(Self::new(Fields::try_from_pairs(pairs)?))
223 }
224 pub fn with_policy(fields: Fields, policy: Policy) -> Self {
225 Self {
226 fields,
227 policy,
228 ..Self::default()
229 }
230 }
231 pub fn into_parts(mut self) -> (Fields, Option<Fields>) {
233 let trailers = self.tail.map(|tail| self.fields.split_off(tail));
234 (self.fields, trailers)
235 }
236 pub fn pairs(&self) -> &[Field] {
237 &self.fields.pairs
238 }
239 pub fn len(&self) -> usize {
240 self.fields.pairs.len()
241 }
242 pub fn is_empty(&self) -> bool {
243 self.fields.pairs.is_empty()
244 }
245 pub fn tail(&self) -> Option<usize> {
246 self.tail
247 }
248 pub fn has_trailers(&self) -> bool {
249 self.tail.is_some()
250 }
251 pub fn headers(&self) -> &[Field] {
252 &self.fields.pairs[..self.tail.unwrap_or(self.fields.pairs.len())]
253 }
254 pub fn trailers(&self) -> &[Field] {
255 &self.fields.pairs[self.tail.unwrap_or(self.fields.pairs.len())..]
256 }
257 pub fn policy(&self) -> &Policy {
258 &self.policy
259 }
260
261 pub fn set_policy(&mut self, policy: Policy) {
263 self.policy = policy;
264 self.indexed = None;
265 }
266
267 pub fn append_trailers(&mut self, fields: Fields) -> Result<(), FieldError> {
270 for field in fields.pairs() {
271 if !self.policy.get(field.name()).trailer {
272 return Err(FieldError::InvalidTrailer(
273 field.name().to_ascii_lowercase(),
274 ));
275 }
276 }
277 if let Some(index) = &self.indexed {
279 let mut next = (**index).clone();
280 for field in fields.pairs() {
281 merge_into(&mut next, field, self.policy.get(field.name()))?;
282 }
283 self.indexed = Some(Box::new(next));
284 }
285 self.tail.get_or_insert(self.fields.len());
286 self.fields.extend(fields);
287 Ok(())
288 }
289
290 pub fn values<'a>(&'a self, name: &'a str) -> impl Iterator<Item = &'a [u8]> + 'a {
291 self.fields
292 .pairs
293 .iter()
294 .filter(move |field| field.name().eq_ignore_ascii_case(name))
295 .map(Field::value)
296 }
297
298 pub fn contains(&self, name: &str) -> bool {
299 self.fields
300 .pairs
301 .iter()
302 .any(|field| field.name().eq_ignore_ascii_case(name))
303 }
304
305 #[inline]
306 pub fn add(
307 &mut self,
308 name: impl AsRef<str>,
309 value: impl AsRef<[u8]>,
310 ) -> Result<(), FieldError> {
311 self.append(Field::new(name, value)?)
312 }
313
314 #[inline]
315 pub fn append(&mut self, field: Field) -> Result<(), FieldError> {
316 self.check_trailer(&field)?;
317 if let Some(index) = &mut self.indexed {
318 merge_into(index, &field, self.policy.get(field.name()))?;
319 }
320 self.fields.pairs.push(field);
321 Ok(())
322 }
323
324 pub fn set(
327 &mut self,
328 name: impl AsRef<str>,
329 value: impl AsRef<[u8]>,
330 ) -> Result<(), FieldError> {
331 let field = Field::new(name, value)?;
332 self.check_trailer(&field)?;
333 let parsed = if self.indexed.is_some() {
334 Some(FieldValue::parse(
335 field.name(),
336 field.value(),
337 self.policy.get(field.name()).combination,
338 )?)
339 } else {
340 None
341 };
342 self.remove(field.name());
343 if let Some(parsed) = parsed {
344 self.indexed
345 .as_mut()
346 .unwrap()
347 .insert(field.name().to_ascii_lowercase(), parsed);
348 }
349 self.fields.pairs.push(field);
350 Ok(())
351 }
352
353 pub fn remove(&mut self, name: &str) -> usize {
355 let mut position = 0;
356 let mut removed_headers = 0;
357 let original_length = self.fields.pairs.len();
358 self.fields.pairs.retain(|field| {
359 let keep = !field.name().eq_ignore_ascii_case(name);
360 if !keep && position < self.tail.unwrap_or(original_length) {
361 removed_headers += 1;
362 }
363 position += 1;
364 keep
365 });
366 if let Some(tail) = &mut self.tail {
367 *tail -= removed_headers;
368 }
369 if let Some(index) = &mut self.indexed {
370 index.remove(&name.to_ascii_lowercase());
371 }
372 original_length - self.fields.pairs.len()
373 }
374
375 pub fn clear(&mut self) {
376 self.fields.pairs.clear();
377 self.tail = None;
378 self.indexed = None;
379 }
380
381 pub fn get(&mut self, name: &str) -> Result<Option<&FieldValue>, FieldError> {
382 Ok(self.index()?.get(&name.to_ascii_lowercase()))
383 }
384
385 pub fn index(&mut self) -> Result<&Index, FieldError> {
388 if self.indexed.is_none() {
389 let mut index = Index::new();
390 for (position, field) in self.fields.pairs.iter().enumerate() {
391 if self.tail.is_some_and(|tail| position >= tail) {
392 self.check_trailer(field)?;
393 }
394 merge_into(&mut index, field, self.policy.get(field.name()))?;
395 }
396 self.indexed = Some(Box::new(index));
397 }
398 Ok(self.indexed.as_ref().unwrap())
399 }
400
401 fn check_trailer(&self, field: &Field) -> Result<(), FieldError> {
402 if self.tail.is_some() && !self.policy.get(field.name()).trailer {
403 return Err(FieldError::InvalidTrailer(
404 field.name().to_ascii_lowercase(),
405 ));
406 }
407 Ok(())
408 }
409}
410
411fn merge_into(index: &mut Index, field: &Field, policy: FieldPolicy) -> Result<(), FieldError> {
412 let name = field.name().to_ascii_lowercase();
413 if policy.combination == Combination::Singleton && index.contains_key(&name) {
414 return Err(FieldError::Duplicate(name));
415 }
416 let parsed = FieldValue::parse(&name, field.value(), policy.combination)?;
417 match index.get_mut(&name) {
418 Some(current) if policy.combination != Combination::Last => {
419 current.parts.extend(parsed.parts)
420 }
421 _ => {
422 index.insert(name, parsed);
423 }
424 }
425 Ok(())
426}