1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
use std::{
borrow::Cow,
fmt::{Display, Formatter},
};
/**
* <https://tools.ietf.org/html/rfc7541>
* 4.1. Calculating Table Size
*/
pub const ESTIMATED_OVERHEAD_BYTES: usize = 32;
/// A QPACK header field that can own or borrow its name and value.
#[derive(Debug, PartialEq, Clone, Hash, Eq)]
pub struct HeaderField<'a> {
pub name: Cow<'a, [u8]>,
pub value: Cow<'a, [u8]>,
/// Whether this field must use a literal representation when forwarded.
pub sensitive: bool,
}
impl HeaderField<'static> {
pub fn new<T, S>(name: T, value: S) -> Self
where
T: Into<Vec<u8>>,
S: Into<Vec<u8>>,
{
HeaderField {
name: Cow::Owned(name.into()),
value: Cow::Owned(value.into()),
sensitive: false,
}
}
}
impl<'a> HeaderField<'a> {
pub(crate) fn borrowed(name: &'a [u8], value: &'a [u8], sensitive: bool) -> Self {
Self {
name: Cow::Borrowed(name),
value: Cow::Borrowed(value),
sensitive,
}
}
pub fn mem_size(&self) -> usize {
self.name.len() + self.value.len() + ESTIMATED_OVERHEAD_BYTES
}
pub fn with_value<T>(&self, value: T) -> Self
where
T: Into<Vec<u8>>,
{
Self {
name: self.name.clone(),
value: Cow::Owned(value.into()),
sensitive: self.sensitive,
}
}
/// Marks the field as sensitive for compression.
///
/// This carries QPACK's never-indexed bit through the HTTP header model so
/// an intermediary can preserve the peer's literal-only instruction when
/// forwarding the field.
///
/// See [RFC 9204, Section 4.5.4](https://www.rfc-editor.org/rfc/rfc9204.html#section-4.5.4).
pub fn set_sensitive(&mut self, sensitive: bool) {
self.sensitive = sensitive;
}
/// Returns whether this field must remain a never-indexed literal.
pub fn is_sensitive(&self) -> bool {
self.sensitive
}
/// Returns this field with the requested compression sensitivity.
pub fn with_sensitive(mut self, sensitive: bool) -> Self {
self.set_sensitive(sensitive);
self
}
pub fn into_inner(self) -> (Cow<'a, [u8]>, Cow<'a, [u8]>) {
(self.name, self.value)
}
}
impl<'a, 'b: 'a> AsRef<HeaderField<'a>> for HeaderField<'b> {
fn as_ref(&self) -> &HeaderField<'a> {
self
}
}
impl Display for HeaderField<'_> {
fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
write!(
f,
"\"{}\": \"{}\"",
String::from_utf8_lossy(&self.name),
String::from_utf8_lossy(&self.value)
)?;
Ok(())
}
}
impl From<HeaderField<'_>> for String {
fn from(field: HeaderField<'_>) -> String {
format!(
"{}\t{}",
String::from_utf8_lossy(&field.name),
String::from_utf8_lossy(&field.value)
)
}
}
impl<N, V> From<(N, V)> for HeaderField<'static>
where
N: AsRef<[u8]>,
V: AsRef<[u8]>,
{
fn from(header: (N, V)) -> Self {
let (name, value) = header;
Self {
// Generic tuple conversion produces an owned field. Borrowed HTTP
// header iteration constructs `HeaderField<'_>` directly.
name: Cow::Owned(Vec::from(name.as_ref())),
value: Cow::Owned(Vec::from(value.as_ref())),
sensitive: false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
/**
* https://tools.ietf.org/html/rfc7541#section-4.1
* "The size of an entry is the sum of its name's length in octets (as
* defined in Section 5.2), its value's length in octets, and 32."
* "The size of an entry is calculated using the length of its name and
* value without any Huffman encoding applied."
*/
#[test]
fn test_field_size_is_offset_by_32() {
let field = HeaderField {
name: Cow::Borrowed(b"Name"),
value: Cow::Borrowed(b"Value"),
sensitive: false,
};
assert_eq!(field.mem_size(), 4 + 5 + 32);
}
#[test]
fn with_value() {
let field = HeaderField {
name: Cow::Borrowed(b"Name"),
value: Cow::Borrowed(b"Value"),
sensitive: true,
};
assert_eq!(
field.with_value("New value"),
HeaderField {
name: Cow::Borrowed(b"Name"),
value: Cow::Borrowed(b"New value"),
sensitive: true,
}
);
}
}