Files
h2/tests/stream_states.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

811 lines
24 KiB
Rust

#[macro_use]
extern crate log;
pub mod support;
use support::prelude::*;
#[test]
fn send_recv_headers_only() {
let _ = env_logger::init();
let mock = mock_io::Builder::new()
.handshake()
// Write GET /
.write(&[
0, 0, 0x10, 1, 5, 0, 0, 0, 1, 0x82, 0x87, 0x41, 0x8B, 0x9D, 0x29,
0xAC, 0x4B, 0x8F, 0xA8, 0xE9, 0x19, 0x97, 0x21, 0xE9, 0x84,
])
.write(frames::SETTINGS_ACK)
// Read response
.read(&[0, 0, 1, 1, 5, 0, 0, 0, 1, 0x89])
.build();
let (mut client, mut h2) = client::handshake(mock).wait().unwrap();
// Send the request
let request = Request::builder()
.uri("https://http2.akamai.com/")
.body(())
.unwrap();
info!("sending request");
let (response, _) = client.send_request(request, true).unwrap();
let resp = h2.run(response).unwrap();
assert_eq!(resp.status(), StatusCode::NO_CONTENT);
h2.wait().unwrap();
}
#[test]
fn send_recv_data() {
let _ = env_logger::init();
let mock = mock_io::Builder::new()
.handshake()
.write(&[
// POST /
0, 0, 16, 1, 4, 0, 0, 0, 1, 131, 135, 65, 139, 157, 41,
172, 75, 143, 168, 233, 25, 151, 33, 233, 132,
])
.write(&[
// DATA
0, 0, 5, 0, 1, 0, 0, 0, 1, 104, 101, 108, 108, 111,
])
.write(frames::SETTINGS_ACK)
// Read response
.read(&[
// HEADERS
0, 0, 1, 1, 4, 0, 0, 0, 1, 136,
// DATA
0, 0, 5, 0, 1, 0, 0, 0, 1, 119, 111, 114, 108, 100
])
.build();
let (mut client, mut h2) = client::Builder::new().handshake(mock).wait().unwrap();
let request = Request::builder()
.method(Method::POST)
.uri("https://http2.akamai.com/")
.body(())
.unwrap();
info!("sending request");
let (response, mut stream) = client.send_request(request, false).unwrap();
// Reserve send capacity
stream.reserve_capacity(5);
assert_eq!(stream.capacity(), 5);
// Send the data
stream.send_data("hello", true).unwrap();
// Get the response
let resp = h2.run(response).unwrap();
assert_eq!(resp.status(), StatusCode::OK);
// Take the body
let (_, body) = resp.into_parts();
// Wait for all the data frames to be received
let bytes = h2.run(body.collect()).unwrap();
// One byte chunk
assert_eq!(1, bytes.len());
assert_eq!(bytes[0], &b"world"[..]);
// The H2 connection is closed
h2.wait().unwrap();
}
#[test]
fn send_headers_recv_data_single_frame() {
let _ = env_logger::init();
let mock = mock_io::Builder::new()
.handshake()
// Write GET /
.write(&[
0, 0, 16, 1, 5, 0, 0, 0, 1, 130, 135, 65, 139, 157, 41, 172, 75,
143, 168, 233, 25, 151, 33, 233, 132
])
.write(frames::SETTINGS_ACK)
// Read response
.read(&[
0, 0, 1, 1, 4, 0, 0, 0, 1, 136, 0, 0, 5, 0, 0, 0, 0, 0, 1, 104, 101,
108, 108, 111, 0, 0, 5, 0, 1, 0, 0, 0, 1, 119, 111, 114, 108, 100,
])
.build();
let (mut client, mut h2) = client::handshake(mock).wait().unwrap();
// Send the request
let request = Request::builder()
.uri("https://http2.akamai.com/")
.body(())
.unwrap();
info!("sending request");
let (response, _) = client.send_request(request, true).unwrap();
let resp = h2.run(response).unwrap();
assert_eq!(resp.status(), StatusCode::OK);
// Take the body
let (_, body) = resp.into_parts();
// Wait for all the data frames to be received
let bytes = h2.run(body.collect()).unwrap();
// Two data frames
assert_eq!(2, bytes.len());
assert_eq!(bytes[0], &b"hello"[..]);
assert_eq!(bytes[1], &b"world"[..]);
// The H2 connection is closed
h2.wait().unwrap();
}
#[test]
fn closed_streams_are_released() {
let _ = ::env_logger::init();
let (io, srv) = mock::new();
let h2 = client::handshake(io).unwrap().and_then(|(mut client, h2)| {
let request = Request::get("https://example.com/").body(()).unwrap();
// Send request
let (response, _) = client.send_request(request, true).unwrap();
h2.drive(response).and_then(move |(_, response)| {
assert_eq!(response.status(), StatusCode::NO_CONTENT);
// There are no active streams
assert_eq!(0, client.num_active_streams());
// The response contains a handle for the body. This keeps the
// stream wired.
assert_eq!(1, client.num_wired_streams());
let (_, body) = response.into_parts();
assert!(body.is_end_stream());
drop(body);
// The stream state is now free
assert_eq!(0, client.num_wired_streams());
Ok(())
})
});
let srv = srv.assert_client_handshake()
.unwrap()
.recv_settings()
.recv_frame(
frames::headers(1)
.request("GET", "https://example.com/")
.eos(),
)
.send_frame(frames::headers(1).response(204).eos())
.close();
let _ = h2.join(srv).wait().unwrap();
}
#[test]
fn errors_if_recv_frame_exceeds_max_frame_size() {
let _ = ::env_logger::init();
let (io, mut srv) = mock::new();
let h2 = client::handshake(io).unwrap().and_then(|(mut client, h2)| {
let request = Request::builder()
.method(Method::GET)
.uri("https://example.com/")
.body(())
.unwrap();
let req = client
.send_request(request, true)
.unwrap()
.0.unwrap()
.and_then(|resp| {
assert_eq!(resp.status(), StatusCode::OK);
let body = resp.into_parts().1;
body.concat2().then(|res| {
let err = res.unwrap_err();
assert_eq!(err.to_string(), "protocol error: frame with invalid size");
Ok::<(), ()>(())
})
});
// client should see a conn error
let conn = h2.then(|res| {
let err = res.unwrap_err();
assert_eq!(err.to_string(), "protocol error: frame with invalid size");
Ok::<(), ()>(())
});
conn.unwrap().join(req)
});
// a bad peer
srv.codec_mut().set_max_send_frame_size(16_384 * 4);
let srv = srv.assert_client_handshake()
.unwrap()
.ignore_settings()
.recv_frame(
frames::headers(1)
.request("GET", "https://example.com/")
.eos(),
)
.send_frame(frames::headers(1).response(200))
.send_frame(frames::data(1, vec![0; 16_385]).eos())
.recv_frame(frames::go_away(0).frame_size())
.close();
let _ = h2.join(srv).wait().unwrap();
}
#[test]
fn configure_max_frame_size() {
let _ = ::env_logger::init();
let (io, mut srv) = mock::new();
let h2 = client::Builder::new()
.max_frame_size(16_384 * 2)
.handshake::<_, Bytes>(io)
.expect("handshake")
.and_then(|(mut client, h2)| {
let request = Request::builder()
.method(Method::GET)
.uri("https://example.com/")
.body(())
.unwrap();
let req = client
.send_request(request, true)
.unwrap()
.0.expect("response")
.and_then(|resp| {
assert_eq!(resp.status(), StatusCode::OK);
let body = resp.into_parts().1;
body.concat2().expect("body")
})
.and_then(|buf| {
assert_eq!(buf.len(), 16_385);
Ok(())
});
h2.expect("client").join(req)
});
// a good peer
srv.codec_mut().set_max_send_frame_size(16_384 * 2);
let srv = srv.assert_client_handshake()
.unwrap()
.ignore_settings()
.recv_frame(
frames::headers(1)
.request("GET", "https://example.com/")
.eos(),
)
.send_frame(frames::headers(1).response(200))
.send_frame(frames::data(1, vec![0; 16_385]).eos())
.close();
let _ = h2.join(srv).wait().expect("wait");
}
#[test]
fn recv_goaway_finishes_processed_streams() {
let _ = ::env_logger::init();
let (io, srv) = mock::new();
let srv = srv.assert_client_handshake()
.unwrap()
.recv_settings()
.recv_frame(
frames::headers(1)
.request("GET", "https://example.com/")
.eos(),
)
.recv_frame(
frames::headers(3)
.request("GET", "https://example.com/")
.eos(),
)
.send_frame(frames::go_away(1))
.send_frame(frames::headers(1).response(200))
.send_frame(frames::data(1, vec![0; 16_384]).eos())
// expecting a goaway of 0, since server never initiated a stream
.recv_frame(frames::go_away(0));
//.close();
let h2 = client::handshake(io)
.expect("handshake")
.and_then(|(mut client, h2)| {
let request = Request::builder()
.method(Method::GET)
.uri("https://example.com/")
.body(())
.unwrap();
let req1 = client.send_request(request, true)
.unwrap()
.0.expect("response")
.and_then(|resp| {
assert_eq!(resp.status(), StatusCode::OK);
let body = resp.into_parts().1;
body.concat2().expect("body")
})
.and_then(|buf| {
assert_eq!(buf.len(), 16_384);
Ok(())
});
// this request will trigger a goaway
let request = Request::builder()
.method(Method::GET)
.uri("https://example.com/")
.body(())
.unwrap();
let req2 = client.send_request(request, true)
.unwrap()
.0.then(|res| {
let err = res.unwrap_err();
assert_eq!(err.to_string(), "protocol error: not a result of an error");
Ok::<(), ()>(())
});
h2.expect("client").join3(req1, req2)
});
h2.join(srv).wait().expect("wait");
}
#[test]
fn recv_next_stream_id_updated_by_malformed_headers() {
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()
// bad headers -- should error.
.send_frame(bad_headers)
.recv_frame(frames::reset(1).protocol_error())
// this frame is good, but the stream id should already have been incr'd
.send_frame(frames::headers(1)
.request("GET", "https://example.com/")
.eos())
.recv_frame(frames::go_away(1).protocol_error())
.close();
let srv = server::handshake(io)
.expect("handshake")
.and_then(|srv| srv.into_future().then(|res| {
let (err, _) = res.unwrap_err();
assert_eq!(
err.to_string(),
"protocol error: unspecific protocol error detected"
);
Ok::<(), ()>(())
})
);
srv.join(client).wait().expect("wait");
}
#[test]
fn skipped_stream_ids_are_implicitly_closed() {
let _ = ::env_logger::init();
let (io, srv) = mock::new();
let srv = srv
.assert_client_handshake()
.expect("handshake")
.recv_settings()
.recv_frame(frames::headers(5)
.request("GET", "https://example.com/")
.eos(),
)
// send the response on a lower-numbered stream, which should be
// implicitly closed.
.send_frame(frames::headers(3).response(200));
let h2 = client::Builder::new()
.initial_stream_id(5)
.handshake::<_, Bytes>(io)
.expect("handshake")
.and_then(|(mut client, h2)| {
let request = Request::builder()
.method(Method::GET)
.uri("https://example.com/")
.body(())
.unwrap();
let req = client.send_request(request, true)
.unwrap()
.0.then(|res| {
let err = res.unwrap_err();
assert_eq!(
err.to_string(),
"protocol error: unspecific protocol error detected");
Ok::<(), ()>(())
});
// client should see a conn error
let conn = h2.then(|res| {
let err = res.unwrap_err();
assert_eq!(
err.to_string(),
"protocol error: unspecific protocol error detected"
);
Ok::<(), ()>(())
});
conn.unwrap().join(req)
});
h2.join(srv).wait().expect("wait");
}
#[test]
fn send_rst_stream_allows_recv_data() {
let _ = ::env_logger::init();
let (io, srv) = mock::new();
let srv = srv.assert_client_handshake()
.unwrap()
.recv_settings()
.recv_frame(
frames::headers(1)
.request("GET", "https://example.com/")
.eos(),
)
.send_frame(frames::headers(1).response(200))
.recv_frame(frames::reset(1).cancel())
// sending frames after canceled!
// note: sending 2 to cosume 50% of connection window
.send_frame(frames::data(1, vec![0; 16_384]))
.send_frame(frames::data(1, vec![0; 16_384]).eos())
// make sure we automatically free the connection window
.recv_frame(frames::window_update(0, 16_384 * 2))
// do a pingpong to ensure no other frames were sent
.ping_pong([1; 8])
.close();
let client = client::handshake(io)
.expect("handshake")
.and_then(|(mut client, conn)| {
let request = Request::builder()
.method(Method::GET)
.uri("https://example.com/")
.body(())
.unwrap();
let req = client.send_request(request, true)
.unwrap()
.0.expect("response")
.and_then(|resp| {
assert_eq!(resp.status(), StatusCode::OK);
// drop resp will send a reset
Ok(())
});
conn.expect("client")
.drive(req)
.and_then(|(conn, _)| conn)
});
client.join(srv).wait().expect("wait");
}
#[test]
fn send_rst_stream_allows_recv_trailers() {
let _ = ::env_logger::init();
let (io, srv) = mock::new();
let srv = srv.assert_client_handshake()
.unwrap()
.recv_settings()
.recv_frame(
frames::headers(1)
.request("GET", "https://example.com/")
.eos(),
)
.send_frame(frames::headers(1).response(200))
.send_frame(frames::data(1, vec![0; 16_384]))
.recv_frame(frames::reset(1).cancel())
// sending frames after canceled!
.send_frame(frames::headers(1).field("foo", "bar").eos())
// do a pingpong to ensure no other frames were sent
.ping_pong([1; 8])
.close();
let client = client::handshake(io)
.expect("handshake")
.and_then(|(mut client, conn)| {
let request = Request::builder()
.method(Method::GET)
.uri("https://example.com/")
.body(())
.unwrap();
let req = client.send_request(request, true)
.unwrap()
.0.expect("response")
.and_then(|resp| {
assert_eq!(resp.status(), StatusCode::OK);
// drop resp will send a reset
Ok(())
});
conn.expect("client")
.drive(req)
.and_then(|(conn, _)| conn)
});
client.join(srv).wait().expect("wait");
}
#[test]
fn rst_stream_expires() {
let _ = ::env_logger::init();
let (io, srv) = mock::new();
let srv = srv.assert_client_handshake()
.unwrap()
.recv_settings()
.recv_frame(
frames::headers(1)
.request("GET", "https://example.com/")
.eos(),
)
.send_frame(frames::headers(1).response(200))
.send_frame(frames::data(1, vec![0; 16_384]))
.recv_frame(frames::reset(1).cancel())
// wait till after the configured duration
.idle_ms(15)
.ping_pong([1; 8])
// sending frame after canceled!
.send_frame(frames::data(1, vec![0; 16_384]).eos())
.recv_frame(frames::go_away(0).protocol_error())
.close();
let client = client::Builder::new()
.reset_stream_duration(Duration::from_millis(10))
.handshake::<_, Bytes>(io)
.expect("handshake")
.and_then(|(mut client, conn)| {
let request = Request::builder()
.method(Method::GET)
.uri("https://example.com/")
.body(())
.unwrap();
let req = client.send_request(request, true)
.unwrap()
.0.expect("response")
.and_then(|resp| {
assert_eq!(resp.status(), StatusCode::OK);
// drop resp will send a reset
Ok(())
})
.map_err(|()| -> Error {
unreachable!()
});
conn.drive(req)
.and_then(|(conn, _)| conn.expect_err("client"))
.map(|err| {
assert_eq!(
err.to_string(),
"protocol error: unspecific protocol error detected"
);
drop(client);
})
});
client.join(srv).wait().expect("wait");
}
#[test]
fn rst_stream_max() {
let _ = ::env_logger::init();
let (io, srv) = mock::new();
let srv = srv.assert_client_handshake()
.unwrap()
.recv_settings()
.recv_frame(
frames::headers(1)
.request("GET", "https://example.com/")
.eos(),
)
.recv_frame(
frames::headers(3)
.request("GET", "https://example.com/")
.eos(),
)
.send_frame(frames::headers(1).response(200))
.send_frame(frames::data(1, vec![0; 16]))
.send_frame(frames::headers(3).response(200))
.send_frame(frames::data(3, vec![0; 16]))
.recv_frame(frames::reset(1).cancel())
.recv_frame(frames::reset(3).cancel())
// sending frame after canceled!
// newer streams trump older streams
// 3 is still being ignored
.send_frame(frames::data(3, vec![0; 16]).eos())
// ping pong to be sure of no goaway
.ping_pong([1; 8])
// 1 has been evicted, will get a goaway
.send_frame(frames::data(1, vec![0; 16]).eos())
.recv_frame(frames::go_away(0).protocol_error())
.close();
let client = client::Builder::new()
.max_concurrent_reset_streams(1)
.handshake::<_, Bytes>(io)
.expect("handshake")
.and_then(|(mut client, conn)| {
let request = Request::builder()
.method(Method::GET)
.uri("https://example.com/")
.body(())
.unwrap();
let req1 = client.send_request(request, true)
.unwrap()
.0.expect("response1")
.and_then(|resp| {
assert_eq!(resp.status(), StatusCode::OK);
// drop resp will send a reset
Ok(())
})
.map_err(|()| -> Error {
unreachable!()
});
let request = Request::builder()
.method(Method::GET)
.uri("https://example.com/")
.body(())
.unwrap();
let req2 = client.send_request(request, true)
.unwrap()
.0.expect("response2")
.and_then(|resp| {
assert_eq!(resp.status(), StatusCode::OK);
// drop resp will send a reset
Ok(())
})
.map_err(|()| -> Error {
unreachable!()
});
conn.drive(req1.join(req2))
.and_then(|(conn, _)| conn.expect_err("client"))
.map(|err| {
assert_eq!(
err.to_string(),
"protocol error: unspecific protocol error detected"
);
})
});
client.join(srv).wait().expect("wait");
}
#[test]
fn reserved_state_recv_window_update() {
let _ = ::env_logger::init();
let (io, srv) = mock::new();
let srv = srv.assert_client_handshake()
.unwrap()
.recv_settings()
.recv_frame(
frames::headers(1)
.request("GET", "https://example.com/")
.eos(),
)
.send_frame(
frames::push_promise(1, 2)
.request("GET", "https://example.com/push")
)
// it'd be weird to send a window update on a push promise,
// since the client can't send us data, but whatever. The
// point is that it's allowed, so we're testing it.
.send_frame(frames::window_update(2, 128))
.send_frame(frames::headers(1).response(200).eos())
// ping pong to ensure no goaway
.ping_pong([1; 8])
.close();
let client = client::handshake(io)
.expect("handshake")
.and_then(|(mut client, conn)| {
let request = Request::builder()
.method(Method::GET)
.uri("https://example.com/")
.body(())
.unwrap();
let req = client.send_request(request, true)
.unwrap()
.0.expect("response")
.and_then(|resp| {
assert_eq!(resp.status(), StatusCode::OK);
Ok(())
});
conn.drive(req)
.and_then(|(conn, _)| conn.expect("client"))
});
client.join(srv).wait().expect("wait");
}
/*
#[test]
fn send_data_after_headers_eos() {
let _ = env_logger::init();
let mock = mock_io::Builder::new()
.handshake()
// Write GET /
.write(&[
// GET /, no EOS
0, 0, 16, 1, 5, 0, 0, 0, 1, 131, 135, 65, 139, 157, 41, 172,
75, 143, 168, 233, 25, 151, 33, 233, 132
])
.build();
let h2 = client::handshake(mock)
.wait().expect("handshake");
// Send the request
let mut request = request::Head::default();
request.method = Method::POST;
request.uri = "https://http2.akamai.com/".parse().unwrap();
let id = 1.into();
let h2 = h2.send_request(id, request, true).wait().expect("send request");
let body = "hello";
// Send the data
let err = h2.send_data(id, body.into(), true).wait().unwrap_err();
assert_user_err!(err, UnexpectedFrameType);
}
#[test]
#[ignore]
fn exceed_max_streams() {
}
*/