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