Skip to content

Commit 386fc0d

Browse files
lnicolaseanmonstar
authored andcommitted
feat(http2): set Content-Length header on outgoing messages
Closes #1547
1 parent f20afba commit 386fc0d

File tree

5 files changed

+97
-0
lines changed

5 files changed

+97
-0
lines changed

src/headers.rs

+7
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ pub fn content_length_value(len: u64) -> HeaderValue {
7878
}
7979
}
8080

81+
pub fn set_content_length_if_missing(headers: &mut HeaderMap, len: u64) {
82+
headers
83+
.entry(CONTENT_LENGTH)
84+
.unwrap()
85+
.or_insert(content_length_value(len));
86+
}
87+
8188
pub fn transfer_encoding_is_chunked(headers: &HeaderMap) -> bool {
8289
is_chunked(headers.get_all(TRANSFER_ENCODING).into_iter())
8390
}

src/proto/h2/client.rs

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use tokio_io::{AsyncRead, AsyncWrite};
77

88
use body::Payload;
99
use ::common::{Exec, Never};
10+
use headers;
1011
use super::{PipeToSendStream, SendBuf};
1112
use ::{Body, Request, Response};
1213

@@ -106,6 +107,9 @@ where
106107
let (head, body) = req.into_parts();
107108
let mut req = ::http::Request::from_parts(head, ());
108109
super::strip_connection_headers(req.headers_mut());
110+
if let Some(len) = body.content_length() {
111+
headers::set_content_length_if_missing(req.headers_mut(), len);
112+
}
109113
let eos = body.is_end_stream();
110114
let (fut, body_tx) = match tx.send_request(req, eos) {
111115
Ok(ok) => ok,

src/proto/h2/server.rs

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use tokio_io::{AsyncRead, AsyncWrite};
55

66
use ::body::Payload;
77
use ::common::Exec;
8+
use ::headers;
89
use ::service::Service;
910
use super::{PipeToSendStream, SendBuf};
1011

@@ -171,6 +172,9 @@ where
171172
let (head, body) = res.into_parts();
172173
let mut res = ::http::Response::from_parts(head, ());
173174
super::strip_connection_headers(res.headers_mut());
175+
if let Some(len) = body.content_length() {
176+
headers::set_content_length_if_missing(res.headers_mut(), len);
177+
}
174178
macro_rules! reply {
175179
($eos:expr) => ({
176180
match self.reply.send_response(res, $eos) {

tests/integration.rs

+23
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,29 @@ t! {
166166
;
167167
}
168168

169+
t! {
170+
post_outgoing_length,
171+
client:
172+
request:
173+
method: "POST",
174+
uri: "/hello",
175+
body: "hello, world!",
176+
;
177+
response:
178+
;
179+
server:
180+
request:
181+
method: "POST",
182+
uri: "/hello",
183+
headers: {
184+
"content-length" => "13",
185+
},
186+
body: "hello, world!",
187+
;
188+
response:
189+
;
190+
}
191+
169192
t! {
170193
post_chunked,
171194
client:

tests/server.rs

+59
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,65 @@ mod response_body_lengths {
353353
expects_con_len: false,
354354
});
355355
}
356+
357+
#[test]
358+
fn http2_auto_response_with_known_length() {
359+
use hyper::body::Payload;
360+
361+
let server = serve();
362+
let addr_str = format!("http://{}", server.addr());
363+
server.reply().body("Hello, World!");
364+
365+
hyper::rt::run(hyper::rt::lazy(move || {
366+
let client: Client<_, hyper::Body> = Client::builder().http2_only(true).build_http();
367+
let uri = addr_str
368+
.parse::<hyper::Uri>()
369+
.expect("server addr should parse");
370+
371+
client
372+
.get(uri)
373+
.and_then(|res| {
374+
assert_eq!(res.headers().get("content-length").unwrap(), "13");
375+
// TODO: enable this after #1546
376+
let _ = res.body().content_length();
377+
// assert_eq!(res.body().content_length(), Some(13));
378+
Ok(())
379+
})
380+
.map(|_| ())
381+
.map_err(|_e| ())
382+
}));
383+
}
384+
385+
#[test]
386+
fn http2_auto_response_with_conflicting_lengths() {
387+
use hyper::body::Payload;
388+
389+
let server = serve();
390+
let addr_str = format!("http://{}", server.addr());
391+
server
392+
.reply()
393+
.header("content-length", "10")
394+
.body("Hello, World!");
395+
396+
hyper::rt::run(hyper::rt::lazy(move || {
397+
let client: Client<_, hyper::Body> = Client::builder().http2_only(true).build_http();
398+
let uri = addr_str
399+
.parse::<hyper::Uri>()
400+
.expect("server addr should parse");
401+
402+
client
403+
.get(uri)
404+
.and_then(|res| {
405+
assert_eq!(res.headers().get("content-length").unwrap(), "10");
406+
// TODO: enable or remove this after #1546
407+
let _ = res.body().content_length();
408+
// assert_eq!(res.body().content_length(), Some(10));
409+
Ok(())
410+
})
411+
.map(|_| ())
412+
.map_err(|_e| ())
413+
}));
414+
}
356415
}
357416

358417
#[test]

0 commit comments

Comments
 (0)