1use super::{
11 Headers,
12 entry_name::{EntryName, PseudoHeaderName},
13 header_value::HeaderValueInner,
14 shared_value::SharedValue,
15};
16use crate::{HeaderValue, Method, Status, compact_cow::CompactCow};
17use fieldwork::Fieldwork;
18use smallvec::SmallVec;
19use std::{
20 borrow::{Borrow, Cow},
21 fmt::{self, Display, Formatter},
22 hash,
23 ops::Deref,
24};
25
26#[derive(Debug, Default, Clone, PartialEq, Eq, Fieldwork)]
31#[fieldwork(
32 get,
33 get_mut(option_borrow_inner = false),
34 take,
35 with,
36 without,
37 set,
38 into
39)]
40pub struct PseudoHeaders<'a> {
41 #[field(copy)]
43 pub(in crate::headers) method: Option<Method>,
44
45 #[field(copy)]
47 pub(in crate::headers) status: Option<Status>,
48
49 pub(in crate::headers) path: Option<Cow<'a, str>>,
51
52 pub(in crate::headers) scheme: Option<Cow<'a, str>>,
54
55 pub(in crate::headers) authority: Option<Cow<'a, str>>,
57
58 pub(in crate::headers) protocol: Option<Cow<'a, str>>,
60}
61
62impl PseudoHeaders<'_> {
63 pub fn is_empty(&self) -> bool {
65 self.method.is_none()
66 && self.status.is_none()
67 && self.path.is_none()
68 && self.scheme.is_none()
69 && self.authority.is_none()
70 && self.protocol.is_none()
71 }
72
73 #[allow(
75 dead_code,
76 reason = "consumed by external callers; not visible in this crate's build"
77 )]
78 pub fn into_owned(self) -> PseudoHeaders<'static> {
79 PseudoHeaders {
80 method: self.method,
81 status: self.status,
82 path: self.path.map(|c| Cow::Owned(c.into_owned())),
83 scheme: self.scheme.map(|c| Cow::Owned(c.into_owned())),
84 authority: self.authority.map(|c| Cow::Owned(c.into_owned())),
85 protocol: self.protocol.map(|c| Cow::Owned(c.into_owned())),
86 }
87 }
88}
89
90impl Display for PseudoHeaders<'_> {
91 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
92 if let Some(method) = &self.method {
93 writeln!(f, ":method: {method}")?;
94 }
95
96 if let Some(status) = &self.status {
97 writeln!(f, ":status: {status}")?;
98 }
99
100 if let Some(path) = &self.path {
101 writeln!(f, ":path: {path}")?;
102 }
103 if let Some(scheme) = &self.scheme {
104 writeln!(f, ":scheme: {scheme}")?;
105 }
106
107 if let Some(authority) = &self.authority {
108 writeln!(f, ":authority: {authority}")?;
109 }
110
111 if let Some(protocol) = &self.protocol {
112 writeln!(f, ":protocol: {protocol}")?;
113 }
114
115 Ok(())
116 }
117}
118
119#[derive(Debug, Clone, Fieldwork)]
121#[fieldwork(get, get_mut, into_field)]
122pub struct FieldSection<'a> {
123 pseudo_headers: PseudoHeaders<'a>,
125
126 headers: Cow<'a, Headers>,
128}
129
130impl<'a> FieldSection<'a> {
131 pub fn new(pseudo_headers: PseudoHeaders<'a>, headers: &'a Headers) -> Self {
133 Self {
134 pseudo_headers,
135 headers: Cow::Borrowed(headers),
136 }
137 }
138
139 pub(in crate::headers) fn from_owned(
142 pseudo_headers: PseudoHeaders<'static>,
143 headers: Headers,
144 ) -> FieldSection<'static> {
145 FieldSection {
146 pseudo_headers,
147 headers: Cow::Owned(headers),
148 }
149 }
150
151 pub(in crate::headers) fn field_lines(&self) -> FieldLines<'_> {
160 fn field_line_value_from(v: &HeaderValue) -> FieldLineValue<'_> {
161 match &v.inner {
162 HeaderValueInner::Utf8(CompactCow::Borrowed(b)) => {
163 FieldLineValue::Static(b.as_bytes())
164 }
165 HeaderValueInner::Utf8(CompactCow::Shared(s)) => {
166 FieldLineValue::Shared(SharedValue::Utf8(s.clone()))
167 }
168 HeaderValueInner::Bytes(b) => FieldLineValue::Shared(SharedValue::Bytes(b.clone())),
169 HeaderValueInner::Utf8(CompactCow::Owned(_)) => {
170 FieldLineValue::Borrowed(v.as_ref())
171 }
172 }
173 }
174
175 let mut lines = SmallVec::with_capacity(self.headers.len() + 6);
179 if let Some(method) = &self.pseudo_headers.method {
180 lines.push((
181 PseudoHeaderName::Method.into(),
182 FieldLineValue::Static(method.as_str().as_bytes()),
183 false,
184 ));
185 }
186
187 if let Some(status) = &self.pseudo_headers.status {
188 lines.push((
189 PseudoHeaderName::Status.into(),
190 FieldLineValue::Static(status.code().as_bytes()),
191 false,
192 ));
193 }
194
195 if let Some(path) = &self.pseudo_headers.path {
196 lines.push((
197 PseudoHeaderName::Path.into(),
198 FieldLineValue::Borrowed(path.as_bytes()),
199 false,
200 ));
201 }
202 if let Some(scheme) = &self.pseudo_headers.scheme {
203 lines.push((
204 PseudoHeaderName::Scheme.into(),
205 FieldLineValue::Borrowed(scheme.as_bytes()),
206 false,
207 ));
208 }
209
210 if let Some(authority) = &self.pseudo_headers.authority {
211 lines.push((
212 PseudoHeaderName::Authority.into(),
213 FieldLineValue::Borrowed(authority.as_bytes()),
214 false,
215 ));
216 }
217
218 if let Some(protocol) = &self.pseudo_headers.protocol {
219 lines.push((
220 PseudoHeaderName::Protocol.into(),
221 FieldLineValue::Borrowed(protocol.as_bytes()),
222 false,
223 ));
224 }
225
226 for (k, hv) in &self.headers.known {
231 for v in hv {
232 let value = field_line_value_from(v);
233 lines.push((EntryName::Known(*k), value, v.is_never_indexed()));
234 }
235 }
236
237 for (uhn, hv) in &self.headers.unknown {
238 for v in hv {
239 let value = field_line_value_from(v);
240 let lowered = uhn.clone().into_lower_static();
244 let name = match lowered.as_static_str() {
245 Some(s) => EntryName::UnknownStatic(s),
246 None => EntryName::Unknown(lowered),
247 };
248 lines.push((name, value, v.is_never_indexed()));
249 }
250 }
251
252 lines
253 }
254
255 pub fn into_parts(self) -> (PseudoHeaders<'a>, Headers) {
257 (self.pseudo_headers, self.headers.into_owned())
258 }
259
260 pub(crate) fn uncompressed_len(&self) -> u64 {
274 self.field_lines()
275 .iter()
276 .map(|(name, value, _)| name.len() as u64 + value.len() as u64 + 32)
277 .sum()
278 }
279}
280
281impl Display for FieldSection<'_> {
282 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
283 write!(f, "{}", self.pseudo_headers)?;
284 for (n, v) in &*self.headers {
285 for v in v {
286 writeln!(f, "{n}: {v}")?;
287 }
288 }
289 Ok(())
290 }
291}
292
293pub(in crate::headers) type FieldLines<'a> =
299 SmallVec<[(EntryName<'a>, FieldLineValue<'a>, bool); 16]>;
300
301#[derive(Debug, Clone)]
312pub(crate) enum FieldLineValue<'a> {
313 Static(&'static [u8]),
314 Borrowed(&'a [u8]),
315 Owned(Vec<u8>),
316 Shared(SharedValue),
317}
318
319impl Deref for FieldLineValue<'_> {
320 type Target = [u8];
321
322 fn deref(&self) -> &Self::Target {
323 self.as_bytes()
324 }
325}
326
327impl PartialEq for FieldLineValue<'_> {
328 fn eq(&self, other: &Self) -> bool {
329 self.as_bytes() == other.as_bytes()
330 }
331}
332
333impl Eq for FieldLineValue<'_> {}
334
335impl Borrow<[u8]> for FieldLineValue<'_> {
338 fn borrow(&self) -> &[u8] {
339 self.as_bytes()
340 }
341}
342
343impl hash::Hash for FieldLineValue<'_> {
344 fn hash<H: hash::Hasher>(&self, state: &mut H) {
345 self.as_bytes().hash(state);
346 }
347}
348
349impl FieldLineValue<'_> {
350 pub(in crate::headers) fn into_shared(self) -> FieldLineValue<'static> {
353 match self {
354 FieldLineValue::Static(b) => FieldLineValue::Static(b),
355 other => FieldLineValue::Shared(other.into()),
356 }
357 }
358
359 pub(in crate::headers) fn reborrow(&self) -> FieldLineValue<'_> {
360 match self {
361 FieldLineValue::Static(items) => FieldLineValue::Static(items),
362 FieldLineValue::Borrowed(items) => FieldLineValue::Borrowed(items),
363 FieldLineValue::Owned(items) => FieldLineValue::Borrowed(items),
364 FieldLineValue::Shared(shared) => FieldLineValue::Shared(shared.clone()),
365 }
366 }
367
368 pub(in crate::headers) fn as_bytes(&self) -> &[u8] {
369 match self {
370 FieldLineValue::Static(items) | FieldLineValue::Borrowed(items) => items,
371 FieldLineValue::Owned(items) => items,
372 FieldLineValue::Shared(shared) => shared.as_bytes(),
373 }
374 }
375}
376
377impl From<Cow<'static, [u8]>> for FieldLineValue<'static> {
378 fn from(value: Cow<'static, [u8]>) -> Self {
379 match value {
380 Cow::Borrowed(b) => FieldLineValue::Static(b),
381 Cow::Owned(v) => FieldLineValue::Owned(v),
382 }
383 }
384}
385
386impl From<FieldLineValue<'_>> for HeaderValue {
390 fn from(value: FieldLineValue<'_>) -> Self {
391 match value {
392 FieldLineValue::Static(b) => HeaderValue::from(b),
393 FieldLineValue::Borrowed(b) => HeaderValue::parse(b),
394 FieldLineValue::Owned(v) => HeaderValue::from(v),
395 FieldLineValue::Shared(s) => HeaderValue::from(s),
396 }
397 }
398}
399
400#[cfg(test)]
401mod tests {
402 use super::*;
403 use crate::{KnownHeaderName, Method};
404
405 #[test]
406 fn uncompressed_len_sums_name_value_plus_32_per_field() {
407 let mut headers = Headers::new();
410 headers.insert(KnownHeaderName::Accept, "*/*");
411 let pseudo = PseudoHeaders::default().with_method(Method::Get);
412 let field_section = FieldSection::new(pseudo, &headers);
413 assert_eq!(field_section.uncompressed_len(), 42 + 41);
414 }
415
416 #[test]
417 fn uncompressed_len_counts_repeated_values_separately() {
418 let mut headers = Headers::new();
420 headers.append(KnownHeaderName::SetCookie, "a=1"); headers.append(KnownHeaderName::SetCookie, "bb=22"); let field_section = FieldSection::new(PseudoHeaders::default(), &headers);
423 assert_eq!(field_section.uncompressed_len(), 45 + 47);
424 }
425}