Skip to main content

pingora_http/
lib.rs

1// Copyright 2026 Cloudflare, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! HTTP header objects that preserve http header cases
16//!
17//! Although HTTP header names are supposed to be case-insensitive for compatibility, proxies
18//! ideally shouldn't alter the HTTP traffic, especially the headers they don't need to read.
19//!
20//! This crate provide structs and methods to preserve the headers in order to build a transparent
21//! proxy.
22
23#![allow(clippy::new_without_default)]
24
25use bytes::BufMut;
26use http::header::{AsHeaderName, HeaderName, HeaderValue};
27use http::request::Builder as ReqBuilder;
28use http::request::Parts as ReqParts;
29use http::response::Builder as RespBuilder;
30use http::response::Parts as RespParts;
31use http::uri::Uri;
32use pingora_error::{ErrorType::*, OrErr, Result};
33use std::ops::{Deref, DerefMut};
34
35pub use http::method::Method;
36pub use http::status::StatusCode;
37pub use http::version::Version;
38pub use http::HeaderMap as HMap;
39
40mod case_header_name;
41use case_header_name::CaseHeaderName;
42pub use case_header_name::IntoCaseHeaderName;
43
44pub mod prelude {
45    pub use crate::RequestHeader;
46    pub use crate::ResponseHeader;
47}
48
49/* an ordered header map to store the original case of each header name
50HMap({
51    "foo": ["Foo", "foO", "FoO"]
52})
53The order how HeaderMap iter over its items is "arbitrary, but consistent".
54Hopefully this property makes sure this map of header names always iterates in the
55same order of the map of header values.
56This idea is inspaired by hyper @nox
57*/
58type CaseMap = HMap<CaseHeaderName>;
59
60pub enum HeaderNameVariant<'a> {
61    Case(&'a CaseHeaderName),
62    Titled(&'a str),
63}
64
65/// The HTTP request header type.
66///
67/// This type is similar to [http::request::Parts] but preserves header name case.
68/// It also preserves request path even if it is not UTF-8.
69///
70/// [RequestHeader] implements [Deref] for [http::request::Parts] so it can be used as it in most
71/// places.
72#[derive(Debug)]
73pub struct RequestHeader {
74    base: ReqParts,
75    header_name_map: Option<CaseMap>,
76    // store the raw path bytes only if it is invalid utf-8
77    raw_path_fallback: Vec<u8>, // can also be Box<[u8]>
78    // whether we send END_STREAM with HEADERS for h2 requests
79    send_end_stream: bool,
80}
81
82impl AsRef<ReqParts> for RequestHeader {
83    fn as_ref(&self) -> &ReqParts {
84        &self.base
85    }
86}
87
88impl Deref for RequestHeader {
89    type Target = ReqParts;
90
91    fn deref(&self) -> &Self::Target {
92        &self.base
93    }
94}
95
96impl DerefMut for RequestHeader {
97    fn deref_mut(&mut self) -> &mut Self::Target {
98        &mut self.base
99    }
100}
101
102impl RequestHeader {
103    fn new_no_case(size_hint: Option<usize>) -> Self {
104        let mut base = ReqBuilder::new().body(()).unwrap().into_parts().0;
105        base.headers.reserve(http_header_map_upper_bound(size_hint));
106        RequestHeader {
107            base,
108            header_name_map: None,
109            raw_path_fallback: vec![],
110            send_end_stream: true,
111        }
112    }
113
114    /// Create a new [RequestHeader] with the given method and path.
115    ///
116    /// The `path` can be non UTF-8.
117    pub fn build(
118        method: impl TryInto<Method>,
119        path: &[u8],
120        size_hint: Option<usize>,
121    ) -> Result<Self> {
122        let mut req = Self::build_no_case(method, path, size_hint)?;
123        req.header_name_map = Some(CaseMap::with_capacity(http_header_map_upper_bound(
124            size_hint,
125        )));
126        Ok(req)
127    }
128
129    /// Create a new [RequestHeader] with the given method and path without preserving header case.
130    ///
131    /// A [RequestHeader] created from this type is more space efficient than those from [Self::build()].
132    ///
133    /// Use this method if reading from or writing to HTTP/2 sessions where header case doesn't matter anyway.
134    pub fn build_no_case(
135        method: impl TryInto<Method>,
136        path: &[u8],
137        size_hint: Option<usize>,
138    ) -> Result<Self> {
139        let mut req = Self::new_no_case(size_hint);
140        req.base.method = method
141            .try_into()
142            .explain_err(InvalidHTTPHeader, |_| "invalid method")?;
143        req.set_raw_path(path)?;
144        Ok(req)
145    }
146
147    /// Append the header name and value to `self`.
148    ///
149    /// If there are already some headers under the same name, a new value will be added without
150    /// any others being removed.
151    pub fn append_header(
152        &mut self,
153        name: impl IntoCaseHeaderName,
154        value: impl TryInto<HeaderValue>,
155    ) -> Result<bool> {
156        let header_value = value
157            .try_into()
158            .explain_err(InvalidHTTPHeader, |_| "invalid value while append")?;
159        append_header_value(
160            self.header_name_map.as_mut(),
161            &mut self.base.headers,
162            name,
163            header_value,
164        )
165    }
166
167    /// Insert the header name and value to `self`.
168    ///
169    /// Different from [Self::append_header()], this method will replace all other existing headers
170    /// under the same name (case-insensitive).
171    pub fn insert_header(
172        &mut self,
173        name: impl IntoCaseHeaderName,
174        value: impl TryInto<HeaderValue>,
175    ) -> Result<()> {
176        let header_value = value
177            .try_into()
178            .explain_err(InvalidHTTPHeader, |_| "invalid value while insert")?;
179        insert_header_value(
180            self.header_name_map.as_mut(),
181            &mut self.base.headers,
182            name,
183            header_value,
184        )
185    }
186
187    /// Remove all headers under the name
188    pub fn remove_header<'a, N: ?Sized>(&mut self, name: &'a N) -> Option<HeaderValue>
189    where
190        &'a N: 'a + AsHeaderName,
191    {
192        remove_header(self.header_name_map.as_mut(), &mut self.base.headers, name)
193    }
194
195    /// Write the header to the `buf` in HTTP/1.1 wire format.
196    ///
197    /// The header case will be preserved.
198    pub fn header_to_h1_wire(&self, buf: &mut impl BufMut) {
199        header_to_h1_wire(self.header_name_map.as_ref(), &self.base.headers, buf)
200    }
201
202    /// If case sensitivity is enabled, returns an iterator to iterate over case-sensitive header names and values.
203    /// Otherwise returns an empty iterator.
204    ///
205    /// Headers of the same name are visited in insertion order.
206    pub fn case_header_iter(&self) -> impl Iterator<Item = (&CaseHeaderName, &HeaderValue)> + '_ {
207        case_header_iter(self.header_name_map.as_ref(), &self.base.headers)
208    }
209
210    /// Returns true if the request has case-sensitive headers.
211    pub fn has_case(&self) -> bool {
212        self.header_name_map.is_some()
213    }
214
215    pub fn map<F: FnMut(HeaderNameVariant, &HeaderValue) -> Result<()>>(
216        &self,
217        mut f: F,
218    ) -> Result<()> {
219        let key_map = self.header_name_map.as_ref();
220        let value_map = &self.base.headers;
221
222        if let Some(key_map) = key_map {
223            let iter = key_map.iter().zip(value_map.iter());
224            for ((header, case_header), (header2, val)) in iter {
225                if header != header2 {
226                    // in case the header iteration order changes in future versions of HMap
227                    panic!("header iter mismatch {}, {}", header, header2)
228                }
229                f(HeaderNameVariant::Case(case_header), val)?;
230            }
231        } else {
232            for (header, value) in value_map {
233                let titled_header =
234                    case_header_name::titled_header_name_str(header).unwrap_or(header.as_str());
235                f(HeaderNameVariant::Titled(titled_header), value)?;
236            }
237        }
238
239        Ok(())
240    }
241
242    /// Set the request method
243    pub fn set_method(&mut self, method: Method) {
244        self.base.method = method;
245    }
246
247    /// Set the request URI
248    pub fn set_uri(&mut self, uri: http::Uri) {
249        self.base.uri = uri;
250        // Clear out raw_path_fallback, or it will be used when serializing
251        self.raw_path_fallback = vec![];
252    }
253
254    /// Set the request URI directly via raw bytes.
255    ///
256    /// Generally prefer [Self::set_uri()] to modify the header's URI if able.
257    ///
258    /// This API is to allow supporting non UTF-8 cases.
259    pub fn set_raw_path(&mut self, path: &[u8]) -> Result<()> {
260        if let Ok(p) = std::str::from_utf8(path) {
261            let uri = Uri::builder()
262                .path_and_query(p)
263                .build()
264                .explain_err(InvalidHTTPHeader, |_| format!("invalid uri {}", p))?;
265            self.base.uri = uri;
266            // keep raw_path empty, no need to store twice
267        } else {
268            // put a valid utf-8 path into base for read only access
269            let lossy_str = String::from_utf8_lossy(path);
270            let uri = Uri::builder()
271                .path_and_query(lossy_str.as_ref())
272                .build()
273                .explain_err(InvalidHTTPHeader, |_| format!("invalid uri {}", lossy_str))?;
274            self.base.uri = uri;
275            self.raw_path_fallback = path.to_vec();
276        }
277        Ok(())
278    }
279
280    /// Set whether we send an END_STREAM on H2 request HEADERS if body is empty.
281    pub fn set_send_end_stream(&mut self, send_end_stream: bool) {
282        self.send_end_stream = send_end_stream;
283    }
284
285    /// Returns if we support sending an END_STREAM on H2 request HEADERS if body is empty,
286    /// returns None if not H2.
287    pub fn send_end_stream(&self) -> Option<bool> {
288        if self.base.version != Version::HTTP_2 {
289            return None;
290        }
291        Some(self.send_end_stream)
292    }
293
294    /// Return the request path in its raw format
295    ///
296    /// Non-UTF8 is supported.
297    pub fn raw_path(&self) -> &[u8] {
298        if !self.raw_path_fallback.is_empty() {
299            &self.raw_path_fallback
300        } else {
301            // Url should always be set
302            self.base
303                .uri
304                .path_and_query()
305                .as_ref()
306                .unwrap()
307                .as_str()
308                .as_bytes()
309        }
310    }
311
312    /// Return the file extension of the path
313    pub fn uri_file_extension(&self) -> Option<&str> {
314        // get everything after the last '.' in path
315        let (_, ext) = self
316            .uri
317            .path_and_query()
318            .and_then(|pq| pq.path().rsplit_once('.'))?;
319        Some(ext)
320    }
321
322    /// Set http version
323    pub fn set_version(&mut self, version: Version) {
324        self.base.version = version;
325    }
326
327    /// Clone `self` into [http::request::Parts].
328    pub fn as_owned_parts(&self) -> ReqParts {
329        clone_req_parts(&self.base)
330    }
331}
332
333impl Clone for RequestHeader {
334    fn clone(&self) -> Self {
335        Self {
336            base: self.as_owned_parts(),
337            header_name_map: self.header_name_map.clone(),
338            raw_path_fallback: self.raw_path_fallback.clone(),
339            send_end_stream: self.send_end_stream,
340        }
341    }
342}
343
344// The `RequestHeader` will be the no case variant, because `ReqParts` keeps no header case
345impl From<ReqParts> for RequestHeader {
346    fn from(parts: ReqParts) -> RequestHeader {
347        Self {
348            base: parts,
349            header_name_map: None,
350            // no illegal path
351            raw_path_fallback: vec![],
352            send_end_stream: true,
353        }
354    }
355}
356
357impl From<RequestHeader> for ReqParts {
358    fn from(resp: RequestHeader) -> ReqParts {
359        resp.base
360    }
361}
362
363/// The HTTP response header type.
364///
365/// This type is similar to [http::response::Parts] but preserves header name case.
366/// [ResponseHeader] implements [Deref] for [http::response::Parts] so it can be used as it in most
367/// places.
368#[derive(Debug)]
369pub struct ResponseHeader {
370    base: RespParts,
371    // an ordered header map to store the original case of each header name
372    header_name_map: Option<CaseMap>,
373    // the reason phrase of the response, if unset, a default one will be used
374    reason_phrase: Option<String>,
375}
376
377impl AsRef<RespParts> for ResponseHeader {
378    fn as_ref(&self) -> &RespParts {
379        &self.base
380    }
381}
382
383impl Deref for ResponseHeader {
384    type Target = RespParts;
385
386    fn deref(&self) -> &Self::Target {
387        &self.base
388    }
389}
390
391impl DerefMut for ResponseHeader {
392    fn deref_mut(&mut self) -> &mut Self::Target {
393        &mut self.base
394    }
395}
396
397impl Clone for ResponseHeader {
398    fn clone(&self) -> Self {
399        Self {
400            base: self.as_owned_parts(),
401            header_name_map: self.header_name_map.clone(),
402            reason_phrase: self.reason_phrase.clone(),
403        }
404    }
405}
406
407// The `ResponseHeader` will be the no case variant, because `RespParts` keeps no header case
408impl From<RespParts> for ResponseHeader {
409    fn from(parts: RespParts) -> ResponseHeader {
410        Self {
411            base: parts,
412            header_name_map: None,
413            reason_phrase: None,
414        }
415    }
416}
417
418impl From<ResponseHeader> for RespParts {
419    fn from(resp: ResponseHeader) -> RespParts {
420        resp.base
421    }
422}
423
424impl From<Box<ResponseHeader>> for Box<RespParts> {
425    fn from(resp: Box<ResponseHeader>) -> Box<RespParts> {
426        Box::new(resp.base)
427    }
428}
429
430impl ResponseHeader {
431    fn new(size_hint: Option<usize>) -> Self {
432        let mut resp_header = Self::new_no_case(size_hint);
433        resp_header.header_name_map = Some(CaseMap::with_capacity(http_header_map_upper_bound(
434            size_hint,
435        )));
436        resp_header
437    }
438
439    fn new_no_case(size_hint: Option<usize>) -> Self {
440        let mut base = RespBuilder::new().body(()).unwrap().into_parts().0;
441        base.headers.reserve(http_header_map_upper_bound(size_hint));
442        ResponseHeader {
443            base,
444            header_name_map: None,
445            reason_phrase: None,
446        }
447    }
448
449    /// Create a new [ResponseHeader] with the given status code.
450    pub fn build(code: impl TryInto<StatusCode>, size_hint: Option<usize>) -> Result<Self> {
451        let mut resp = Self::new(size_hint);
452        resp.base.status = code
453            .try_into()
454            .explain_err(InvalidHTTPHeader, |_| "invalid status")?;
455        Ok(resp)
456    }
457
458    /// Create a new [ResponseHeader] with the given status code without preserving header case.
459    ///
460    /// A [ResponseHeader] created from this type is more space efficient than those from [Self::build()].
461    ///
462    /// Use this method if reading from or writing to HTTP/2 sessions where header case doesn't matter anyway.
463    pub fn build_no_case(code: impl TryInto<StatusCode>, size_hint: Option<usize>) -> Result<Self> {
464        let mut resp = Self::new_no_case(size_hint);
465        resp.base.status = code
466            .try_into()
467            .explain_err(InvalidHTTPHeader, |_| "invalid status")?;
468        Ok(resp)
469    }
470
471    /// Append the header name and value to `self`.
472    ///
473    /// If there are already some headers under the same name, a new value will be added without
474    /// any others being removed.
475    pub fn append_header(
476        &mut self,
477        name: impl IntoCaseHeaderName,
478        value: impl TryInto<HeaderValue>,
479    ) -> Result<bool> {
480        let header_value = value
481            .try_into()
482            .explain_err(InvalidHTTPHeader, |_| "invalid value while append")?;
483        append_header_value(
484            self.header_name_map.as_mut(),
485            &mut self.base.headers,
486            name,
487            header_value,
488        )
489    }
490
491    /// Insert the header name and value to `self`.
492    ///
493    /// Different from [Self::append_header()], this method will replace all other existing headers
494    /// under the same name (case insensitive).
495    pub fn insert_header(
496        &mut self,
497        name: impl IntoCaseHeaderName,
498        value: impl TryInto<HeaderValue>,
499    ) -> Result<()> {
500        let header_value = value
501            .try_into()
502            .explain_err(InvalidHTTPHeader, |_| "invalid value while insert")?;
503        insert_header_value(
504            self.header_name_map.as_mut(),
505            &mut self.base.headers,
506            name,
507            header_value,
508        )
509    }
510
511    /// Remove all headers under the name
512    pub fn remove_header<'a, N: ?Sized>(&mut self, name: &'a N) -> Option<HeaderValue>
513    where
514        &'a N: 'a + AsHeaderName,
515    {
516        remove_header(self.header_name_map.as_mut(), &mut self.base.headers, name)
517    }
518
519    /// Write the header to the `buf` in HTTP/1.1 wire format.
520    ///
521    /// The header case will be preserved.
522    pub fn header_to_h1_wire(&self, buf: &mut impl BufMut) {
523        header_to_h1_wire(self.header_name_map.as_ref(), &self.base.headers, buf)
524    }
525
526    /// If case sensitivity is enabled, returns an iterator to iterate over case-sensitive header names and values.
527    /// Otherwise returns an empty iterator.
528    ///
529    /// Headers of the same name are visited in insertion order.
530    pub fn case_header_iter(&self) -> impl Iterator<Item = (&CaseHeaderName, &HeaderValue)> + '_ {
531        case_header_iter(self.header_name_map.as_ref(), &self.base.headers)
532    }
533
534    /// Returns true if the response has case-sensitive headers.
535    pub fn has_case(&self) -> bool {
536        self.header_name_map.is_some()
537    }
538
539    pub fn map<F: FnMut(HeaderNameVariant, &HeaderValue) -> Result<()>>(
540        &self,
541        mut f: F,
542    ) -> Result<()> {
543        let key_map = self.header_name_map.as_ref();
544        let value_map = &self.base.headers;
545
546        if let Some(key_map) = key_map {
547            let iter = key_map.iter().zip(value_map.iter());
548            for ((header, case_header), (header2, val)) in iter {
549                if header != header2 {
550                    // in case the header iteration order changes in future versions of HMap
551                    panic!("header iter mismatch {}, {}", header, header2)
552                }
553                f(HeaderNameVariant::Case(case_header), val)?;
554            }
555        } else {
556            for (header, value) in value_map {
557                let titled_header =
558                    case_header_name::titled_header_name_str(header).unwrap_or(header.as_str());
559                f(HeaderNameVariant::Titled(titled_header), value)?;
560            }
561        }
562
563        Ok(())
564    }
565
566    /// Set the status code
567    pub fn set_status(&mut self, status: impl TryInto<StatusCode>) -> Result<()> {
568        self.base.status = status
569            .try_into()
570            .explain_err(InvalidHTTPHeader, |_| "invalid status")?;
571        Ok(())
572    }
573
574    /// Set the HTTP version
575    pub fn set_version(&mut self, version: Version) {
576        self.base.version = version
577    }
578
579    /// Set the HTTP reason phase. If `None`, a default reason phase will be used
580    pub fn set_reason_phrase(&mut self, reason_phrase: Option<&str>) -> Result<()> {
581        // No need to allocate memory to store the phrase if it is the default one.
582        if reason_phrase == self.base.status.canonical_reason() {
583            self.reason_phrase = None;
584            return Ok(());
585        }
586
587        // TODO: validate it "*( HTAB / SP / VCHAR / obs-text )"
588        self.reason_phrase = reason_phrase.map(str::to_string);
589        Ok(())
590    }
591
592    /// Get the HTTP reason phase. If [Self::set_reason_phrase()] is never called
593    /// or set to `None`, a default reason phase will be used
594    pub fn get_reason_phrase(&self) -> Option<&str> {
595        self.reason_phrase
596            .as_deref()
597            .or_else(|| self.base.status.canonical_reason())
598    }
599
600    /// Clone `self` into [http::response::Parts].
601    pub fn as_owned_parts(&self) -> RespParts {
602        clone_resp_parts(&self.base)
603    }
604
605    /// Helper function to set the HTTP content length on the response header.
606    pub fn set_content_length(&mut self, len: usize) -> Result<()> {
607        self.insert_header(http::header::CONTENT_LENGTH, len)
608    }
609}
610
611fn clone_req_parts(me: &ReqParts) -> ReqParts {
612    let mut parts = ReqBuilder::new()
613        .method(me.method.clone())
614        .uri(me.uri.clone())
615        .version(me.version)
616        .body(())
617        .unwrap()
618        .into_parts()
619        .0;
620    parts.headers = me.headers.clone();
621    parts.extensions = me.extensions.clone();
622    parts
623}
624
625fn clone_resp_parts(me: &RespParts) -> RespParts {
626    let mut parts = RespBuilder::new()
627        .status(me.status)
628        .version(me.version)
629        .body(())
630        .unwrap()
631        .into_parts()
632        .0;
633    parts.headers = me.headers.clone();
634    parts.extensions = me.extensions.clone();
635    parts
636}
637
638// This function returns an upper bound on the size of the header map used inside the http crate.
639// As of version 0.2, there is a limit of 1 << 15 (32,768) items inside the map. There is an
640// assertion against this size inside the crate, so we want to avoid panicking by not exceeding this
641// upper bound.
642fn http_header_map_upper_bound(size_hint: Option<usize>) -> usize {
643    // Even though the crate has 1 << 15 as the max size, calls to `with_capacity` invoke a
644    // function that returns the size + size / 3.
645    //
646    // See https://github.com/hyperium/http/blob/34a9d6bdab027948d6dea3b36d994f9cbaf96f75/src/header/map.rs#L3220
647    //
648    // Therefore we set our max size to be even lower, so we guarantee ourselves we won't hit that
649    // upper bound in the crate. Any way you cut it, 4,096 headers is insane.
650    const PINGORA_MAX_HEADER_COUNT: usize = 4096;
651    const INIT_HEADER_SIZE: usize = 8;
652
653    // We select the size hint or the max size here, ensuring that we pick a value substantially lower
654    // than 1 << 15 with room to grow the header map.
655    std::cmp::min(
656        size_hint.unwrap_or(INIT_HEADER_SIZE),
657        PINGORA_MAX_HEADER_COUNT,
658    )
659}
660
661#[inline]
662fn append_header_value<T>(
663    name_map: Option<&mut CaseMap>,
664    value_map: &mut HMap<T>,
665    name: impl IntoCaseHeaderName,
666    value: T,
667) -> Result<bool> {
668    let case_header_name = name.into_case_header_name();
669    let header_name: HeaderName = case_header_name
670        .as_slice()
671        .try_into()
672        .or_err(InvalidHTTPHeader, "invalid header name")?;
673    // store the original case in the map
674    if let Some(name_map) = name_map {
675        name_map.append(header_name.clone(), case_header_name);
676    }
677
678    Ok(value_map.append(header_name, value))
679}
680
681#[inline]
682fn insert_header_value<T>(
683    name_map: Option<&mut CaseMap>,
684    value_map: &mut HMap<T>,
685    name: impl IntoCaseHeaderName,
686    value: T,
687) -> Result<()> {
688    let case_header_name = name.into_case_header_name();
689    let header_name: HeaderName = case_header_name
690        .as_slice()
691        .try_into()
692        .or_err(InvalidHTTPHeader, "invalid header name")?;
693    if let Some(name_map) = name_map {
694        // store the original case in the map
695        name_map.insert(header_name.clone(), case_header_name);
696    }
697    value_map.insert(header_name, value);
698    Ok(())
699}
700
701// the &N here is to avoid clone(). None Copy type like String can impl AsHeaderName
702#[inline]
703fn remove_header<'a, T, N: ?Sized>(
704    name_map: Option<&mut CaseMap>,
705    value_map: &mut HMap<T>,
706    name: &'a N,
707) -> Option<T>
708where
709    &'a N: 'a + AsHeaderName,
710{
711    let removed = value_map.remove(name);
712    if removed.is_some() {
713        if let Some(name_map) = name_map {
714            name_map.remove(name);
715        }
716    }
717    removed
718}
719
720#[inline]
721fn header_to_h1_wire(key_map: Option<&CaseMap>, value_map: &HMap, buf: &mut impl BufMut) {
722    const CRLF: &[u8; 2] = b"\r\n";
723    const HEADER_KV_DELIMITER: &[u8; 2] = b": ";
724
725    if let Some(key_map) = key_map {
726        case_header_iter(key_map.into(), value_map).for_each(|(case_header, val)| {
727            buf.put_slice(case_header.as_slice());
728            buf.put_slice(HEADER_KV_DELIMITER);
729            buf.put_slice(val.as_ref());
730            buf.put_slice(CRLF);
731        });
732    } else {
733        for (header, value) in value_map {
734            let titled_header =
735                case_header_name::titled_header_name_str(header).unwrap_or(header.as_str());
736            buf.put_slice(titled_header.as_bytes());
737            buf.put_slice(HEADER_KV_DELIMITER);
738            buf.put_slice(value.as_ref());
739            buf.put_slice(CRLF);
740        }
741    }
742}
743
744#[inline]
745fn case_header_iter<'a>(
746    name_map: Option<&'a CaseMap>,
747    value_map: &'a HMap,
748) -> impl Iterator<Item = (&'a CaseHeaderName, &'a HeaderValue)> + 'a {
749    name_map.into_iter().flat_map(|name_map| {
750        name_map
751            .iter()
752            .zip(value_map.iter())
753            .map(|((h1, name), (h2, value))| {
754                // in case the header iteration order changes in future versions of HMap
755                assert_eq!(h1, h2, "header iter mismatch {}, {}", h1, h2);
756                (name, value)
757            })
758    })
759}
760
761#[cfg(test)]
762mod tests {
763    use super::*;
764
765    #[test]
766    fn header_map_upper_bound() {
767        assert_eq!(8, http_header_map_upper_bound(None));
768        assert_eq!(16, http_header_map_upper_bound(Some(16)));
769        assert_eq!(4096, http_header_map_upper_bound(Some(7777)));
770    }
771
772    #[test]
773    fn test_single_header() {
774        let mut req = RequestHeader::build("GET", b"\\", None).unwrap();
775        req.insert_header("foo", "bar").unwrap();
776        req.insert_header("FoO", "Bar").unwrap();
777        let mut buf: Vec<u8> = vec![];
778        req.header_to_h1_wire(&mut buf);
779        assert_eq!(buf, b"FoO: Bar\r\n");
780        req.case_header_iter().enumerate().for_each(|(i, (k, v))| {
781            let name = String::from_utf8_lossy(k.as_slice()).into_owned();
782            let value = String::from_utf8_lossy(v.as_ref()).into_owned();
783            match i + 1 {
784                1 => {
785                    assert_eq!(name, "FoO");
786                    assert_eq!(value, "Bar");
787                }
788                _ => panic!("too many headers"),
789            }
790        });
791
792        let mut resp = ResponseHeader::new(None);
793        resp.insert_header("foo", "bar").unwrap();
794        resp.insert_header("FoO", "Bar").unwrap();
795        let mut buf: Vec<u8> = vec![];
796        resp.header_to_h1_wire(&mut buf);
797        assert_eq!(buf, b"FoO: Bar\r\n");
798        resp.case_header_iter().enumerate().for_each(|(i, (k, v))| {
799            let name = String::from_utf8_lossy(k.as_slice()).into_owned();
800            let value = String::from_utf8_lossy(v.as_ref()).into_owned();
801            match i + 1 {
802                1 => {
803                    assert_eq!(name, "FoO");
804                    assert_eq!(value, "Bar");
805                }
806                _ => panic!("too many headers"),
807            }
808        });
809    }
810
811    #[test]
812    fn test_single_header_no_case() {
813        let mut req = RequestHeader::new_no_case(None);
814        req.insert_header("foo", "bar").unwrap();
815        req.insert_header("FoO", "Bar").unwrap();
816        let mut buf: Vec<u8> = vec![];
817        req.header_to_h1_wire(&mut buf);
818        assert_eq!(buf, b"foo: Bar\r\n");
819        req.case_header_iter().for_each(|(_, _)| {
820            unreachable!("request has no case");
821        });
822
823        let mut resp = ResponseHeader::new_no_case(None);
824        resp.insert_header("foo", "bar").unwrap();
825        resp.insert_header("FoO", "Bar").unwrap();
826        let mut buf: Vec<u8> = vec![];
827        resp.header_to_h1_wire(&mut buf);
828        assert_eq!(buf, b"foo: Bar\r\n");
829        resp.case_header_iter().for_each(|(_, _)| {
830            unreachable!("response has no case");
831        });
832    }
833
834    #[test]
835    fn test_multiple_header() {
836        let mut req = RequestHeader::build("GET", b"\\", None).unwrap();
837        req.append_header("FoO", "Bar").unwrap();
838        req.append_header("fOO", "bar").unwrap();
839        req.append_header("BAZ", "baR").unwrap();
840        req.append_header(http::header::CONTENT_LENGTH, "0")
841            .unwrap();
842        req.append_header("a", "b").unwrap();
843        req.remove_header("a");
844        let mut buf: Vec<u8> = vec![];
845        req.header_to_h1_wire(&mut buf);
846        assert_eq!(
847            buf,
848            b"FoO: Bar\r\nfOO: bar\r\nBAZ: baR\r\nContent-Length: 0\r\n"
849        );
850        req.case_header_iter().enumerate().for_each(|(i, (k, v))| {
851            let name = String::from_utf8_lossy(k.as_slice()).into_owned();
852            let value = String::from_utf8_lossy(v.as_ref()).into_owned();
853            match i + 1 {
854                1 => {
855                    assert_eq!(name, "FoO");
856                    assert_eq!(value, "Bar");
857                }
858                2 => {
859                    assert_eq!(name, "fOO");
860                    assert_eq!(value, "bar");
861                }
862                3 => {
863                    assert_eq!(name, "BAZ");
864                    assert_eq!(value, "baR");
865                }
866                4 => {
867                    assert_eq!(name, "Content-Length");
868                    assert_eq!(value, "0");
869                }
870                _ => panic!("too many headers"),
871            }
872        });
873
874        let mut resp = ResponseHeader::new(None);
875        resp.append_header("FoO", "Bar").unwrap();
876        resp.append_header("fOO", "bar").unwrap();
877        resp.append_header("BAZ", "baR").unwrap();
878        resp.append_header(http::header::CONTENT_LENGTH, "0")
879            .unwrap();
880        resp.append_header("a", "b").unwrap();
881        resp.remove_header("a");
882        let mut buf: Vec<u8> = vec![];
883        resp.header_to_h1_wire(&mut buf);
884        assert_eq!(
885            buf,
886            b"FoO: Bar\r\nfOO: bar\r\nBAZ: baR\r\nContent-Length: 0\r\n"
887        );
888        resp.case_header_iter().enumerate().for_each(|(i, (k, v))| {
889            let name = String::from_utf8_lossy(k.as_slice()).into_owned();
890            let value = String::from_utf8_lossy(v.as_ref()).into_owned();
891            match i + 1 {
892                1 => {
893                    assert_eq!(name, "FoO");
894                    assert_eq!(value, "Bar");
895                }
896                2 => {
897                    assert_eq!(name, "fOO");
898                    assert_eq!(value, "bar");
899                }
900                3 => {
901                    assert_eq!(name, "BAZ");
902                    assert_eq!(value, "baR");
903                }
904                4 => {
905                    assert_eq!(name, "Content-Length");
906                    assert_eq!(value, "0");
907                }
908                _ => panic!("too many headers"),
909            }
910        });
911    }
912
913    #[cfg(feature = "patched_http1")]
914    #[test]
915    fn test_invalid_path() {
916        let raw_path = b"Hello\xF0\x90\x80World";
917        let req = RequestHeader::build("GET", &raw_path[..], None).unwrap();
918        assert_eq!("Hello�World", req.uri.path_and_query().unwrap());
919        assert_eq!(raw_path, req.raw_path());
920    }
921
922    #[cfg(feature = "patched_http1")]
923    #[test]
924    fn test_override_invalid_path() {
925        let raw_path = b"Hello\xF0\x90\x80World";
926        let mut req = RequestHeader::build("GET", &raw_path[..], None).unwrap();
927        assert_eq!("Hello�World", req.uri.path_and_query().unwrap());
928        assert_eq!(raw_path, req.raw_path());
929
930        let new_path = "/HelloWorld";
931        req.set_uri(Uri::builder().path_and_query(new_path).build().unwrap());
932        assert_eq!(new_path, req.uri.path_and_query().unwrap());
933        assert_eq!(new_path.as_bytes(), req.raw_path());
934    }
935
936    #[test]
937    fn test_reason_phrase() {
938        let mut resp = ResponseHeader::new(None);
939        let reason = resp.get_reason_phrase().unwrap();
940        assert_eq!(reason, "OK");
941
942        resp.set_reason_phrase(Some("FooBar")).unwrap();
943        let reason = resp.get_reason_phrase().unwrap();
944        assert_eq!(reason, "FooBar");
945
946        resp.set_reason_phrase(Some("OK")).unwrap();
947        let reason = resp.get_reason_phrase().unwrap();
948        assert_eq!(reason, "OK");
949
950        resp.set_reason_phrase(None).unwrap();
951        let reason = resp.get_reason_phrase().unwrap();
952        assert_eq!(reason, "OK");
953    }
954
955    #[test]
956    fn set_test_send_end_stream() {
957        let mut req = RequestHeader::build("GET", b"/", None).unwrap();
958        req.set_send_end_stream(true);
959
960        // None for requests that are not h2
961        assert!(req.send_end_stream().is_none());
962
963        let mut req = RequestHeader::build("GET", b"/", None).unwrap();
964        req.set_version(Version::HTTP_2);
965
966        // Some(true) by default for h2
967        assert!(req.send_end_stream().unwrap());
968
969        req.set_send_end_stream(false);
970        // Some(false)
971        assert!(!req.send_end_stream().unwrap());
972    }
973
974    #[test]
975    fn set_test_set_content_length() {
976        let mut resp = ResponseHeader::new(None);
977        resp.set_content_length(10).unwrap();
978
979        assert_eq!(
980            b"10",
981            resp.headers
982                .get(http::header::CONTENT_LENGTH)
983                .map(|d| d.as_bytes())
984                .unwrap()
985        );
986    }
987}