Files
h2/tests/server.rs
Darren Tsung 0c59957d88 When Streams are dropped, close Connection (#221) (#222)
When all Streams are dropped / finished, the Connection was held
open until the peer hangs up. Instead, the Connection should hang up
once it knows that nothing more will be sent.

To fix this, we notify the Connection when a stream is no longer
referenced. On the Connection poll(), we check that there are no
active, held, reset streams or any references to the Streams
and transition to sending a GOAWAY if that is case.

The specific behavior depends on if running as a client or server.
2018-02-15 13:14:18 -08:00

409 lines
12 KiB
Rust

pub mod support;
use support::prelude::*;
const SETTINGS: &'static [u8] = &[0, 0, 0, 4, 0, 0, 0, 0, 0];
const SETTINGS_ACK: &'static [u8] = &[0, 0, 0, 4, 1, 0, 0, 0, 0];
#[test]
fn read_preface_in_multiple_frames() {
let _ = ::env_logger::init();
let mock = mock_io::Builder::new()
.read(b"PRI * HTTP/2.0")
.read(b"\r\n\r\nSM\r\n\r\n")
.write(SETTINGS)
.read(SETTINGS)
.write(SETTINGS_ACK)
.read(SETTINGS_ACK)
.build();
let h2 = server::handshake(mock).wait().unwrap();
assert!(Stream::wait(h2).next().is_none());
}
#[test]
fn server_builder_set_max_concurrent_streams() {
let _ = ::env_logger::init();
let (io, client) = mock::new();
let mut settings = frame::Settings::default();
settings.set_max_concurrent_streams(Some(1));
let client = client
.assert_server_handshake()
.unwrap()
.recv_custom_settings(settings)
.send_frame(
frames::headers(1)
.request("GET", "https://example.com/"),
)
.send_frame(
frames::headers(3)
.request("GET", "https://example.com/"),
)
.send_frame(frames::data(1, &b"hello"[..]).eos(),)
.recv_frame(frames::reset(3).refused())
.recv_frame(frames::headers(1).response(200).eos())
.close();
let mut builder = server::Builder::new();
builder.max_concurrent_streams(1);
let h2 = builder
.handshake::<_, Bytes>(io)
.expect("handshake")
.and_then(|srv| {
srv.into_future().unwrap().and_then(|(reqstream, srv)| {
let (req, mut stream) = reqstream.unwrap();
assert_eq!(req.method(), &http::Method::GET);
let rsp =
http::Response::builder()
.status(200).body(())
.unwrap();
stream.send_response(rsp, true).unwrap();
srv.into_future().unwrap()
})
});
h2.join(client).wait().expect("wait");
}
#[test]
fn serve_request() {
let _ = ::env_logger::init();
let (io, client) = mock::new();
let client = client
.assert_server_handshake()
.unwrap()
.recv_settings()
.send_frame(
frames::headers(1)
.request("GET", "https://example.com/")
.eos(),
)
.recv_frame(frames::headers(1).response(200).eos())
.close();
let srv = server::handshake(io).expect("handshake").and_then(|srv| {
srv.into_future().unwrap().and_then(|(reqstream, srv)| {
let (req, mut stream) = reqstream.unwrap();
assert_eq!(req.method(), &http::Method::GET);
let rsp = http::Response::builder().status(200).body(()).unwrap();
stream.send_response(rsp, true).unwrap();
srv.into_future().unwrap()
})
});
srv.join(client).wait().expect("wait");
}
#[test]
#[ignore]
fn accept_with_pending_connections_after_socket_close() {}
#[test]
fn recv_invalid_authority() {
let _ = ::env_logger::init();
let (io, client) = mock::new();
let bad_auth = util::byte_str("not:a/good authority");
let mut bad_headers: frame::Headers = frames::headers(1)
.request("GET", "https://example.com/")
.eos()
.into();
bad_headers.pseudo_mut().authority = Some(bad_auth);
let client = client
.assert_server_handshake()
.unwrap()
.recv_settings()
.send_frame(bad_headers)
.recv_frame(frames::reset(1).protocol_error())
.close();
let srv = server::handshake(io)
.expect("handshake")
.and_then(|srv| srv.into_future().unwrap());
srv.join(client).wait().expect("wait");
}
#[test]
fn recv_connection_header() {
let _ = ::env_logger::init();
let (io, client) = mock::new();
let req = |id, name, val| {
frames::headers(id)
.request("GET", "https://example.com/")
.field(name, val)
.eos()
};
let client = client
.assert_server_handshake()
.unwrap()
.recv_settings()
.send_frame(req(1, "connection", "foo"))
.send_frame(req(3, "keep-alive", "5"))
.send_frame(req(5, "proxy-connection", "bar"))
.send_frame(req(7, "transfer-encoding", "chunked"))
.send_frame(req(9, "upgrade", "HTTP/2.0"))
.recv_frame(frames::reset(1).protocol_error())
.recv_frame(frames::reset(3).protocol_error())
.recv_frame(frames::reset(5).protocol_error())
.recv_frame(frames::reset(7).protocol_error())
.recv_frame(frames::reset(9).protocol_error())
.close();
let srv = server::handshake(io)
.expect("handshake")
.and_then(|srv| srv.into_future().unwrap());
srv.join(client).wait().expect("wait");
}
#[test]
fn sends_reset_cancel_when_req_body_is_dropped() {
let _ = ::env_logger::init();
let (io, client) = mock::new();
let client = client
.assert_server_handshake()
.unwrap()
.recv_settings()
.send_frame(
frames::headers(1)
.request("POST", "https://example.com/")
)
.recv_frame(frames::headers(1).response(200).eos())
.recv_frame(frames::reset(1).cancel())
.close();
let srv = server::handshake(io).expect("handshake").and_then(|srv| {
srv.into_future().unwrap().and_then(|(reqstream, srv)| {
let (req, mut stream) = reqstream.unwrap();
assert_eq!(req.method(), &http::Method::POST);
let rsp = http::Response::builder().status(200).body(()).unwrap();
stream.send_response(rsp, true).unwrap();
srv.into_future().unwrap()
})
});
srv.join(client).wait().expect("wait");
}
#[test]
fn sends_goaway_when_serv_closes_connection() {
let _ = ::env_logger::init();
let (io, client) = mock::new();
let client = client
.assert_server_handshake()
.unwrap()
.recv_settings()
.send_frame(
frames::headers(1)
.request("POST", "https://example.com/")
)
.recv_frame(frames::go_away(1))
.close();
let srv = server::handshake(io).expect("handshake").and_then(|srv| {
srv.into_future().unwrap().and_then(|(_, mut srv)| {
srv.close_connection();
srv.into_future().unwrap()
})
});
srv.join(client).wait().expect("wait");
}
#[test]
fn serve_request_then_serv_closes_connection() {
let _ = ::env_logger::init();
let (io, client) = mock::new();
let client = client
.assert_server_handshake()
.unwrap()
.recv_settings()
.send_frame(
frames::headers(1)
.request("GET", "https://example.com/"),
)
.recv_frame(frames::headers(1).response(200).eos())
.recv_frame(frames::reset(1).cancel())
.send_frame(
frames::headers(3)
.request("GET", "https://example.com/"),
)
.recv_frame(frames::go_away(3))
// streams sent after GOAWAY receive no response
.send_frame(
frames::headers(5)
.request("GET", "https://example.com/"),
)
.close();
let srv = server::handshake(io).expect("handshake").and_then(|srv| {
srv.into_future().unwrap().and_then(|(reqstream, srv)| {
let (req, mut stream) = reqstream.unwrap();
assert_eq!(req.method(), &http::Method::GET);
let rsp = http::Response::builder().status(200).body(()).unwrap();
stream.send_response(rsp, true).unwrap();
srv.into_future().unwrap().and_then(|(_reqstream, mut srv)| {
srv.close_connection();
srv.into_future().unwrap()
})
})
});
srv.join(client).wait().expect("wait");
}
#[test]
fn sends_reset_cancel_when_res_body_is_dropped() {
let _ = ::env_logger::init();
let (io, client) = mock::new();
let client = client
.assert_server_handshake()
.unwrap()
.recv_settings()
.send_frame(
frames::headers(1)
.request("GET", "https://example.com/")
.eos()
)
.recv_frame(frames::headers(1).response(200))
.recv_frame(frames::reset(1).cancel())
.send_frame(
frames::headers(3)
.request("GET", "https://example.com/")
.eos()
)
.recv_frame(frames::headers(3).response(200))
.recv_frame(frames::data(3, vec![0; 10]))
.recv_frame(frames::reset(3).cancel())
.close();
let srv = server::handshake(io).expect("handshake").and_then(|srv| {
srv.into_future().unwrap().and_then(|(reqstream, srv)| {
let (req, mut stream) = reqstream.unwrap();
assert_eq!(req.method(), &http::Method::GET);
let rsp = http::Response::builder()
.status(200)
.body(())
.unwrap();
stream.send_response(rsp, false).unwrap();
// SendStream dropped
srv.into_future().unwrap()
}).and_then(|(reqstream, srv)| {
let (_req, mut stream) = reqstream.unwrap();
let rsp = http::Response::builder()
.status(200)
.body(())
.unwrap();
let mut tx = stream.send_response(rsp, false).unwrap();
tx.send_data(vec![0; 10].into(), false).unwrap();
// no send_data with eos
srv.into_future().unwrap()
})
});
srv.join(client).wait().expect("wait");
}
#[test]
fn too_big_headers_sends_431() {
let _ = ::env_logger::init();
let (io, client) = mock::new();
let client = client
.assert_server_handshake()
.unwrap()
.recv_custom_settings(
frames::settings()
.max_header_list_size(10)
)
.send_frame(
frames::headers(1)
.request("GET", "https://example.com/")
.field("some-header", "some-value")
.eos()
)
.recv_frame(frames::headers(1).response(431).eos())
.idle_ms(10)
.close();
let srv = server::Builder::new()
.max_header_list_size(10)
.handshake::<_, Bytes>(io)
.expect("handshake")
.and_then(|srv| {
srv.into_future()
.expect("server")
.map(|(req, _)| {
assert!(req.is_none(), "req is {:?}", req);
})
});
srv.join(client).wait().expect("wait");
}
#[test]
fn too_big_headers_sends_reset_after_431_if_not_eos() {
let _ = ::env_logger::init();
let (io, client) = mock::new();
let client = client
.assert_server_handshake()
.unwrap()
.recv_custom_settings(
frames::settings()
.max_header_list_size(10)
)
.send_frame(
frames::headers(1)
.request("GET", "https://example.com/")
.field("some-header", "some-value")
)
.recv_frame(frames::headers(1).response(431).eos())
.recv_frame(frames::reset(1).refused())
.close();
let srv = server::Builder::new()
.max_header_list_size(10)
.handshake::<_, Bytes>(io)
.expect("handshake")
.and_then(|srv| {
srv.into_future()
.expect("server")
.map(|(req, _)| {
assert!(req.is_none(), "req is {:?}", req);
})
});
srv.join(client).wait().expect("wait");
}