perf(h1): improve parsing and encoding of http1 messages

This commit is contained in:
Sean McArthur
2018-05-11 15:40:42 -07:00
parent c3c35e866c
commit 26417fc24a
13 changed files with 1006 additions and 442 deletions

View File

@@ -181,6 +181,13 @@ mod response_body_lengths {
has_header(&body, "transfer-encoding:"),
"expects_chunked"
);
assert_eq!(
case.expects_chunked,
has_header(&body, "chunked\r\n"),
"expects_chunked"
);
assert_eq!(
case.expects_con_len,
has_header(&body, "content-length:"),
@@ -200,7 +207,7 @@ mod response_body_lengths {
}
#[test]
fn get_fixed_response_known() {
fn fixed_response_known() {
run_test(TestCase {
version: 1,
headers: &[("content-length", "11")],
@@ -211,7 +218,7 @@ mod response_body_lengths {
}
#[test]
fn get_fixed_response_unknown() {
fn fixed_response_unknown() {
run_test(TestCase {
version: 1,
headers: &[("content-length", "11")],
@@ -222,7 +229,18 @@ mod response_body_lengths {
}
#[test]
fn get_chunked_response_known() {
fn fixed_response_known_empty() {
run_test(TestCase {
version: 1,
headers: &[("content-length", "0")],
body: Bd::Known(""),
expects_chunked: false,
expects_con_len: true,
});
}
#[test]
fn chunked_response_known() {
run_test(TestCase {
version: 1,
headers: &[("transfer-encoding", "chunked")],
@@ -234,7 +252,7 @@ mod response_body_lengths {
}
#[test]
fn get_chunked_response_unknown() {
fn chunked_response_unknown() {
run_test(TestCase {
version: 1,
headers: &[("transfer-encoding", "chunked")],
@@ -245,7 +263,22 @@ mod response_body_lengths {
}
#[test]
fn get_chunked_response_trumps_length() {
fn te_response_adds_chunked() {
run_test(TestCase {
version: 1,
headers: &[("transfer-encoding", "gzip")],
body: Bd::Unknown("foo bar baz"),
expects_chunked: true,
expects_con_len: false,
});
}
#[test]
#[ignore]
// This used to be the case, but providing this functionality got in the
// way of performance. It can probably be brought back later, and doing
// so should be backwards-compatible...
fn chunked_response_trumps_length() {
run_test(TestCase {
version: 1,
headers: &[
@@ -260,7 +293,7 @@ mod response_body_lengths {
}
#[test]
fn get_auto_response_with_entity_unknown_length() {
fn auto_response_with_unknown_length() {
run_test(TestCase {
version: 1,
// no headers means trying to guess from Payload
@@ -272,7 +305,7 @@ mod response_body_lengths {
}
#[test]
fn get_auto_response_with_entity_known_length() {
fn auto_response_with_known_length() {
run_test(TestCase {
version: 1,
// no headers means trying to guess from Payload
@@ -283,9 +316,20 @@ mod response_body_lengths {
});
}
#[test]
fn auto_response_known_empty() {
run_test(TestCase {
version: 1,
// no headers means trying to guess from Payload
headers: &[],
body: Bd::Known(""),
expects_chunked: false,
expects_con_len: true,
});
}
#[test]
fn http_10_get_auto_response_with_entity_unknown_length() {
fn http10_auto_response_with_unknown_length() {
run_test(TestCase {
version: 0,
// no headers means trying to guess from Payload
@@ -298,7 +342,7 @@ mod response_body_lengths {
#[test]
fn http_10_get_chunked_response() {
fn http10_chunked_response() {
run_test(TestCase {
version: 0,
// http/1.0 should strip this header
@@ -620,6 +664,62 @@ fn disable_keep_alive() {
}
}
#[test]
fn header_connection_close() {
let foo_bar = b"foo bar baz";
let server = serve();
server.reply()
.header("content-length", foo_bar.len().to_string())
.header("connection", "close")
.body(foo_bar);
let mut req = connect(server.addr());
req.write_all(b"\
GET / HTTP/1.1\r\n\
Host: example.domain\r\n\
Connection: keep-alive\r\n\
\r\n\
").expect("writing 1");
let mut buf = [0; 1024 * 8];
loop {
let n = req.read(&mut buf[..]).expect("reading 1");
if n < buf.len() {
if &buf[n - foo_bar.len()..n] == foo_bar {
break;
} else {
}
}
}
// try again!
// but since the server responded with connection: close, the internal
// state should have noticed and shutdown
let quux = b"zar quux";
server.reply()
.header("content-length", quux.len().to_string())
.body(quux);
// the write can possibly succeed, since it fills the kernel buffer on the first write
let _ = req.write_all(b"\
GET /quux HTTP/1.1\r\n\
Host: example.domain\r\n\
Connection: close\r\n\
\r\n\
");
let mut buf = [0; 1024 * 8];
match req.read(&mut buf[..]) {
// Ok(0) means EOF, so a proper shutdown
// Err(_) could mean ConnReset or something, also fine
Ok(0) |
Err(_) => {}
Ok(n) => {
panic!("read {} bytes on a disabled keep-alive socket", n);
}
}
}
#[test]
fn expect_continue() {
let server = serve();