Skip to main content

base_http/
fields.rs

1// Released under the MIT License.
2// Copyright, 2026, by Samuel Williams.
3
4//! Ordered pairs of token names and opaque byte values, without interpretation
5//! or section state. Protocol drivers decide which value bytes each protocol can
6//! carry: HTTP/2 and HTTP/3 extensions can negotiate binary values, so the
7//! semantic model does not restrict them.
8use crate::syntax::is_token;
9use arrayvec::{ArrayString, ArrayVec};
10use smallvec::SmallVec;
11use std::{error::Error, fmt, ops::Deref};
12
13#[derive(Clone, Debug, Eq, PartialEq)]
14pub enum FieldError {
15    InvalidName,
16    Duplicate(String),
17    InvalidTrailer(String),
18    InvalidList(String),
19}
20
21impl fmt::Display for FieldError {
22    fn fmt(&self, output: &mut fmt::Formatter<'_>) -> fmt::Result {
23        match self {
24            Self::InvalidName => output.write_str("invalid HTTP field name"),
25            Self::Duplicate(name) => write!(output, "duplicate singleton field: {name}"),
26            Self::InvalidTrailer(name) => {
27                write!(output, "field is not permitted in trailers: {name}")
28            }
29            Self::InvalidList(name) => write!(output, "unterminated quoted value in field: {name}"),
30        }
31    }
32}
33
34impl Error for FieldError {}
35
36/// One pair with a token name, retaining the original name spelling and value
37/// bytes.
38#[derive(Clone, Debug, Eq, PartialEq)]
39pub struct Field {
40    name: NameStorage,
41    value: ValueStorage,
42}
43
44// Common names and short opaque values require no separate allocation. The
45// canonical representation depends only on length, preserving value equality.
46#[derive(Clone, Debug, Eq, PartialEq)]
47enum NameStorage {
48    Inline(ArrayString<24>),
49    Heap(String),
50}
51impl NameStorage {
52    #[inline]
53    fn new(value: &str) -> Self {
54        match ArrayString::from(value) {
55            Ok(value) => Self::Inline(value),
56            Err(_) => Self::Heap(value.to_owned()),
57        }
58    }
59}
60impl Deref for NameStorage {
61    type Target = str;
62    fn deref(&self) -> &str {
63        match self {
64            Self::Inline(value) => value.as_str(),
65            Self::Heap(value) => value,
66        }
67    }
68}
69
70// Each variant also records whether the value contains CR, LF or NUL, which
71// fits in the inline variant's padding.
72#[derive(Clone, Debug, Eq, PartialEq)]
73enum ValueStorage {
74    Inline(ArrayVec<u8, 24>, bool),
75    Heap(Vec<u8>, bool),
76}
77impl ValueStorage {
78    #[inline]
79    fn new(value: &[u8]) -> Self {
80        // Folding without stopping early lets the compiler vectorize the scan.
81        let dangerous = value.iter().fold(false, |found, byte| {
82            found | matches!(byte, b'\r' | b'\n' | 0)
83        });
84        let mut inline = ArrayVec::new();
85        if inline.try_extend_from_slice(value).is_ok() {
86            Self::Inline(inline, dangerous)
87        } else {
88            Self::Heap(value.to_vec(), dangerous)
89        }
90    }
91}
92impl Deref for ValueStorage {
93    type Target = [u8];
94    fn deref(&self) -> &[u8] {
95        match self {
96            Self::Inline(value, _) => value.as_slice(),
97            Self::Heap(value, _) => value,
98        }
99    }
100}
101
102impl Field {
103    #[inline]
104    pub fn new(name: impl AsRef<str>, value: impl AsRef<[u8]>) -> Result<Self, FieldError> {
105        let name = name.as_ref();
106        if !is_token(name.as_bytes()) {
107            return Err(FieldError::InvalidName);
108        }
109        Ok(Self {
110            name: NameStorage::new(name),
111            value: ValueStorage::new(value.as_ref()),
112        })
113    }
114
115    pub fn name(&self) -> &str {
116        &self.name
117    }
118    pub fn value(&self) -> &[u8] {
119        &self.value
120    }
121    /// Whether the value contains CR, LF or NUL, which RFC 9110 (section 5.5)
122    /// calls invalid and dangerous in field values. HTTP/1 cannot carry them,
123    /// and HTTP/2 and HTTP/3 forbid them without a negotiated extension. This is
124    /// recorded when the field is constructed.
125    pub fn has_dangerous_bytes(&self) -> bool {
126        match self.value {
127            ValueStorage::Inline(_, dangerous) | ValueStorage::Heap(_, dangerous) => dangerous,
128        }
129    }
130}
131
132/// Raw ordered pairs. Duplicate names, spelling, spacing, and opaque value
133/// bytes are preserved. Interpretation belongs to an explicit header policy;
134/// trailers are a separate `BodyFrame::Headers` event.
135#[derive(Clone, Debug, Default, Eq, PartialEq)]
136pub struct Fields {
137    pub(crate) pairs: SmallVec<[Field; 1]>,
138}
139
140impl Fields {
141    #[inline]
142    pub fn new() -> Self {
143        Self::default()
144    }
145    /// Fields with storage for `capacity` pairs, for a driver that knows how
146    /// many it will add.
147    #[inline]
148    pub fn with_capacity(capacity: usize) -> Self {
149        Self {
150            pairs: SmallVec::with_capacity(capacity),
151        }
152    }
153    /// Reserve storage for at least `additional` more pairs.
154    #[inline]
155    pub fn reserve(&mut self, additional: usize) {
156        self.pairs.reserve(additional);
157    }
158    pub fn try_from_pairs<N: AsRef<str>, V: AsRef<[u8]>>(
159        pairs: impl IntoIterator<Item = (N, V)>,
160    ) -> Result<Self, FieldError> {
161        let mut fields = Self::new();
162        for (name, value) in pairs {
163            fields.add(name, value)?;
164        }
165        Ok(fields)
166    }
167    pub fn pairs(&self) -> &[Field] {
168        &self.pairs
169    }
170    pub fn len(&self) -> usize {
171        self.pairs.len()
172    }
173    pub fn is_empty(&self) -> bool {
174        self.pairs.is_empty()
175    }
176    pub fn values<'a>(&'a self, name: &'a str) -> impl Iterator<Item = &'a [u8]> + 'a {
177        self.pairs
178            .iter()
179            .filter(move |field| field.name().eq_ignore_ascii_case(name))
180            .map(Field::value)
181    }
182    pub fn contains(&self, name: &str) -> bool {
183        self.pairs
184            .iter()
185            .any(|field| field.name().eq_ignore_ascii_case(name))
186    }
187    #[inline]
188    pub fn add(
189        &mut self,
190        name: impl AsRef<str>,
191        value: impl AsRef<[u8]>,
192    ) -> Result<(), FieldError> {
193        self.append(Field::new(name, value)?)
194    }
195    #[inline]
196    pub fn append(&mut self, field: Field) -> Result<(), FieldError> {
197        self.pairs.push(field);
198        Ok(())
199    }
200    pub fn set(
201        &mut self,
202        name: impl AsRef<str>,
203        value: impl AsRef<[u8]>,
204    ) -> Result<(), FieldError> {
205        let field = Field::new(name, value)?;
206        self.remove(field.name());
207        self.pairs.push(field);
208        Ok(())
209    }
210    pub fn remove(&mut self, name: &str) -> usize {
211        let original = self.len();
212        self.retain(|field| !field.name().eq_ignore_ascii_case(name));
213        original - self.len()
214    }
215    pub fn retain(&mut self, mut keep: impl FnMut(&Field) -> bool) {
216        self.pairs.retain(|field| keep(field));
217    }
218    pub fn clear(&mut self) {
219        self.pairs.clear();
220    }
221    pub fn split_off(&mut self, at: usize) -> Self {
222        Self {
223            pairs: self.pairs.drain(at..).collect(),
224        }
225    }
226}
227impl IntoIterator for Fields {
228    type Item = Field;
229    type IntoIter = smallvec::IntoIter<[Field; 1]>;
230    fn into_iter(self) -> Self::IntoIter {
231        self.pairs.into_iter()
232    }
233}
234impl Extend<Field> for Fields {
235    fn extend<T: IntoIterator<Item = Field>>(&mut self, iter: T) {
236        self.pairs.extend(iter);
237    }
238}
239impl FromIterator<Field> for Fields {
240    fn from_iter<T: IntoIterator<Item = Field>>(iter: T) -> Self {
241        Self {
242            pairs: iter.into_iter().collect(),
243        }
244    }
245}