207 lines
8.0 KiB
Rust
207 lines
8.0 KiB
Rust
use super::*;
|
|
use http_body_util::{BodyExt, Full};
|
|
use hyper_util::rt::{TokioExecutor, TokioIo};
|
|
use tokio::sync::Notify;
|
|
|
|
fn limits() -> Limits {
|
|
Limits {
|
|
http2: Some(http::Http2Limits::default()),
|
|
..Limits::default()
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn idle_http2_connection_releases_capacity_after_response() {
|
|
runtime()
|
|
.run(async |scope| {
|
|
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
|
|
let port = listener.port().unwrap();
|
|
let incoming = listener.clone();
|
|
let limits = Limits {
|
|
connections: 1,
|
|
timeout_ms: 50,
|
|
..limits()
|
|
};
|
|
let serving = limits.clone();
|
|
let server = scope
|
|
.spawn(move |child| async move {
|
|
http::serve::<_, _, false>(&incoming, &serving, &child, route).await
|
|
})
|
|
.unwrap();
|
|
let stream = tokio::net::TcpStream::connect(("127.0.0.1", port as u16))
|
|
.await
|
|
.unwrap();
|
|
let (mut sender, connection) =
|
|
hyper::client::conn::http2::Builder::new(TokioExecutor::new())
|
|
.handshake::<_, Full<bytes::Bytes>>(TokioIo::new(stream))
|
|
.await
|
|
.unwrap();
|
|
let driver = scope.spawn_leaf(move || connection).unwrap();
|
|
let response = sender
|
|
.send_request(
|
|
hyper::Request::builder()
|
|
.uri("http://fixture.test/echo")
|
|
.body(Full::new(bytes::Bytes::from_static(b"first")))
|
|
.unwrap(),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(
|
|
response.into_body().collect().await.unwrap().to_bytes(),
|
|
b"first"[..]
|
|
);
|
|
tokio::time::timeout(Duration::from_secs(2), driver.wait())
|
|
.await
|
|
.unwrap()
|
|
.unwrap()
|
|
.unwrap();
|
|
assert_eq!(
|
|
http::send("127.0.0.1", port, request("/echo", b"second"), &limits)
|
|
.await
|
|
.unwrap()
|
|
.body
|
|
.as_ref(),
|
|
b"second"
|
|
);
|
|
listener.close().unwrap();
|
|
server.wait().await.unwrap().unwrap();
|
|
})
|
|
.unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn http2_single_request_preserves_body_headers_and_validates_protocol_options() {
|
|
runtime()
|
|
.run(async |scope| {
|
|
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
|
|
let port = listener.port().unwrap();
|
|
let incoming = listener.clone();
|
|
let server = scope
|
|
.spawn(move |child| async move {
|
|
http::serve::<_, _, false>(&incoming, &limits(), &child, route).await
|
|
})
|
|
.unwrap();
|
|
let response = http::send("127.0.0.1", port, request("/echo", b"http2"), &limits())
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.body.as_ref(), b"http2");
|
|
let headers = response
|
|
.headers
|
|
.iter()
|
|
.filter(|header| header.name == "x-data")
|
|
.collect::<Vec<_>>();
|
|
assert_eq!(headers.len(), 2);
|
|
assert_eq!(headers[0].value.as_ref(), [0x80]);
|
|
for header in ["Connection", "Keep-Alive", "Proxy-Connection", "TE"] {
|
|
let mut message = request("/", b"");
|
|
message.headers.push(Header {
|
|
name: header.into(),
|
|
value: b"invalid".to_vec().into(),
|
|
});
|
|
assert!(
|
|
http::send("127.0.0.1", port, message, &limits())
|
|
.await
|
|
.unwrap_err()
|
|
.contains("connection header")
|
|
);
|
|
}
|
|
listener.close().unwrap();
|
|
server.wait().await.unwrap().unwrap();
|
|
})
|
|
.unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn http2_multiplexes_independent_streams_normalizes_authority_and_drains_goaway() {
|
|
for threads in [1, 2] {
|
|
let runtime = Runtime::new(dever_runtime::task::Limits {
|
|
threads,
|
|
workers: 2,
|
|
tasks: 32,
|
|
})
|
|
.unwrap();
|
|
runtime.run(async |scope| {
|
|
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
|
|
let port = listener.port().unwrap();
|
|
let incoming = listener.clone();
|
|
let started = Arc::new(Notify::new());
|
|
let ready = started.clone();
|
|
let release = Arc::new(Notify::new());
|
|
let released = release.clone();
|
|
let server = scope.spawn(move |child| async move {
|
|
http::serve::<_, _, false>(&incoming, &limits(), &child, move |request, _| {
|
|
let ready = ready.clone();
|
|
let released = released.clone();
|
|
async move {
|
|
assert!(request.headers.iter().any(|header| header.name == "host" && header.value.as_ref() == b"fixture.test"));
|
|
if request.target == "/slow" {
|
|
ready.notify_one();
|
|
released.notified().await;
|
|
}
|
|
Ok(Response { status: 200, headers: vec![], body: request.target.into_bytes().into() })
|
|
}
|
|
}).await
|
|
}).unwrap();
|
|
let stream = tokio::net::TcpStream::connect(("127.0.0.1", port as u16)).await.unwrap();
|
|
let (mut sender, connection) = hyper::client::conn::http2::Builder::new(TokioExecutor::new())
|
|
.handshake::<_, Full<bytes::Bytes>>(TokioIo::new(stream)).await.unwrap();
|
|
let driver = scope.spawn_leaf(move || connection).unwrap();
|
|
let message = |path: &str| hyper::Request::builder().uri(format!("http://fixture.test{path}"))
|
|
.body(Full::new(bytes::Bytes::new())).unwrap();
|
|
let slow = sender.send_request(message("/slow"));
|
|
tokio::pin!(slow);
|
|
tokio::select! {
|
|
result = &mut slow => panic!("slow stream finished before release: {result:?}"),
|
|
() = started.notified() => (),
|
|
() = tokio::time::sleep(Duration::from_secs(3)) => panic!("slow stream did not start"),
|
|
}
|
|
let fast = tokio::time::timeout(Duration::from_secs(3), sender.send_request(message("/fast"))).await.unwrap().unwrap();
|
|
assert_eq!(fast.into_body().collect().await.unwrap().to_bytes(), b"/fast"[..]);
|
|
listener.close().unwrap();
|
|
release.notify_one();
|
|
let slow = slow.await.unwrap();
|
|
assert_eq!(slow.into_body().collect().await.unwrap().to_bytes(), b"/slow"[..]);
|
|
drop(sender);
|
|
tokio::time::timeout(Duration::from_secs(3), server.wait()).await.unwrap().unwrap().unwrap();
|
|
driver.stop().await.unwrap();
|
|
}).unwrap();
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn http2_limits_reject_invalid_windows_and_total_stream_capacity() {
|
|
runtime()
|
|
.run(async |_| {
|
|
for http2 in [
|
|
http::Http2Limits {
|
|
streams: 2049,
|
|
..http::Http2Limits::default()
|
|
},
|
|
http::Http2Limits {
|
|
streams: 0,
|
|
..http::Http2Limits::default()
|
|
},
|
|
http::Http2Limits {
|
|
stream_window_bytes: 0,
|
|
..http::Http2Limits::default()
|
|
},
|
|
http::Http2Limits {
|
|
connection_window_bytes: 65534,
|
|
..http::Http2Limits::default()
|
|
},
|
|
] {
|
|
let limits = Limits {
|
|
http2: Some(http2),
|
|
..Limits::default()
|
|
};
|
|
assert!(
|
|
http::send("127.0.0.1", 1, request("/", b""), &limits)
|
|
.await
|
|
.unwrap_err()
|
|
.contains("HTTP/2 limits")
|
|
);
|
|
}
|
|
})
|
|
.unwrap();
|
|
}
|