Skip to content

Commit 78de891

Browse files
authoredApr 23, 2022
feature(ffi): add connection option to preserve header order (#2798)
Libcurl expects that headers are iterated in the same order that they are recieved. Previously this caused curl tests 580 and 581 to fail. This necessitated exposing a way to preserve the original ordering of http headers. SUMMARY OF CHANGES: Add a new data structure called OriginalHeaderOrder that represents the order in which headers originally appear in a HTTP message. This datastructure is `Vec<(Headername, multimap-index)>`. This vector is ordered by the order which headers were recieved. Add the following ffi functions: - ffi::client::hyper_clientconn_options_set_preserve_header_order : An ffi interface to configure a connection to preserve header order. - ffi::client::hyper_clientconn_options_set_preserve_header_case : An ffi interface to configure a connection to preserve header case. - Add a new option to ParseContext, and Conn::State called `preserve_header_order`. This option, and all the code paths it creates are behind the `ffi` feature flag. This should not change performance of response parsing for non-ffi users. Closes #2780 BREAKING CHANGE: hyper_clientconn_options_new no longer sets the http1_preserve_header_case connection option by default. Users should now call hyper_clientconn_options_set_preserve_header_case if they desire that functionality.
1 parent e1138d7 commit 78de891

File tree

9 files changed

+381
-30
lines changed

9 files changed

+381
-30
lines changed
 

‎capi/include/hyper.h

+16
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,22 @@ void hyper_clientconn_free(struct hyper_clientconn *conn);
355355
*/
356356
struct hyper_clientconn_options *hyper_clientconn_options_new(void);
357357

358+
/*
359+
Set the whether or not header case is preserved.
360+
361+
Pass `0` to allow lowercase normalization (default), `1` to retain original case.
362+
*/
363+
void hyper_clientconn_options_set_preserve_header_case(struct hyper_clientconn_options *opts,
364+
int enabled);
365+
366+
/*
367+
Set the whether or not header order is preserved.
368+
369+
Pass `0` to allow reordering (default), `1` to retain original ordering.
370+
*/
371+
void hyper_clientconn_options_set_preserve_header_order(struct hyper_clientconn_options *opts,
372+
int enabled);
373+
358374
/*
359375
Free a `hyper_clientconn_options *`.
360376
*/

‎src/client/conn.rs

+24
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ pub struct Builder {
156156
h1_writev: Option<bool>,
157157
h1_title_case_headers: bool,
158158
h1_preserve_header_case: bool,
159+
#[cfg(feature = "ffi")]
160+
h1_preserve_header_order: bool,
159161
h1_read_buf_exact_size: Option<usize>,
160162
h1_max_buf_size: Option<usize>,
161163
#[cfg(feature = "ffi")]
@@ -558,6 +560,8 @@ impl Builder {
558560
h1_parser_config: Default::default(),
559561
h1_title_case_headers: false,
560562
h1_preserve_header_case: false,
563+
#[cfg(feature = "ffi")]
564+
h1_preserve_header_order: false,
561565
h1_max_buf_size: None,
562566
#[cfg(feature = "ffi")]
563567
h1_headers_raw: false,
@@ -704,6 +708,21 @@ impl Builder {
704708
self
705709
}
706710

711+
/// Set whether to support preserving original header order.
712+
///
713+
/// Currently, this will record the order in which headers are received, and store this
714+
/// ordering in a private extension on the `Response`. It will also look for and use
715+
/// such an extension in any provided `Request`.
716+
///
717+
/// Note that this setting does not affect HTTP/2.
718+
///
719+
/// Default is false.
720+
#[cfg(feature = "ffi")]
721+
pub fn http1_preserve_header_order(&mut self, enabled: bool) -> &mut Builder {
722+
self.h1_preserve_header_order = enabled;
723+
self
724+
}
725+
707726
/// Sets the exact size of the read buffer to *always* use.
708727
///
709728
/// Note that setting this option unsets the `http1_max_buf_size` option.
@@ -948,9 +967,14 @@ impl Builder {
948967
if opts.h1_title_case_headers {
949968
conn.set_title_case_headers();
950969
}
970+
#[cfg(feature = "ffi")]
Has a conversation. Original line has a conversation.
951971
if opts.h1_preserve_header_case {
952972
conn.set_preserve_header_case();
953973
}
974+
#[cfg(feature = "ffi")]
975+
if opts.h1_preserve_header_order {
976+
conn.set_preserve_header_order();
977+
}
954978
if opts.h09_responses {
955979
conn.set_h09_responses();
956980
}

‎src/ext.rs

+100-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
//! HTTP extensions.
22
33
use bytes::Bytes;
4+
use http::header::HeaderName;
45
#[cfg(feature = "http1")]
5-
use http::header::{HeaderName, IntoHeaderName, ValueIter};
6+
use http::header::{IntoHeaderName, ValueIter};
67
use http::HeaderMap;
8+
#[cfg(feature = "ffi")]
9+
use std::collections::HashMap;
710
#[cfg(feature = "http2")]
811
use std::fmt;
912

@@ -120,3 +123,99 @@ impl HeaderCaseMap {
120123
self.0.append(name, orig);
121124
}
122125
}
126+
127+
#[cfg(feature = "ffi")]
128+
#[derive(Clone, Debug)]
129+
/// Hashmap<Headername, numheaders with that name>
130+
pub(crate) struct OriginalHeaderOrder {
131+
/// Stores how many entries a Headername maps to. This is used
132+
/// for accounting.
133+
num_entries: HashMap<HeaderName, usize>,
134+
/// Stores the ordering of the headers. ex: `vec[i] = (headerName, idx)`,
135+
/// The vector is ordered such that the ith element
136+
/// represents the ith header that came in off the line.
137+
/// The `HeaderName` and `idx` are then used elsewhere to index into
138+
/// the multi map that stores the header values.
139+
entry_order: Vec<(HeaderName, usize)>,
140+
}
141+
142+
#[cfg(all(feature = "http1", feature = "ffi"))]
143+
impl OriginalHeaderOrder {
144+
pub(crate) fn default() -> Self {
145+
OriginalHeaderOrder {
146+
num_entries: HashMap::new(),
147+
entry_order: Vec::new(),
148+
}
149+
}
150+
151+
pub(crate) fn insert(&mut self, name: HeaderName) {
152+
if !self.num_entries.contains_key(&name) {
153+
let idx = 0;
154+
self.num_entries.insert(name.clone(), 1);
155+
self.entry_order.push((name, idx));
156+
}
157+
// Replacing an already existing element does not
158+
// change ordering, so we only care if its the first
159+
// header name encountered
160+
}
161+
162+
pub(crate) fn append<N>(&mut self, name: N)
163+
where
164+
N: IntoHeaderName + Into<HeaderName> + Clone,
165+
{
166+
let name: HeaderName = name.into();
167+
let idx;
168+
if self.num_entries.contains_key(&name) {
169+
idx = self.num_entries[&name];
170+
*self.num_entries.get_mut(&name).unwrap() += 1;
171+
} else {
172+
idx = 0;
173+
self.num_entries.insert(name.clone(), 1);
174+
}
175+
self.entry_order.push((name, idx));
176+
}
177+
178+
// No doc test is run here because `RUSTFLAGS='--cfg hyper_unstable_ffi'`
179+
// is needed to compile. Once ffi is stablized `no_run` should be removed
180+
// here.
181+
/// This returns an iterator that provides header names and indexes
182+
/// in the original order recieved.
183+
///
184+
/// # Examples
185+
/// ```no_run
186+
/// use hyper::ext::OriginalHeaderOrder;
187+
/// use hyper::header::{HeaderName, HeaderValue, HeaderMap};
188+
///
189+
/// let mut h_order = OriginalHeaderOrder::default();
190+
/// let mut h_map = Headermap::new();
191+
///
192+
/// let name1 = b"Set-CookiE";
193+
/// let value1 = b"a=b";
194+
/// h_map.append(name1);
195+
/// h_order.append(name1);
196+
///
197+
/// let name2 = b"Content-Encoding";
198+
/// let value2 = b"gzip";
199+
/// h_map.append(name2, value2);
200+
/// h_order.append(name2);
201+
///
202+
/// let name3 = b"SET-COOKIE";
203+
/// let value3 = b"c=d";
204+
/// h_map.append(name3, value3);
205+
/// h_order.append(name3)
206+
///
207+
/// let mut iter = h_order.get_in_order()
208+
///
209+
/// let (name, idx) = iter.next();
210+
/// assert_eq!(b"a=b", h_map.get_all(name).nth(idx).unwrap());
211+
///
212+
/// let (name, idx) = iter.next();
213+
/// assert_eq!(b"gzip", h_map.get_all(name).nth(idx).unwrap());
214+
///
215+
/// let (name, idx) = iter.next();
216+
/// assert_eq!(b"c=d", h_map.get_all(name).nth(idx).unwrap());
217+
/// ```
218+
pub(crate) fn get_in_order(&self) -> impl Iterator<Item = &(HeaderName, usize)> {
219+
self.entry_order.iter()
220+
}
221+
}

‎src/ffi/client.rs

+21-2
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,7 @@ unsafe impl AsTaskType for hyper_clientconn {
9393
ffi_fn! {
9494
/// Creates a new set of HTTP clientconn options to be used in a handshake.
9595
fn hyper_clientconn_options_new() -> *mut hyper_clientconn_options {
96-
let mut builder = conn::Builder::new();
97-
builder.http1_preserve_header_case(true);
96+
let builder = conn::Builder::new();
9897

9998
Box::into_raw(Box::new(hyper_clientconn_options {
10099
builder,
@@ -103,6 +102,26 @@ ffi_fn! {
103102
} ?= std::ptr::null_mut()
104103
}
105104

105+
ffi_fn! {
106+
/// Set the whether or not header case is preserved.
107+
///
108+
/// Pass `0` to allow lowercase normalization (default), `1` to retain original case.
109+
fn hyper_clientconn_options_set_preserve_header_case(opts: *mut hyper_clientconn_options, enabled: c_int) {
110+
let opts = non_null! { &mut *opts ?= () };
111+
opts.builder.http1_preserve_header_case(enabled != 0);
112+
}
113+
}
114+
115+
ffi_fn! {
116+
/// Set the whether or not header order is preserved.
117+
///
118+
/// Pass `0` to allow reordering (default), `1` to retain original ordering.
119+
fn hyper_clientconn_options_set_preserve_header_order(opts: *mut hyper_clientconn_options, enabled: c_int) {
120+
let opts = non_null! { &mut *opts ?= () };
121+
opts.builder.http1_preserve_header_order(enabled != 0);
122+
}
123+
}
124+
106125
ffi_fn! {
107126
/// Free a `hyper_clientconn_options *`.
108127
fn hyper_clientconn_options_free(opts: *mut hyper_clientconn_options) {

‎src/ffi/http_types.rs

+114-12
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use super::body::{hyper_body, hyper_buf};
66
use super::error::hyper_code;
77
use super::task::{hyper_task_return_type, AsTaskType};
88
use super::{UserDataPointer, HYPER_ITER_CONTINUE};
9-
use crate::ext::HeaderCaseMap;
9+
use crate::ext::{HeaderCaseMap, OriginalHeaderOrder};
1010
use crate::header::{HeaderName, HeaderValue};
1111
use crate::{Body, HeaderMap, Method, Request, Response, Uri};
1212

@@ -22,6 +22,7 @@ pub struct hyper_response(pub(super) Response<Body>);
2222
pub struct hyper_headers {
2323
pub(super) headers: HeaderMap,
2424
orig_casing: HeaderCaseMap,
25+
orig_order: OriginalHeaderOrder,
2526
}
2627

2728
#[derive(Debug)]
@@ -233,6 +234,7 @@ impl hyper_request {
233234
if let Some(headers) = self.0.extensions_mut().remove::<hyper_headers>() {
234235
*self.0.headers_mut() = headers.headers;
235236
self.0.extensions_mut().insert(headers.orig_casing);
237+
self.0.extensions_mut().insert(headers.orig_order);
236238
}
237239
}
238240
}
@@ -348,9 +350,14 @@ impl hyper_response {
348350
.extensions_mut()
349351
.remove::<HeaderCaseMap>()
350352
.unwrap_or_else(HeaderCaseMap::default);
353+
let orig_order = resp
354+
.extensions_mut()
355+
.remove::<OriginalHeaderOrder>()
356+
.unwrap_or_else(OriginalHeaderOrder::default);
351357
resp.extensions_mut().insert(hyper_headers {
352358
headers,
353359
orig_casing,
360+
orig_order,
354361
});
355362

356363
hyper_response(resp)
@@ -404,26 +411,54 @@ ffi_fn! {
404411
// and for each one, try to pair the originally cased name with the value.
405412
//
406413
// TODO: consider adding http::HeaderMap::entries() iterator
407-
for name in headers.headers.keys() {
408-
let mut names = headers.orig_casing.get_all(name);
409-
410-
for value in headers.headers.get_all(name) {
411-
let (name_ptr, name_len) = if let Some(orig_name) = names.next() {
414+
let mut ordered_iter = headers.orig_order.get_in_order().peekable();
415+
if ordered_iter.peek().is_some() {
416+
for (name, idx) in ordered_iter {
417+
let (name_ptr, name_len) = if let Some(orig_name) = headers.orig_casing.get_all(name).nth(*idx) {
412418
(orig_name.as_ref().as_ptr(), orig_name.as_ref().len())
413419
} else {
414420
(
415-
name.as_str().as_bytes().as_ptr(),
416-
name.as_str().as_bytes().len(),
421+
name.as_str().as_bytes().as_ptr(),
422+
name.as_str().as_bytes().len(),
417423
)
418424
};
419425

420-
let val_ptr = value.as_bytes().as_ptr();
421-
let val_len = value.as_bytes().len();
426+
let val_ptr;
427+
let val_len;
428+
if let Some(value) = headers.headers.get_all(name).iter().nth(*idx) {
429+
val_ptr = value.as_bytes().as_ptr();
430+
val_len = value.as_bytes().len();
431+
} else {
432+
// Stop iterating, something has gone wrong.
433+
return;
434+
}
422435

423436
if HYPER_ITER_CONTINUE != func(userdata, name_ptr, name_len, val_ptr, val_len) {
424437
return;
425438
}
426439
}
440+
} else {
441+
for name in headers.headers.keys() {
442+
let mut names = headers.orig_casing.get_all(name);
443+
444+
for value in headers.headers.get_all(name) {
445+
let (name_ptr, name_len) = if let Some(orig_name) = names.next() {
446+
(orig_name.as_ref().as_ptr(), orig_name.as_ref().len())
447+
} else {
448+
(
449+
name.as_str().as_bytes().as_ptr(),
450+
name.as_str().as_bytes().len(),
451+
)
452+
};
453+
454+
let val_ptr = value.as_bytes().as_ptr();
455+
let val_len = value.as_bytes().len();
456+
457+
if HYPER_ITER_CONTINUE != func(userdata, name_ptr, name_len, val_ptr, val_len) {
458+
return;
459+
}
460+
}
461+
}
427462
}
428463
}
429464
}
@@ -437,7 +472,8 @@ ffi_fn! {
437472
match unsafe { raw_name_value(name, name_len, value, value_len) } {
438473
Ok((name, value, orig_name)) => {
439474
headers.headers.insert(&name, value);
440-
headers.orig_casing.insert(name, orig_name);
475+
headers.orig_casing.insert(name.clone(), orig_name.clone());
476+
headers.orig_order.insert(name);
441477
hyper_code::HYPERE_OK
442478
}
443479
Err(code) => code,
@@ -456,7 +492,8 @@ ffi_fn! {
456492
match unsafe { raw_name_value(name, name_len, value, value_len) } {
457493
Ok((name, value, orig_name)) => {
458494
headers.headers.append(&name, value);
459-
headers.orig_casing.append(name, orig_name);
495+
headers.orig_casing.append(&name, orig_name.clone());
496+
headers.orig_order.append(name);
460497
hyper_code::HYPERE_OK
461498
}
462499
Err(code) => code,
@@ -469,6 +506,7 @@ impl Default for hyper_headers {
469506
Self {
470507
headers: Default::default(),
471508
orig_casing: HeaderCaseMap::default(),
509+
orig_order: OriginalHeaderOrder::default(),
472510
}
473511
}
474512
}
@@ -555,4 +593,68 @@ mod tests {
555593
HYPER_ITER_CONTINUE
556594
}
557595
}
596+
597+
#[cfg(all(feature = "http1", feature = "ffi"))]
598+
#[test]
599+
fn test_headers_foreach_order_preserved() {
600+
let mut headers = hyper_headers::default();
601+
602+
let name1 = b"Set-CookiE";
603+
let value1 = b"a=b";
604+
hyper_headers_add(
605+
&mut headers,
606+
name1.as_ptr(),
607+
name1.len(),
608+
value1.as_ptr(),
609+
value1.len(),
610+
);
611+
612+
let name2 = b"Content-Encoding";
613+
let value2 = b"gzip";
614+
hyper_headers_add(
615+
&mut headers,
616+
name2.as_ptr(),
617+
name2.len(),
618+
value2.as_ptr(),
619+
value2.len(),
620+
);
621+
622+
let name3 = b"SET-COOKIE";
623+
let value3 = b"c=d";
624+
hyper_headers_add(
625+
&mut headers,
626+
name3.as_ptr(),
627+
name3.len(),
628+
value3.as_ptr(),
629+
value3.len(),
630+
);
631+
632+
let mut vec = Vec::<u8>::new();
633+
hyper_headers_foreach(&headers, concat, &mut vec as *mut _ as *mut c_void);
634+
635+
println!("{}", std::str::from_utf8(&vec).unwrap());
636+
assert_eq!(
637+
vec,
638+
b"Set-CookiE: a=b\r\nContent-Encoding: gzip\r\nSET-COOKIE: c=d\r\n"
639+
);
640+
641+
extern "C" fn concat(
642+
vec: *mut c_void,
643+
name: *const u8,
644+
name_len: usize,
645+
value: *const u8,
646+
value_len: usize,
647+
) -> c_int {
648+
unsafe {
649+
let vec = &mut *(vec as *mut Vec<u8>);
650+
let name = std::slice::from_raw_parts(name, name_len);
651+
let value = std::slice::from_raw_parts(value, value_len);
652+
vec.extend(name);
653+
vec.extend(b": ");
654+
vec.extend(value);
655+
vec.extend(b"\r\n");
656+
}
657+
HYPER_ITER_CONTINUE
658+
}
659+
}
558660
}

‎src/proto/h1/conn.rs

+11
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ where
5858
#[cfg(all(feature = "server", feature = "runtime"))]
5959
h1_header_read_timeout_running: false,
6060
preserve_header_case: false,
61+
#[cfg(feature = "ffi")]
62+
preserve_header_order: false,
6163
title_case_headers: false,
6264
h09_responses: false,
6365
#[cfg(feature = "ffi")]
@@ -111,6 +113,11 @@ where
111113
self.state.preserve_header_case = true;
112114
}
113115

116+
#[cfg(feature = "ffi")]
117+
pub(crate) fn set_preserve_header_order(&mut self) {
118+
self.state.preserve_header_order = true;
119+
}
120+
114121
#[cfg(feature = "client")]
115122
pub(crate) fn set_h09_responses(&mut self) {
116123
self.state.h09_responses = true;
@@ -200,6 +207,8 @@ where
200207
#[cfg(all(feature = "server", feature = "runtime"))]
201208
h1_header_read_timeout_running: &mut self.state.h1_header_read_timeout_running,
202209
preserve_header_case: self.state.preserve_header_case,
210+
#[cfg(feature = "ffi")]
211+
preserve_header_order: self.state.preserve_header_order,
203212
h09_responses: self.state.h09_responses,
204213
#[cfg(feature = "ffi")]
205214
on_informational: &mut self.state.on_informational,
@@ -824,6 +833,8 @@ struct State {
824833
#[cfg(all(feature = "server", feature = "runtime"))]
825834
h1_header_read_timeout_running: bool,
826835
preserve_header_case: bool,
836+
#[cfg(feature = "ffi")]
837+
preserve_header_order: bool,
827838
title_case_headers: bool,
828839
h09_responses: bool,
829840
/// If set, called with each 1xx informational response received for

‎src/proto/h1/io.rs

+20-12
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
use std::cmp;
22
use std::fmt;
3+
#[cfg(all(feature = "server", feature = "runtime"))]
4+
use std::future::Future;
35
use std::io::{self, IoSlice};
46
use std::marker::Unpin;
57
use std::mem::MaybeUninit;
68
#[cfg(all(feature = "server", feature = "runtime"))]
7-
use std::future::Future;
8-
#[cfg(all(feature = "server", feature = "runtime"))]
99
use std::time::Duration;
1010

11-
#[cfg(all(feature = "server", feature = "runtime"))]
12-
use tokio::time::Instant;
1311
use bytes::{Buf, BufMut, Bytes, BytesMut};
1412
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
13+
#[cfg(all(feature = "server", feature = "runtime"))]
14+
use tokio::time::Instant;
1515
use tracing::{debug, trace};
1616

1717
use super::{Http1Transaction, ParseContext, ParsedMessage};
@@ -194,6 +194,8 @@ where
194194
#[cfg(all(feature = "server", feature = "runtime"))]
195195
h1_header_read_timeout_running: parse_ctx.h1_header_read_timeout_running,
196196
preserve_header_case: parse_ctx.preserve_header_case,
197+
#[cfg(feature = "ffi")]
198+
preserve_header_order: parse_ctx.preserve_header_order,
197199
h09_responses: parse_ctx.h09_responses,
198200
#[cfg(feature = "ffi")]
199201
on_informational: parse_ctx.on_informational,
@@ -208,9 +210,13 @@ where
208210
{
209211
*parse_ctx.h1_header_read_timeout_running = false;
210212

211-
if let Some(h1_header_read_timeout_fut) = parse_ctx.h1_header_read_timeout_fut {
213+
if let Some(h1_header_read_timeout_fut) =
214+
parse_ctx.h1_header_read_timeout_fut
215+
{
212216
// Reset the timer in order to avoid woken up when the timeout finishes
213-
h1_header_read_timeout_fut.as_mut().reset(Instant::now() + Duration::from_secs(30 * 24 * 60 * 60));
217+
h1_header_read_timeout_fut
218+
.as_mut()
219+
.reset(Instant::now() + Duration::from_secs(30 * 24 * 60 * 60));
214220
}
215221
}
216222
return Poll::Ready(Ok(msg));
@@ -224,12 +230,14 @@ where
224230

225231
#[cfg(all(feature = "server", feature = "runtime"))]
226232
if *parse_ctx.h1_header_read_timeout_running {
227-
if let Some(h1_header_read_timeout_fut) = parse_ctx.h1_header_read_timeout_fut {
228-
if Pin::new( h1_header_read_timeout_fut).poll(cx).is_ready() {
233+
if let Some(h1_header_read_timeout_fut) =
234+
parse_ctx.h1_header_read_timeout_fut
235+
{
236+
if Pin::new(h1_header_read_timeout_fut).poll(cx).is_ready() {
229237
*parse_ctx.h1_header_read_timeout_running = false;
230238

231239
tracing::warn!("read header from client timeout");
232-
return Poll::Ready(Err(crate::Error::new_header_timeout()))
240+
return Poll::Ready(Err(crate::Error::new_header_timeout()));
233241
}
234242
}
235243
}
@@ -734,6 +742,8 @@ mod tests {
734742
#[cfg(feature = "runtime")]
735743
h1_header_read_timeout_running: &mut false,
736744
preserve_header_case: false,
745+
#[cfg(feature = "ffi")]
746+
preserve_header_order: false,
737747
h09_responses: false,
738748
#[cfg(feature = "ffi")]
739749
on_informational: &mut None,
@@ -897,9 +907,7 @@ mod tests {
897907
async fn write_buf_flatten() {
898908
let _ = pretty_env_logger::try_init();
899909

900-
let mock = Mock::new()
901-
.write(b"hello world, it's hyper!")
902-
.build();
910+
let mock = Mock::new().write(b"hello world, it's hyper!").build();
903911

904912
let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
905913
buffered.write_buf.set_strategy(WriteStrategy::Flatten);

‎src/proto/h1/mod.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub(crate) use self::conn::Conn;
1414
pub(crate) use self::decode::Decoder;
1515
pub(crate) use self::dispatch::Dispatcher;
1616
pub(crate) use self::encode::{EncodedBuf, Encoder};
17-
//TODO: move out of h1::io
17+
//TODO: move out of h1::io
1818
pub(crate) use self::io::MINIMUM_MAX_BUFFER_SIZE;
1919

2020
mod conn;
@@ -24,7 +24,6 @@ mod encode;
2424
mod io;
2525
mod role;
2626

27-
2827
cfg_client! {
2928
pub(crate) type ClientTransaction = role::Client;
3029
}
@@ -84,6 +83,8 @@ pub(crate) struct ParseContext<'a> {
8483
#[cfg(all(feature = "server", feature = "runtime"))]
8584
h1_header_read_timeout_running: &'a mut bool,
8685
preserve_header_case: bool,
86+
#[cfg(feature = "ffi")]
87+
preserve_header_order: bool,
8788
h09_responses: bool,
8889
#[cfg(feature = "ffi")]
8990
on_informational: &'a mut Option<crate::ffi::OnInformational>,

‎src/proto/h1/role.rs

+72-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ use crate::body::DecodedLength;
1717
use crate::common::date;
1818
use crate::error::Parse;
1919
use crate::ext::HeaderCaseMap;
20+
#[cfg(feature = "ffi")]
21+
use crate::ext::OriginalHeaderOrder;
2022
use crate::headers;
2123
use crate::proto::h1::{
2224
Encode, Encoder, Http1Transaction, ParseContext, ParseResult, ParsedMessage,
@@ -214,6 +216,13 @@ impl Http1Transaction for Server {
214216
None
215217
};
216218

219+
#[cfg(feature = "ffi")]
220+
let mut header_order = if ctx.preserve_header_order {
221+
Some(OriginalHeaderOrder::default())
222+
} else {
223+
None
224+
};
225+
217226
let mut headers = ctx.cached_headers.take().unwrap_or_else(HeaderMap::new);
218227

219228
headers.reserve(headers_len);
@@ -290,6 +299,11 @@ impl Http1Transaction for Server {
290299
header_case_map.append(&name, slice.slice(header.name.0..header.name.1));
291300
}
292301

302+
#[cfg(feature = "ffi")]
303+
if let Some(ref mut header_order) = header_order {
304+
header_order.append(&name);
305+
}
306+
293307
headers.append(name, value);
294308
}
295309

@@ -304,6 +318,11 @@ impl Http1Transaction for Server {
304318
extensions.insert(header_case_map);
305319
}
306320

321+
#[cfg(feature = "ffi")]
322+
if let Some(header_order) = header_order {
323+
extensions.insert(header_order);
324+
}
325+
307326
*ctx.req_method = Some(subject.0.clone());
308327

309328
Ok(Some(ParsedMessage {
@@ -957,7 +976,10 @@ impl Http1Transaction for Client {
957976

958977
let mut slice = buf.split_to(len);
959978

960-
if ctx.h1_parser_config.obsolete_multiline_headers_in_responses_are_allowed() {
979+
if ctx
980+
.h1_parser_config
981+
.obsolete_multiline_headers_in_responses_are_allowed()
982+
{
961983
for header in &headers_indices[..headers_len] {
962984
// SAFETY: array is valid up to `headers_len`
963985
let header = unsafe { &*header.as_ptr() };
@@ -981,6 +1003,13 @@ impl Http1Transaction for Client {
9811003
None
9821004
};
9831005

1006+
#[cfg(feature = "ffi")]
1007+
let mut header_order = if ctx.preserve_header_order {
1008+
Some(OriginalHeaderOrder::default())
1009+
} else {
1010+
None
1011+
};
1012+
9841013
headers.reserve(headers_len);
9851014
for header in &headers_indices[..headers_len] {
9861015
// SAFETY: array is valid up to `headers_len`
@@ -1003,6 +1032,11 @@ impl Http1Transaction for Client {
10031032
header_case_map.append(&name, slice.slice(header.name.0..header.name.1));
10041033
}
10051034

1035+
#[cfg(feature = "ffi")]
1036+
if let Some(ref mut header_order) = header_order {
1037+
header_order.append(&name);
1038+
}
1039+
10061040
headers.append(name, value);
10071041
}
10081042

@@ -1012,6 +1046,11 @@ impl Http1Transaction for Client {
10121046
extensions.insert(header_case_map);
10131047
}
10141048

1049+
#[cfg(feature = "ffi")]
1050+
if let Some(header_order) = header_order {
1051+
extensions.insert(header_order);
1052+
}
1053+
10151054
#[cfg(feature = "ffi")]
10161055
if let Some(reason) = reason {
10171056
extensions.insert(crate::ffi::ReasonPhrase(reason));
@@ -1481,6 +1520,8 @@ mod tests {
14811520
#[cfg(feature = "runtime")]
14821521
h1_header_read_timeout_running: &mut false,
14831522
preserve_header_case: false,
1523+
#[cfg(feature = "ffi")]
1524+
preserve_header_order: false,
14841525
h09_responses: false,
14851526
#[cfg(feature = "ffi")]
14861527
on_informational: &mut None,
@@ -1514,6 +1555,8 @@ mod tests {
15141555
#[cfg(feature = "runtime")]
15151556
h1_header_read_timeout_running: &mut false,
15161557
preserve_header_case: false,
1558+
#[cfg(feature = "ffi")]
1559+
preserve_header_order: false,
15171560
h09_responses: false,
15181561
#[cfg(feature = "ffi")]
15191562
on_informational: &mut None,
@@ -1542,6 +1585,8 @@ mod tests {
15421585
#[cfg(feature = "runtime")]
15431586
h1_header_read_timeout_running: &mut false,
15441587
preserve_header_case: false,
1588+
#[cfg(feature = "ffi")]
1589+
preserve_header_order: false,
15451590
h09_responses: false,
15461591
#[cfg(feature = "ffi")]
15471592
on_informational: &mut None,
@@ -1568,6 +1613,8 @@ mod tests {
15681613
#[cfg(feature = "runtime")]
15691614
h1_header_read_timeout_running: &mut false,
15701615
preserve_header_case: false,
1616+
#[cfg(feature = "ffi")]
1617+
preserve_header_order: false,
15711618
h09_responses: true,
15721619
#[cfg(feature = "ffi")]
15731620
on_informational: &mut None,
@@ -1596,6 +1643,8 @@ mod tests {
15961643
#[cfg(feature = "runtime")]
15971644
h1_header_read_timeout_running: &mut false,
15981645
preserve_header_case: false,
1646+
#[cfg(feature = "ffi")]
1647+
preserve_header_order: false,
15991648
h09_responses: false,
16001649
#[cfg(feature = "ffi")]
16011650
on_informational: &mut None,
@@ -1628,6 +1677,8 @@ mod tests {
16281677
#[cfg(feature = "runtime")]
16291678
h1_header_read_timeout_running: &mut false,
16301679
preserve_header_case: false,
1680+
#[cfg(feature = "ffi")]
1681+
preserve_header_order: false,
16311682
h09_responses: false,
16321683
#[cfg(feature = "ffi")]
16331684
on_informational: &mut None,
@@ -1657,6 +1708,8 @@ mod tests {
16571708
#[cfg(feature = "runtime")]
16581709
h1_header_read_timeout_running: &mut false,
16591710
preserve_header_case: false,
1711+
#[cfg(feature = "ffi")]
1712+
preserve_header_order: false,
16601713
h09_responses: false,
16611714
#[cfg(feature = "ffi")]
16621715
on_informational: &mut None,
@@ -1681,6 +1734,8 @@ mod tests {
16811734
#[cfg(feature = "runtime")]
16821735
h1_header_read_timeout_running: &mut false,
16831736
preserve_header_case: true,
1737+
#[cfg(feature = "ffi")]
1738+
preserve_header_order: false,
16841739
h09_responses: false,
16851740
#[cfg(feature = "ffi")]
16861741
on_informational: &mut None,
@@ -1726,6 +1781,8 @@ mod tests {
17261781
#[cfg(feature = "runtime")]
17271782
h1_header_read_timeout_running: &mut false,
17281783
preserve_header_case: false,
1784+
#[cfg(feature = "ffi")]
1785+
preserve_header_order: false,
17291786
h09_responses: false,
17301787
#[cfg(feature = "ffi")]
17311788
on_informational: &mut None,
@@ -1752,6 +1809,8 @@ mod tests {
17521809
#[cfg(feature = "runtime")]
17531810
h1_header_read_timeout_running: &mut false,
17541811
preserve_header_case: false,
1812+
#[cfg(feature = "ffi")]
1813+
preserve_header_order: false,
17551814
h09_responses: false,
17561815
#[cfg(feature = "ffi")]
17571816
on_informational: &mut None,
@@ -1987,6 +2046,8 @@ mod tests {
19872046
#[cfg(feature = "runtime")]
19882047
h1_header_read_timeout_running: &mut false,
19892048
preserve_header_case: false,
2049+
#[cfg(feature = "ffi")]
2050+
preserve_header_order: false,
19902051
h09_responses: false,
19912052
#[cfg(feature = "ffi")]
19922053
on_informational: &mut None,
@@ -2013,6 +2074,8 @@ mod tests {
20132074
#[cfg(feature = "runtime")]
20142075
h1_header_read_timeout_running: &mut false,
20152076
preserve_header_case: false,
2077+
#[cfg(feature = "ffi")]
2078+
preserve_header_order: false,
20162079
h09_responses: false,
20172080
#[cfg(feature = "ffi")]
20182081
on_informational: &mut None,
@@ -2039,6 +2102,8 @@ mod tests {
20392102
#[cfg(feature = "runtime")]
20402103
h1_header_read_timeout_running: &mut false,
20412104
preserve_header_case: false,
2105+
#[cfg(feature = "ffi")]
2106+
preserve_header_order: false,
20422107
h09_responses: false,
20432108
#[cfg(feature = "ffi")]
20442109
on_informational: &mut None,
@@ -2542,6 +2607,8 @@ mod tests {
25422607
#[cfg(feature = "runtime")]
25432608
h1_header_read_timeout_running: &mut false,
25442609
preserve_header_case: false,
2610+
#[cfg(feature = "ffi")]
2611+
preserve_header_order: false,
25452612
h09_responses: false,
25462613
#[cfg(feature = "ffi")]
25472614
on_informational: &mut None,
@@ -2632,6 +2699,8 @@ mod tests {
26322699
#[cfg(feature = "runtime")]
26332700
h1_header_read_timeout_running: &mut false,
26342701
preserve_header_case: false,
2702+
#[cfg(feature = "ffi")]
2703+
preserve_header_order: false,
26352704
h09_responses: false,
26362705
#[cfg(feature = "ffi")]
26372706
on_informational: &mut None,
@@ -2678,6 +2747,8 @@ mod tests {
26782747
#[cfg(feature = "runtime")]
26792748
h1_header_read_timeout_running: &mut false,
26802749
preserve_header_case: false,
2750+
#[cfg(feature = "ffi")]
2751+
preserve_header_order: false,
26812752
h09_responses: false,
26822753
#[cfg(feature = "ffi")]
26832754
on_informational: &mut None,

0 commit comments

Comments
 (0)
Please sign in to comment.