Skip to main content

trillium_http/headers/
header_value.rs

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