use rama::{
Layer, Service,
graceful::Shutdown,
http::{
Body, HeaderValue, Request, Response, Version, client::EasyHttpWebClient, header,
header::HOST, server::HttpServer,
},
net::test_utils::client::MockConnectorService,
rt::Executor,
service::service_fn,
tls::boring::server::TlsAcceptorLayer,
tls::client::TlsClientConfig,
tls::server::{SelfSignedData, TlsServerConfig},
};
use rama_tls::client::ServerVerifyMode;
use tokio::time::sleep;
use tokio_util::sync::CancellationToken;
use std::{convert::Infallible, time::Duration};
#[tokio::test]
async fn h2_with_connection_pooling() {
let http_server =
HttpServer::new_h2(Executor::default()).service(service_fn(async |req: Request| {
assert_eq!(req.headers().get(HOST), None);
Ok::<_, Infallible>(Response::new(Body::empty()))
}));
let tls_service_data = TlsServerConfig::new()
.try_with_self_signed(SelfSignedData {
organisation_name: Some("Example Server Acceptor".to_owned()),
..Default::default()
})
.expect("self-signed")
.with_alpn_http_2();
let server = TlsAcceptorLayer::new(tls_service_data).into_layer(http_server);
let direct_connection = MockConnectorService::new(move || server.clone());
let tls_config = TlsClientConfig::default_http().with_server_verify(ServerVerifyMode::Disable);
let client = EasyHttpWebClient::connector_builder()
.with_custom_transport_connector(direct_connection)
.without_dns_connector()
.without_tls_proxy_support()
.without_proxy_support()
.with_tls_support_using_boringssl(tls_config)
.with_default_http_connector(Executor::default())
.try_with_default_connection_pool()
.unwrap()
.build_client();
let create_req = || {
Request::builder()
.uri("https://localhost/test")
.body(Body::empty())
.unwrap()
};
let _resp = client.serve(create_req()).await.unwrap();
let _resp = client.serve(create_req()).await.unwrap();
}
#[tokio::test]
async fn h1_with_connection_pooling_detects_closed_connections() {
let http_server =
HttpServer::new_http1(Executor::default()).service(service_fn(async |_req: Request| {
let mut resp = Response::new(Body::empty());
resp.headers_mut()
.insert(header::CONNECTION, HeaderValue::from_static("close"));
Ok::<_, Infallible>(resp)
}));
let tls_service_data = TlsServerConfig::new()
.try_with_self_signed(SelfSignedData {
organisation_name: Some("Example Server Acceptor".to_owned()),
..Default::default()
})
.expect("self-signed")
.with_alpn_http_1();
let server = TlsAcceptorLayer::new(tls_service_data).into_layer(http_server);
let direct_connection = MockConnectorService::new(move || server.clone());
let tls_config = TlsClientConfig::default_http()
.with_server_verify(rama_tls::client::ServerVerifyMode::Disable);
let client = EasyHttpWebClient::connector_builder()
.with_custom_transport_connector(direct_connection)
.without_dns_connector()
.without_tls_proxy_support()
.without_proxy_support()
.with_tls_support_using_boringssl(tls_config)
.with_default_http_connector(Executor::default())
.try_with_default_connection_pool()
.unwrap()
.build_client();
let create_req = || {
Request::builder()
.uri("https://localhost/test")
.body(Body::empty())
.unwrap()
};
let _resp = client.serve(create_req()).await.unwrap();
let _resp = client.serve(create_req()).await.unwrap();
}
async fn connection_pooling_detects_closed_connections(version: Version, delay: Option<Duration>) {
let direct_connection = MockConnectorService::new(move || {
let token = CancellationToken::new();
let shutdown = Shutdown::new(token.clone().cancelled_owned());
let executor = Executor::graceful(shutdown.guard());
let http_server = HttpServer::auto(executor).service(service_fn(move |_req: Request| {
let token = token.clone();
tokio::spawn(async move {
if let Some(delay) = delay {
sleep(delay).await;
}
token.cancel();
});
async move {
let resp = Response::new(Body::empty());
Ok::<_, Infallible>(resp)
}
}));
let tls_service_data = {
let tls = TlsServerConfig::new()
.try_with_self_signed(SelfSignedData {
organisation_name: Some("Example Server Acceptor".to_owned()),
..Default::default()
})
.expect("self-signed");
match version {
Version::HTTP_11 => tls.with_alpn_http_1(),
Version::HTTP_2 => tls.with_alpn_http_2(),
_ => panic!("not supported by this test"),
}
};
TlsAcceptorLayer::new(tls_service_data).into_layer(http_server)
});
let tls_config = TlsClientConfig::default_http()
.with_server_verify(rama_tls::client::ServerVerifyMode::Disable);
let client = EasyHttpWebClient::connector_builder()
.with_custom_transport_connector(direct_connection)
.without_dns_connector()
.without_tls_proxy_support()
.without_proxy_support()
.with_tls_support_using_boringssl(tls_config)
.with_default_http_connector(Executor::default())
.try_with_default_connection_pool()
.unwrap()
.build_client();
let create_req = || {
Request::builder()
.uri("https://localhost/test")
.body(Body::empty())
.unwrap()
};
let _resp = client.serve(create_req()).await.unwrap();
sleep(Duration::from_millis(100)).await;
if let Some(delay) = delay {
sleep(delay).await;
}
let _resp = client.serve(create_req()).await.unwrap();
}
#[tokio::test]
async fn h1_with_connection_pooling_detects_instant_close() {
connection_pooling_detects_closed_connections(Version::HTTP_11, None).await;
}
#[tokio::test]
async fn h1_with_connection_pooling_detects_late_close() {
connection_pooling_detects_closed_connections(
Version::HTTP_11,
Some(Duration::from_millis(50)),
)
.await;
}
#[tokio::test]
async fn h2_with_connection_pooling_detects_instant_goaway() {
connection_pooling_detects_closed_connections(Version::HTTP_2, None).await;
}
#[tokio::test]
async fn h2_with_connection_pooling_detects_late_goaway() {
connection_pooling_detects_closed_connections(Version::HTTP_2, Some(Duration::from_millis(50)))
.await;
}