Add Graceful Shutdown support
If graceful shutdown is initiated, a GOAWAY of the max stream ID - 1 is sent, followed by a PING frame, to measure RTT. When the PING is ACKed, the connection sends a new GOAWAY with the proper last processed stream ID. From there, once all active streams have completely, the connection will finally close.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
#![deny(warnings)]
|
||||
pub mod support;
|
||||
use support::prelude::*;
|
||||
|
||||
@@ -205,7 +206,7 @@ fn sends_reset_cancel_when_req_body_is_dropped() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sends_goaway_when_serv_closes_connection() {
|
||||
fn abrupt_shutdown() {
|
||||
let _ = ::env_logger::try_init();
|
||||
let (io, client) = mock::new();
|
||||
|
||||
@@ -222,7 +223,7 @@ fn sends_goaway_when_serv_closes_connection() {
|
||||
|
||||
let srv = server::handshake(io).expect("handshake").and_then(|srv| {
|
||||
srv.into_future().unwrap().and_then(|(_, mut srv)| {
|
||||
srv.close_connection();
|
||||
srv.abrupt_shutdown(Reason::NO_ERROR);
|
||||
srv.into_future().unwrap()
|
||||
})
|
||||
});
|
||||
@@ -231,7 +232,7 @@ fn sends_goaway_when_serv_closes_connection() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serve_request_then_serv_closes_connection() {
|
||||
fn graceful_shutdown() {
|
||||
let _ = ::env_logger::try_init();
|
||||
let (io, client) = mock::new();
|
||||
|
||||
@@ -241,37 +242,74 @@ fn serve_request_then_serv_closes_connection() {
|
||||
.recv_settings()
|
||||
.send_frame(
|
||||
frames::headers(1)
|
||||
.request("GET", "https://example.com/"),
|
||||
.request("GET", "https://example.com/")
|
||||
.eos(),
|
||||
)
|
||||
.recv_frame(frames::go_away(StreamId::MAX_CLIENT))
|
||||
.recv_frame(frames::ping(frame::Ping::SHUTDOWN))
|
||||
.recv_frame(frames::headers(1).response(200).eos())
|
||||
.recv_frame(frames::reset(1).cancel())
|
||||
// Pretend this stream was sent while the GOAWAY was in flight
|
||||
.send_frame(
|
||||
frames::headers(3)
|
||||
.request("GET", "https://example.com/"),
|
||||
.request("POST", "https://example.com/"),
|
||||
)
|
||||
.send_frame(frames::ping(frame::Ping::SHUTDOWN).pong())
|
||||
.recv_frame(frames::go_away(3))
|
||||
// streams sent after GOAWAY receive no response
|
||||
.send_frame(
|
||||
frames::headers(5)
|
||||
frames::headers(7)
|
||||
.request("GET", "https://example.com/"),
|
||||
)
|
||||
.close();
|
||||
.send_frame(frames::data(7, "").eos())
|
||||
.send_frame(frames::data(3, "").eos())
|
||||
.recv_frame(frames::headers(3).response(200).eos())
|
||||
.close(); //TODO: closed()?
|
||||
|
||||
let srv = server::handshake(io).expect("handshake").and_then(|srv| {
|
||||
srv.into_future().unwrap().and_then(|(reqstream, srv)| {
|
||||
let srv = server::handshake(io)
|
||||
.expect("handshake")
|
||||
.and_then(|srv| {
|
||||
srv.into_future().unwrap()
|
||||
})
|
||||
.and_then(|(reqstream, mut srv)| {
|
||||
let (req, mut stream) = reqstream.unwrap();
|
||||
|
||||
assert_eq!(req.method(), &http::Method::GET);
|
||||
|
||||
let rsp = http::Response::builder().status(200).body(()).unwrap();
|
||||
srv.graceful_shutdown();
|
||||
|
||||
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.into_future().unwrap()
|
||||
})
|
||||
});
|
||||
.and_then(|(reqstream, srv)| {
|
||||
let (req, mut stream) = reqstream.unwrap();
|
||||
assert_eq!(req.method(), &http::Method::POST);
|
||||
let body = req.into_parts().1;
|
||||
|
||||
let body = body.concat2().and_then(move |buf| {
|
||||
assert!(buf.is_empty());
|
||||
|
||||
let rsp = http::Response::builder()
|
||||
.status(200)
|
||||
.body(())
|
||||
.unwrap();
|
||||
stream.send_response(rsp, true).unwrap();
|
||||
Ok(())
|
||||
});
|
||||
|
||||
srv.into_future()
|
||||
.map(|(req, _srv)| {
|
||||
assert!(req.is_none(), "unexpected request");
|
||||
})
|
||||
.drive(body)
|
||||
.and_then(|(srv, ())| {
|
||||
srv.expect("srv")
|
||||
})
|
||||
});
|
||||
|
||||
srv.join(client).wait().expect("wait");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user