#![allow(dead_code)]
#[cfg(feature = "http-full")]
use rama::http::Body;
use rama::telemetry::tracing::{
self,
level_filters::LevelFilter,
subscriber::{self, EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt},
};
use std::{
process::{Child, ExitStatus, Output},
sync::Once,
};
#[cfg(feature = "http-full")]
use ::std::time::Duration;
#[cfg(feature = "http-full")]
use rama::{
Layer, Service,
error::BoxError,
http::StreamingBody,
http::client::proxy::layer::SetProxyAuthHttpHeaderLayer,
http::service::client::{HttpClientExt, IntoUrl, RequestBuilder},
http::ws::handshake::client::{HttpClientWebSocketExt, WebSocketRequestBuilder, WithService},
http::{
Request, Response,
client::EasyHttpWebClient,
layer::{
follow_redirect::FollowRedirectLayer,
required_header::AddRequiredRequestHeadersLayer,
retry::{ManagedPolicy, RetryLayer},
trace::TraceLayer,
},
},
layer::MapResultLayer,
service::BoxService,
utils::{backoff::ExponentialBackoff, rng::HasherRng},
};
#[cfg(all(feature = "http-full", feature = "compression"))]
use rama::http::layer::decompression::DecompressionLayer;
#[cfg(all(feature = "http-full", feature = "boring"))]
use rama::tls::client::{ServerVerifyMode, TlsClientConfig};
#[cfg(all(
feature = "http-full",
any(all(feature = "rustls", feature = "aws-lc"), feature = "boring")
))]
use rama::rt::Executor;
#[cfg(feature = "http-full")]
pub(super) type ClientService = BoxService<Request, Response, BoxError>;
pub(super) struct ExampleRunner {
pub(super) server_process: Child,
#[cfg(feature = "http-full")]
pub(super) client: ClientService,
#[cfg(not(feature = "http-full"))]
_phantom: std::marker::PhantomData<()>,
}
static INIT_TRACING_ONCE: Once = Once::new();
pub(super) fn init_tracing() {
INIT_TRACING_ONCE.call_once(|| {
_ = subscriber::registry()
.with(fmt::layer())
.with(
EnvFilter::builder()
.with_default_directive(LevelFilter::TRACE.into())
.from_env_lossy(),
)
.try_init();
});
}
impl ExampleRunner {
pub(super) fn interactive(
example_name: impl AsRef<str>,
extra_features: Option<&'static str>,
) -> Self {
Self::interactive_with_envs(example_name, extra_features, [])
}
pub(super) fn interactive_with_envs(
example_name: impl AsRef<str>,
extra_features: Option<&'static str>,
envs: impl IntoIterator<Item = (&'static str, &'static str)>,
) -> Self {
let child = escargot::CargoBuild::new()
.arg(format!(
"--features=cli,tcp,http-full,proxy-full,{}",
extra_features.unwrap_or_default()
))
.example(example_name.as_ref())
.manifest_path("Cargo.toml")
.target_dir("./target/")
.run()
.unwrap()
.command()
.env(
"RUST_LOG",
std::env::var("RUST_LOG").unwrap_or("trace".into()),
)
.env("SSLKEYLOGFILE", "./target/test_ssl_key_log.txt")
.envs(envs)
.spawn()
.unwrap();
#[cfg(not(feature = "http-full"))]
{
Self {
server_process: child,
_phantom: std::marker::PhantomData,
}
}
#[cfg(feature = "http-full")]
{
#[cfg(all(not(feature = "rustls"), not(feature = "boring")))]
let inner_client = EasyHttpWebClient::default();
#[cfg(feature = "boring")]
let inner_client = {
let tls_config = TlsClientConfig::default_http()
.with_server_verify(ServerVerifyMode::Disable)
.with_store_server_cert_chain(true);
let proxy_tls_config =
TlsClientConfig::new().with_server_verify(ServerVerifyMode::Disable);
EasyHttpWebClient::connector_builder()
.with_default_transport_connector()
.with_default_dns_connector()
.with_tls_proxy_support_using_boringssl_config(proxy_tls_config)
.with_proxy_support()
.with_tls_support_using_boringssl(tls_config)
.with_default_http_connector(Executor::default())
.build_client()
};
#[cfg(all(feature = "rustls", feature = "aws-lc", not(feature = "boring")))]
let inner_client = {
let tls_config = TlsClientConfig::default_http()
.with_server_verify(rama::tls::client::ServerVerifyMode::Disable)
.with_store_server_cert_chain(true);
let proxy_tls_config = TlsClientConfig::new()
.with_server_verify(rama::tls::client::ServerVerifyMode::Disable)
.with_keylog(rama::tls::KeyLogIntent::Environment);
EasyHttpWebClient::connector_builder()
.with_default_transport_connector()
.with_default_dns_connector()
.with_tls_proxy_support_using_rustls_config(proxy_tls_config)
.with_proxy_support()
.with_tls_support_using_rustls(tls_config)
.with_default_http_connector(Executor::default())
.build_client()
};
let client = (
MapResultLayer::new(map_internal_client_error),
TraceLayer::new_for_http(),
#[cfg(feature = "compression")]
DecompressionLayer::new(),
FollowRedirectLayer::default(),
RetryLayer::new(
ManagedPolicy::default().with_backoff(
ExponentialBackoff::new(
Duration::from_millis(100),
Duration::from_secs(60),
0.01,
HasherRng::default,
)
.unwrap(),
),
),
AddRequiredRequestHeadersLayer::default(),
SetProxyAuthHttpHeaderLayer::default(),
)
.into_layer(inner_client)
.boxed();
Self {
server_process: child,
client,
}
}
}
#[cfg(feature = "http-full")]
pub(super) fn set_client(&mut self, client: ClientService) {
self.client = client;
}
#[cfg(feature = "http-full")]
pub(super) fn get(&self, url: impl IntoUrl) -> RequestBuilder<'_, ClientService, Response> {
self.client.get(url)
}
#[cfg(feature = "http-full")]
pub(super) fn head(&self, url: impl IntoUrl) -> RequestBuilder<'_, ClientService, Response> {
self.client.head(url)
}
#[cfg(feature = "http-full")]
pub(super) fn post(&self, url: impl IntoUrl) -> RequestBuilder<'_, ClientService, Response> {
self.client.post(url)
}
#[cfg(feature = "http-full")]
pub(super) fn delete(&self, url: impl IntoUrl) -> RequestBuilder<'_, ClientService, Response> {
self.client.delete(url)
}
#[cfg(feature = "http-full")]
pub(super) fn websocket(
&self,
url: impl IntoUrl,
) -> WebSocketRequestBuilder<WithService<'_, ClientService, Body>> {
self.client.websocket(url)
}
#[cfg(feature = "http-full")]
pub(super) fn websocket_h2(
&self,
url: impl IntoUrl,
) -> WebSocketRequestBuilder<WithService<'_, ClientService, Body>> {
self.client.websocket_h2(url)
}
}
impl ExampleRunner {
pub(super) async fn run(example_name: impl AsRef<str>) -> ExitStatus {
let example_name = example_name.as_ref().to_owned();
tokio::task::spawn_blocking(|| {
escargot::CargoBuild::new()
.arg("--all-features")
.example(example_name)
.manifest_path("Cargo.toml")
.target_dir("./target/")
.run()
.unwrap()
.command()
.env(
"RUST_LOG",
std::env::var("RUST_LOG").unwrap_or("info".into()),
)
.status()
.unwrap()
})
.await
.unwrap()
}
pub(super) async fn run_with_args_output(
example_name: impl AsRef<str>,
args: impl IntoIterator<Item = impl AsRef<str>>,
) -> Output {
let example_name = example_name.as_ref().to_owned();
let args = args
.into_iter()
.map(|arg| arg.as_ref().to_owned())
.collect::<Vec<_>>();
tokio::task::spawn_blocking(move || {
let mut command = escargot::CargoBuild::new()
.arg("--all-features")
.example(example_name)
.manifest_path("Cargo.toml")
.target_dir("./target/")
.run()
.unwrap()
.command();
command.env(
"RUST_LOG",
std::env::var("RUST_LOG").unwrap_or("info".into()),
);
command.args(args);
command.output().unwrap()
})
.await
.unwrap()
}
}
impl std::ops::Drop for ExampleRunner {
fn drop(&mut self) {
tracing::info!("kill server process");
self.server_process.kill().expect("kill server process");
}
}
#[cfg(feature = "http-full")]
fn map_internal_client_error<E, Body>(
result: Result<Response<Body>, E>,
) -> Result<Response, rama::error::BoxError>
where
E: Into<rama::error::BoxError>,
Body: StreamingBody<Data = bytes::Bytes, Error: Into<BoxError>> + Send + Sync + 'static,
{
match result {
Ok(response) => Ok(response.map(rama::http::Body::new)),
Err(err) => Err(err.into()),
}
}