Skip to main content

trillium_http/headers/
header_value.rs

1use HeaderValueInner::{Bytes, Utf8};
2use smartcow::SmartCow;
3use smartstring::SmartString;
4use std::{
5    borrow::Cow,
6    fmt::{Debug, Display, Formatter, Write},
7};
8
9/// A `HeaderValue` represents the right hand side of a single `name:
10/// value` pair.
11#[derive(Clone)]
12pub struct HeaderValue {
13    pub(crate) inner: HeaderValueInner,
14    /// HPACK / QPACK "Never-Indexed" bit, carried for proxy round-trip fidelity:
15    /// decoders set it from the wire bit and encoders re-emit a never-indexed
16    /// literal when it is set. Not part of value identity (`PartialEq` / `Hash`
17    /// ignore it).
18    pub(crate) never_indexed: bool,
19}
20
21impl HeaderValue {
22    pub(crate) const fn from_inner(inner: HeaderValueInner) -> Self {
23        Self {
24            inner,
25            never_indexed: false,
26        }
27    }
28
29    pub(crate) fn is_never_indexed(&self) -> bool {
30        self.never_indexed
31    }
32
33    pub(crate) fn set_never_indexed(&mut self, never_indexed: bool) {
34        self.never_indexed = never_indexed;
35    }
36}
37
38impl PartialEq for HeaderValue {
39    fn eq(&self, other: &Self) -> bool {
40        self.inner == other.inner
41    }
42}
43
44impl Eq for HeaderValue {}
45
46impl std::hash::Hash for HeaderValue {
47    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
48        self.inner.hash(state);
49    }
50}
51
52impl PartialOrd for HeaderValue {
53    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
54        Some(self.cmp(other))
55    }
56}
57
58impl Ord for HeaderValue {
59    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
60        self.inner.cmp(&other.inner)
61    }
62}
63
64impl From<Cow<'static, [u8]>> for HeaderValue {
65    fn from(value: Cow<'static, [u8]>) -> Self {
66        match value {
67            Cow::Borrowed(bytes) => match std::str::from_utf8(bytes) {
68                Ok(s) => Self::from_inner(Utf8(SmartCow::Borrowed(s))),
69                Err(_) => Self::from_inner(Bytes(bytes.into())),
70            },
71
72            Cow::Owned(bytes) => match String::from_utf8(bytes) {
73                Ok(s) => Self::from_inner(Utf8(SmartCow::Owned(s.into()))),
74                Err(e) => Self::from_inner(Bytes(e.into_bytes().into())),
75            },
76        }
77    }
78}
79
80#[derive(Eq, PartialEq, Clone, Hash)]
81pub(crate) enum HeaderValueInner {
82    Utf8(SmartCow<'static>),
83    Bytes(Box<[u8]>),
84}
85
86impl PartialOrd for HeaderValueInner {
87    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
88        Some(self.cmp(other))
89    }
90}
91impl Ord for HeaderValueInner {
92    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
93        let this: &[u8] = self.as_ref();
94        let that: &[u8] = other.as_ref();
95        this.cmp(that)
96    }
97}
98
99#[cfg(feature = "serde")]
100impl serde::Serialize for HeaderValue {
101    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
102    where
103        S: serde::Serializer,
104    {
105        match &self.inner {
106            Utf8(s) => serializer.serialize_str(s),
107            Bytes(bytes) => serializer.serialize_bytes(bytes),
108        }
109    }
110}
111
112impl Debug for HeaderValue {
113    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
114        match &self.inner {
115            Utf8(s) => Debug::fmt(s, f),
116            Bytes(b) => Debug::fmt(&String::from_utf8_lossy(b), f),
117        }
118    }
119}
120
121impl HeaderValue {
122    /// Build a new header value from a &'static str at compile time
123    pub const fn const_new(value: &'static str) -> Self {
124        Self::from_inner(Utf8(SmartCow::Borrowed(value)))
125    }
126
127    /// determine if this header contains no unsafe characters (\r, \n, \0)
128    pub fn is_valid(&self) -> bool {
129        memchr::memchr3(b'\r', b'\n', 0, self.as_ref()).is_none()
130    }
131
132    /// Returns this header value as a &str if it is utf8, `None` otherwise. For
133    /// a byte slice regardless of utf8-ness, use the `AsRef<[u8]>` impl.
134    pub fn as_str(&self) -> Option<&str> {
135        match &self.inner {
136            Utf8(utf8) => Some(utf8),
137            Bytes(_) => None,
138        }
139    }
140}
141
142impl HeaderValue {
143    pub(crate) fn parse(bytes: &[u8]) -> Self {
144        match std::str::from_utf8(bytes) {
145            Ok(s) => Self::from_inner(Utf8(SmartCow::Owned(s.into()))),
146            Err(_) => Self::from_inner(Bytes(bytes.into())),
147        }
148    }
149}
150
151impl Display for HeaderValue {
152    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
153        match &self.inner {
154            Utf8(s) => f.write_str(s),
155            Bytes(b) => f.write_str(&String::from_utf8_lossy(b)),
156        }
157    }
158}
159
160impl From<Vec<u8>> for HeaderValue {
161    fn from(v: Vec<u8>) -> Self {
162        match String::from_utf8(v) {
163            Ok(s) => Self::from_inner(Utf8(SmartCow::Owned(s.into()))),
164            Err(e) => Self::from_inner(Bytes(e.into_bytes().into())),
165        }
166    }
167}
168
169impl From<Cow<'static, str>> for HeaderValue {
170    fn from(c: Cow<'static, str>) -> Self {
171        Self::from_inner(Utf8(SmartCow::from(c)))
172    }
173}
174
175impl From<&'static [u8]> for HeaderValue {
176    fn from(b: &'static [u8]) -> Self {
177        match std::str::from_utf8(b) {
178            Ok(s) => Self::from_inner(Utf8(SmartCow::Borrowed(s))),
179            Err(_) => Self::from_inner(Bytes(b.into())),
180        }
181    }
182}
183
184impl From<String> for HeaderValue {
185    fn from(s: String) -> Self {
186        Self::from_inner(Utf8(SmartCow::Owned(s.into())))
187    }
188}
189
190impl From<&'static str> for HeaderValue {
191    fn from(s: &'static str) -> Self {
192        Self::from_inner(Utf8(SmartCow::Borrowed(s)))
193    }
194}
195
196macro_rules! delegate_from_to_format {
197    ($($t:ty),*) => {
198        $(
199        impl From<$t> for HeaderValue {
200            fn from(value: $t) -> Self {
201                format_args!("{value}").into()
202            }
203        }
204        )*
205    };
206}
207
208delegate_from_to_format!(usize, u64, u16, u32, i32, i64);
209
210impl From<std::fmt::Arguments<'_>> for HeaderValue {
211    fn from(value: std::fmt::Arguments<'_>) -> Self {
212        let mut s = SmartString::new();
213        s.write_fmt(value).unwrap();
214        Self::from_inner(Utf8(SmartCow::Owned(s)))
215    }
216}
217
218impl AsRef<[u8]> for HeaderValueInner {
219    fn as_ref(&self) -> &[u8] {
220        match self {
221            Utf8(utf8) => utf8.as_bytes(),
222            Bytes(b) => b,
223        }
224    }
225}
226
227impl AsRef<[u8]> for HeaderValue {
228    fn as_ref(&self) -> &[u8] {
229        self.inner.as_ref()
230    }
231}
232
233impl PartialEq<&str> for HeaderValue {
234    fn eq(&self, other: &&str) -> bool {
235        self.as_str() == Some(*other)
236    }
237}
238
239impl PartialEq<&[u8]> for HeaderValue {
240    fn eq(&self, other: &&[u8]) -> bool {
241        self.as_ref() == *other
242    }
243}
244
245impl PartialEq<[u8]> for HeaderValue {
246    fn eq(&self, other: &[u8]) -> bool {
247        self.as_ref() == other
248    }
249}
250
251impl PartialEq<str> for HeaderValue {
252    fn eq(&self, other: &str) -> bool {
253        self.as_str() == Some(other)
254    }
255}
256
257impl PartialEq<String> for HeaderValue {
258    fn eq(&self, other: &String) -> bool {
259        self.as_str() == Some(other)
260    }
261}
262
263impl PartialEq<&String> for HeaderValue {
264    fn eq(&self, other: &&String) -> bool {
265        self.as_str() == Some(&**other)
266    }
267}
268
269impl PartialEq<[u8]> for &HeaderValue {
270    fn eq(&self, other: &[u8]) -> bool {
271        self.as_ref() == other
272    }
273}
274
275impl PartialEq<str> for &HeaderValue {
276    fn eq(&self, other: &str) -> bool {
277        self.as_str() == Some(other)
278    }
279}
280
281impl PartialEq<String> for &HeaderValue {
282    fn eq(&self, other: &String) -> bool {
283        self.as_str() == Some(other)
284    }
285}