fix(http2): add Date header if not present for HTTP2 server responses
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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))
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user