Skip to content

Commit 39c33ee

Browse files
committed
feat(http1): decouple preserving header case from FFI (fixes hyperium#2313)
1 parent 51ed71b commit 39c33ee

File tree

9 files changed

+71
-86
lines changed

9 files changed

+71
-86
lines changed

src/client/client.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -964,14 +964,27 @@ impl Builder {
964964
/// Set whether HTTP/1 connections will write header names as title case at
965965
/// the socket level.
966966
///
967-
/// Note that this setting does not affect HTTP/2.
967+
/// Note that this setting does not affect HTTP/2 and supersedes the
968+
/// `http1_preserve_header_case` setting.
968969
///
969970
/// Default is false.
970971
pub fn http1_title_case_headers(&mut self, val: bool) -> &mut Self {
971972
self.conn_builder.h1_title_case_headers(val);
972973
self
973974
}
974975

976+
/// Set whether HTTP/1 connections will write header names as provided
977+
/// at the socket level.
978+
///
979+
/// Note that this setting does not affect HTTP/2 and is superseded
980+
/// by the `http1_title_case_headers` setting.
981+
///
982+
/// Default is false.
983+
pub fn http1_preserve_header_case(&mut self, val: bool) -> &mut Self {
984+
self.conn_builder.h1_preserve_header_case(val);
985+
self
986+
}
987+
975988
/// Set whether the connection **must** use HTTP/2.
976989
///
977990
/// The destination must either allow HTTP2 Prior Knowledge, or the

src/client/conn.rs

+10
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ where
123123
pub struct Builder {
124124
pub(super) exec: Exec,
125125
h1_title_case_headers: bool,
126+
h1_preserve_header_case: bool,
126127
h1_read_buf_exact_size: Option<usize>,
127128
h1_max_buf_size: Option<usize>,
128129
#[cfg(feature = "http2")]
@@ -495,6 +496,7 @@ impl Builder {
495496
exec: Exec::Default,
496497
h1_read_buf_exact_size: None,
497498
h1_title_case_headers: false,
499+
h1_preserve_header_case: false,
498500
h1_max_buf_size: None,
499501
#[cfg(feature = "http2")]
500502
h2_builder: Default::default(),
@@ -519,6 +521,11 @@ impl Builder {
519521
self
520522
}
521523

524+
pub(crate) fn h1_preserve_header_case(&mut self, enabled: bool) -> &mut Builder {
525+
self.h1_preserve_header_case = enabled;
526+
self
527+
}
528+
522529
pub(super) fn h1_read_buf_exact_size(&mut self, sz: Option<usize>) -> &mut Builder {
523530
self.h1_read_buf_exact_size = sz;
524531
self.h1_max_buf_size = None;
@@ -700,6 +707,9 @@ impl Builder {
700707
if opts.h1_title_case_headers {
701708
conn.set_title_case_headers();
702709
}
710+
if opts.h1_preserve_header_case {
711+
conn.set_preserve_header_case();
712+
}
703713
if let Some(sz) = opts.h1_read_buf_exact_size {
704714
conn.set_read_buf_exact_size(sz);
705715
}

src/ffi/client.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,11 @@ unsafe impl AsTaskType for hyper_clientconn {
106106
ffi_fn! {
107107
/// Creates a new set of HTTP clientconn options to be used in a handshake.
108108
fn hyper_clientconn_options_new() -> *mut hyper_clientconn_options {
109+
let mut builder = conn::Builder::new();
110+
builder.h1_preserve_header_case(true);
111+
109112
Box::into_raw(Box::new(hyper_clientconn_options {
110-
builder: conn::Builder::new(),
113+
builder,
111114
exec: WeakExec::new(),
112115
}))
113116
}

src/ffi/http_types.rs

+1-23
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use super::error::hyper_code;
77
use super::task::{hyper_task_return_type, AsTaskType};
88
use super::HYPER_ITER_CONTINUE;
99
use crate::header::{HeaderName, HeaderValue};
10+
use crate::proto::h1::HeaderCaseMap;
1011
use crate::{Body, HeaderMap, Method, Request, Response, Uri};
1112

1213
/// An HTTP request.
@@ -24,10 +25,6 @@ pub struct hyper_headers {
2425
orig_casing: HeaderCaseMap,
2526
}
2627

27-
// Will probably be moved to `hyper::ext::http1`
28-
#[derive(Debug, Default)]
29-
pub(crate) struct HeaderCaseMap(HeaderMap<Bytes>);
30-
3128
#[derive(Debug)]
3229
pub(crate) struct ReasonPhrase(pub(crate) Bytes);
3330

@@ -370,25 +367,6 @@ unsafe fn raw_name_value(
370367
Ok((name, value, orig_name))
371368
}
372369

373-
// ===== impl HeaderCaseMap =====
374-
375-
impl HeaderCaseMap {
376-
pub(crate) fn get_all(&self, name: &HeaderName) -> http::header::GetAll<'_, Bytes> {
377-
self.0.get_all(name)
378-
}
379-
380-
pub(crate) fn insert(&mut self, name: HeaderName, orig: Bytes) {
381-
self.0.insert(name, orig);
382-
}
383-
384-
pub(crate) fn append<N>(&mut self, name: N, orig: Bytes)
385-
where
386-
N: http::header::IntoHeaderName,
387-
{
388-
self.0.append(name, orig);
389-
}
390-
}
391-
392370
#[cfg(test)]
393371
mod tests {
394372
use super::*;

src/ffi/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ pub use self::io::*;
6262
pub use self::task::*;
6363

6464
pub(crate) use self::body::UserBody;
65-
pub(crate) use self::http_types::{HeaderCaseMap, ReasonPhrase};
65+
pub(crate) use self::http_types::ReasonPhrase;
6666

6767
/// Return in iter functions to continue iterating.
6868
pub const HYPER_ITER_CONTINUE: libc::c_int = 0;

src/proto/h1/conn.rs

+5-13
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ where
4444
error: None,
4545
keep_alive: KA::Busy,
4646
method: None,
47-
#[cfg(feature = "ffi")]
4847
preserve_header_case: false,
4948
title_case_headers: false,
5049
notify_read: false,
@@ -78,6 +77,11 @@ where
7877
self.state.title_case_headers = true;
7978
}
8079

80+
#[cfg(feature = "client")]
81+
pub(crate) fn set_preserve_header_case(&mut self) {
82+
self.state.preserve_header_case = true;
83+
}
84+
8185
#[cfg(feature = "server")]
8286
pub(crate) fn set_allow_half_close(&mut self) {
8387
self.state.allow_half_close = true;
@@ -144,7 +148,6 @@ where
144148
ParseContext {
145149
cached_headers: &mut self.state.cached_headers,
146150
req_method: &mut self.state.method,
147-
#[cfg(feature = "ffi")]
148151
preserve_header_case: self.state.preserve_header_case,
149152
}
150153
)) {
@@ -478,16 +481,6 @@ where
478481

479482
self.enforce_version(&mut head);
480483

481-
// Maybe check if we should preserve header casing on received
482-
// message headers...
483-
#[cfg(feature = "ffi")]
484-
{
485-
if T::is_client() && !self.state.preserve_header_case {
486-
self.state.preserve_header_case =
487-
head.extensions.get::<crate::ffi::HeaderCaseMap>().is_some();
488-
}
489-
}
490-
491484
let buf = self.io.headers_buf();
492485
match super::role::encode_headers::<T>(
493486
Encode {
@@ -750,7 +743,6 @@ struct State {
750743
/// This is used to know things such as if the message can include
751744
/// a body or not.
752745
method: Option<Method>,
753-
#[cfg(feature = "ffi")]
754746
preserve_header_case: bool,
755747
title_case_headers: bool,
756748
/// Set to true when the Dispatcher should poll read operations

src/proto/h1/io.rs

-2
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,6 @@ where
159159
ParseContext {
160160
cached_headers: parse_ctx.cached_headers,
161161
req_method: parse_ctx.req_method,
162-
#[cfg(feature = "ffi")]
163162
preserve_header_case: parse_ctx.preserve_header_case,
164163
},
165164
)? {
@@ -638,7 +637,6 @@ mod tests {
638637
let parse_ctx = ParseContext {
639638
cached_headers: &mut None,
640639
req_method: &mut None,
641-
#[cfg(feature = "ffi")]
642640
preserve_header_case: false,
643641
};
644642
assert!(buffered

src/proto/h1/mod.rs

+23-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use bytes::BytesMut;
1+
use bytes::{Bytes, BytesMut};
2+
use http::header::{GetAll, HeaderName, IntoHeaderName};
23
use http::{HeaderMap, Method};
34

45
use crate::body::DecodedLength;
@@ -70,7 +71,6 @@ pub(crate) struct ParsedMessage<T> {
7071
pub(crate) struct ParseContext<'a> {
7172
cached_headers: &'a mut Option<HeaderMap>,
7273
req_method: &'a mut Option<Method>,
73-
#[cfg(feature = "ffi")]
7474
preserve_header_case: bool,
7575
}
7676

@@ -102,3 +102,24 @@ impl Wants {
102102
(self.0 & other.0) == other.0
103103
}
104104
}
105+
106+
#[derive(Debug, Default)]
107+
pub(crate) struct HeaderCaseMap(HeaderMap<Bytes>);
108+
109+
impl HeaderCaseMap {
110+
pub(crate) fn get_all(&self, name: &HeaderName) -> GetAll<'_, Bytes> {
111+
self.0.get_all(name)
112+
}
113+
114+
#[cfg(any(test, feature = "ffi"))]
115+
pub(crate) fn insert(&mut self, name: HeaderName, orig: Bytes) {
116+
self.0.insert(name, orig);
117+
}
118+
119+
pub(crate) fn append<N>(&mut self, name: N, orig: Bytes)
120+
where
121+
N: IntoHeaderName,
122+
{
123+
self.0.append(name, orig);
124+
}
125+
}

0 commit comments

Comments
 (0)