fix(http2): add Date header if not present for HTTP2 server responses

This commit is contained in:
Sean McArthur
2018-10-08 17:52:08 -07:00
parent 3e12bccac0
commit 37ec724fd6
5 changed files with 149 additions and 36 deletions

View File

@@ -2,6 +2,7 @@ use std::cell::RefCell;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use std::str; use std::str;
use http::header::HeaderValue;
use time::{self, Duration}; use time::{self, Duration};
// "Sun, 06 Nov 1994 08:49:37 GMT".len() // "Sun, 06 Nov 1994 08:49:37 GMT".len()
@@ -19,6 +20,15 @@ pub fn update() {
}) })
} }
pub(crate) fn update_and_header_value() -> HeaderValue {
CACHED.with(|cache| {
let mut cache = cache.borrow_mut();
cache.check();
HeaderValue::from_bytes(cache.buffer())
.expect("Date format should be valid HeaderValue")
})
}
struct CachedDate { struct CachedDate {
bytes: [u8; DATE_VALUE_LENGTH], bytes: [u8; DATE_VALUE_LENGTH],
pos: usize, pos: usize,

View File

@@ -11,7 +11,7 @@ pub use self::io::Cursor; //TODO: move out of h1::io
pub use self::io::MINIMUM_MAX_BUFFER_SIZE; pub use self::io::MINIMUM_MAX_BUFFER_SIZE;
mod conn; mod conn;
mod date; pub(super) mod date;
mod decode; mod decode;
pub(crate) mod dispatch; pub(crate) mod dispatch;
mod encode; mod encode;

View File

@@ -193,6 +193,15 @@ where
let (head, body) = res.into_parts(); let (head, body) = res.into_parts();
let mut res = ::http::Response::from_parts(head, ()); let mut res = ::http::Response::from_parts(head, ());
super::strip_connection_headers(res.headers_mut(), false); super::strip_connection_headers(res.headers_mut(), false);
// set Date header if it isn't already set...
res
.headers_mut()
.entry(::http::header::DATE)
.expect("DATE is a valid HeaderName")
.or_insert_with(::proto::h1::date::update_and_header_value);
// automatically set Content-Length from body...
if let Some(len) = body.content_length() { if let Some(len) = body.content_length() {
headers::set_content_length_if_missing(res.headers_mut(), len); headers::set_content_length_if_missing(res.headers_mut(), len);
} }

View File

@@ -11,6 +11,9 @@ t! {
; ;
response: response:
status: 200, status: 200,
headers: {
"date" => SOME,
},
; ;
server: server:
request: request:
@@ -37,6 +40,27 @@ t! {
; ;
} }
t! {
date_isnt_overwritten,
client:
request:
;
response:
status: 200,
headers: {
"date" => "let me through",
},
;
server:
request:
;
response:
headers: {
"date" => "let me through",
},
;
}
t! { t! {
get_body, get_body,
client: client:

View File

@@ -95,21 +95,21 @@ macro_rules! t {
fn $name() { fn $name() {
let c = vec![$(( let c = vec![$((
__CReq { __CReq {
$($c_req_prop: __internal_req_res_prop!($c_req_prop: $c_req_val),)* $($c_req_prop: __internal_map_prop!($c_req_prop: $c_req_val),)*
..Default::default() ..Default::default()
}, },
__CRes { __CRes {
$($c_res_prop: __internal_req_res_prop!($c_res_prop: $c_res_val),)* $($c_res_prop: __internal_eq_prop!($c_res_prop: $c_res_val),)*
..Default::default() ..Default::default()
} }
),)*]; ),)*];
let s = vec![$(( let s = vec![$((
__SReq { __SReq {
$($s_req_prop: __internal_req_res_prop!($s_req_prop: $s_req_val),)* $($s_req_prop: __internal_eq_prop!($s_req_prop: $s_req_val),)*
..Default::default() ..Default::default()
}, },
__SRes { __SRes {
$($s_res_prop: __internal_req_res_prop!($s_res_prop: $s_res_val),)* $($s_res_prop: __internal_map_prop!($s_res_prop: $s_res_val),)*
..Default::default() ..Default::default()
} }
),)*]; ),)*];
@@ -157,6 +157,34 @@ macro_rules! t {
); );
} }
macro_rules! __internal_map_prop {
(headers: $map:tt) => ({
#[allow(unused_mut)]
{
let mut headers = HeaderMap::new();
__internal_headers_map!(headers, $map);
headers
}
});
($name:tt: $val:tt) => ({
__internal_req_res_prop!($name: $val)
});
}
macro_rules! __internal_eq_prop {
(headers: $map:tt) => ({
#[allow(unused_mut)]
{
let mut headers = Vec::new();
__internal_headers_eq!(headers, $map);
headers
}
});
($name:tt: $val:tt) => ({
__internal_req_res_prop!($name: $val)
});
}
macro_rules! __internal_req_res_prop { macro_rules! __internal_req_res_prop {
(method: $prop_val:expr) => ( (method: $prop_val:expr) => (
$prop_val $prop_val
@@ -164,20 +192,12 @@ macro_rules! __internal_req_res_prop {
(status: $prop_val:expr) => ( (status: $prop_val:expr) => (
StatusCode::from_u16($prop_val).expect("status code") StatusCode::from_u16($prop_val).expect("status code")
); );
(headers: $map:tt) => ({
#[allow(unused_mut)]
{
let mut headers = HeaderMap::new();
__internal_headers!(headers, $map);
headers
}
});
($prop_name:ident: $prop_val:expr) => ( ($prop_name:ident: $prop_val:expr) => (
From::from($prop_val) From::from($prop_val)
) )
} }
macro_rules! __internal_headers { macro_rules! __internal_headers_map {
($headers:ident, { $($name:expr => $val:expr,)* }) => { ($headers:ident, { $($name:expr => $val:expr,)* }) => {
$( $(
$headers.insert($name, $val.to_string().parse().expect("header value")); $headers.insert($name, $val.to_string().parse().expect("header value"));
@@ -185,7 +205,39 @@ macro_rules! __internal_headers {
} }
} }
#[derive(Clone, Debug, Default)] macro_rules! __internal_headers_eq {
(@pat $name: expr, $pat:pat) => {
::std::sync::Arc::new(move |__hdrs: &::hyper::HeaderMap| {
match __hdrs.get($name) {
$pat => (),
other => panic!("headers[{}] was not {}: {:?}", stringify!($name), stringify!($pat), other),
}
}) as ::std::sync::Arc<Fn(&::hyper::HeaderMap) + Send + Sync>
};
(@val $name: expr, NONE) => {
__internal_headers_eq!(@pat $name, None);
};
(@val $name: expr, SOME) => {
__internal_headers_eq!(@pat $name, Some(_));
};
(@val $name: expr, $val:expr) => ({
let __val = Option::from($val);
::std::sync::Arc::new(move |__hdrs: &::hyper::HeaderMap| {
if let Some(ref val) = __val {
assert_eq!(__hdrs.get($name).expect(stringify!($name)), val.to_string().as_str(), stringify!($name));
} else {
assert_eq!(__hdrs.get($name), None, stringify!($name));
}
}) as ::std::sync::Arc<Fn(&::hyper::HeaderMap) + Send + Sync>
});
($headers:ident, { $($name:expr => $val:tt,)* }) => {
$(
$headers.push(__internal_headers_eq!(@val $name, $val));
)*
}
}
#[derive(Clone, Debug)]
pub struct __CReq { pub struct __CReq {
pub method: &'static str, pub method: &'static str,
pub uri: &'static str, pub uri: &'static str,
@@ -193,21 +245,43 @@ pub struct __CReq {
pub body: Vec<u8>, pub body: Vec<u8>,
} }
#[derive(Clone, Debug, Default)] impl Default for __CReq {
fn default() -> __CReq {
__CReq {
method: "GET",
uri: "/",
headers: HeaderMap::new(),
body: Vec::new(),
}
}
}
#[derive(Clone, Default)]
pub struct __CRes { pub struct __CRes {
pub status: hyper::StatusCode, pub status: hyper::StatusCode,
pub body: Vec<u8>, pub body: Vec<u8>,
pub headers: HeaderMap, pub headers: __HeadersEq,
} }
#[derive(Clone, Debug, Default)] #[derive(Clone)]
pub struct __SReq { pub struct __SReq {
pub method: &'static str, pub method: &'static str,
pub uri: &'static str, pub uri: &'static str,
pub headers: HeaderMap, pub headers: __HeadersEq,
pub body: Vec<u8>, pub body: Vec<u8>,
} }
impl Default for __SReq {
fn default() -> __SReq {
__SReq {
method: "GET",
uri: "/",
headers: Vec::new(),
body: Vec::new(),
}
}
}
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct __SRes { pub struct __SRes {
pub status: hyper::StatusCode, pub status: hyper::StatusCode,
@@ -215,6 +289,8 @@ pub struct __SRes {
pub headers: HeaderMap, pub headers: HeaderMap,
} }
pub type __HeadersEq = Vec<Arc<Fn(&HeaderMap) + Send + Sync>>;
pub struct __TestConfig { pub struct __TestConfig {
pub client_version: usize, pub client_version: usize,
pub client_msgs: Vec<(__CReq, __CRes)>, pub client_msgs: Vec<(__CReq, __CRes)>,
@@ -257,20 +333,17 @@ pub fn __run_test(cfg: __TestConfig) {
.unwrap() .unwrap()
.remove(0); .remove(0);
assert_eq!(req.uri().path(), sreq.uri); assert_eq!(req.uri().path(), sreq.uri, "client path");
assert_eq!(req.method(), &sreq.method); assert_eq!(req.method(), &sreq.method, "client method");
assert_eq!(req.version(), version); assert_eq!(req.version(), version, "client version");
for (name, value) in &sreq.headers { for func in &sreq.headers {
assert_eq!( func(&req.headers());
req.headers()[name],
value
);
} }
let sbody = sreq.body; let sbody = sreq.body;
req.into_body() req.into_body()
.concat2() .concat2()
.map(move |body| { .map(move |body| {
assert_eq!(body.as_ref(), sbody.as_slice()); assert_eq!(body.as_ref(), sbody.as_slice(), "client body");
let mut res = Response::builder() let mut res = Response::builder()
.status(sres.status) .status(sres.status)
@@ -339,18 +412,15 @@ pub fn __run_test(cfg: __TestConfig) {
client.request(req) client.request(req)
.and_then(move |res| { .and_then(move |res| {
assert_eq!(res.status(), cstatus); assert_eq!(res.status(), cstatus, "server status");
assert_eq!(res.version(), version); assert_eq!(res.version(), version, "server version");
for (name, value) in &cheaders { for func in &cheaders {
assert_eq!( func(&res.headers());
res.headers()[name],
value
);
} }
res.into_body().concat2() res.into_body().concat2()
}) })
.map(move |body| { .map(move |body| {
assert_eq!(body.as_ref(), cbody.as_slice()); assert_eq!(body.as_ref(), cbody.as_slice(), "server body");
}) })
.map_err(|e| panic!("client error: {}", e)) .map_err(|e| panic!("client error: {}", e))
}); });