#![cfg_attr(docsrs, feature(doc_cfg))]
#,"
)]
#, [`HttpsConnector`](hyper_tls::HttpsConnector),"
)]
#, [`client::RustlsConnector`],"
)]
#, [`HttpsConnector`](hyper_tls::HttpsConnector), [`client::RustlsConnector`],"
)]
# accepts only such `Service`s."
)]
#![cfg_attr(
feature = "axum",
doc = "The [`ProxyError`] type implements [`IntoResponse`](axum::response::IntoResponse) if you enable the \
`axum` feature. \
It returns an empty body, with the status code `INTERNAL_SERVER_ERROR`. The description of this \
error will be logged out with [`tracing::event!`] at the [`tracing::Level::ERROR`] level in the \
[`IntoResponse::into_response`](axum::response::IntoResponse::into_response) method. \
"
)]
# for [`ProxyError`]"
)]
mod error;
pub use error::ProxyError;
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]
pub mod client;
pub mod rewrite;
pub use rewrite::*;
mod future;
pub use future::RevProxyFuture;
#[cfg(any(feature = "http1", feature = "http2"))]
mod oneshot;
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]
pub use oneshot::OneshotService;
#[cfg(any(feature = "http1", feature = "http2"))]
mod reused;
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]
pub use reused::Builder as ReusedServiceBuilder;
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]
pub use reused::ReusedService;
#[cfg(all(
any(feature = "http1", feature = "http2"),
any(feature = "https", feature = "nativetls")
))]
#[cfg_attr(
docsrs,
doc(cfg(all(
any(feature = "http1", feature = "http2"),
any(feature = "https", feature = "nativetls")
)))
)]
pub use reused::builder_https;
#[cfg(all(any(feature = "http1", feature = "http2"), feature = "nativetls"))]
#[cfg_attr(
docsrs,
doc(cfg(all(any(feature = "http1", feature = "http2"), feature = "nativetls")))
)]
pub use reused::builder_nativetls;
#[cfg(all(any(feature = "http1", feature = "http2"), feature = "__rustls"))]
#[cfg_attr(
docsrs,
doc(cfg(all(any(feature = "http1", feature = "http2"), feature = "rustls")))
)]
pub use reused::builder_rustls;
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]
pub use reused::{builder, builder_http};
#[cfg(not(feature = "http1"))]
compile_error!("http1 is a mandatory feature");
#[cfg(all(
any(feature = "rustls-ring", feature = "rustls-aws-lc"),
not(any(feature = "rustls-webpki-roots", feature = "rustls-native-roots"))
))]
compile_error!(
"When enabling rustls-ring and/or rustls-aws-lc, you must enable rustls-webpki-roots and/or rustls-native-roots"
);
#[cfg(test)]
mod test_helper {
use std::convert::Infallible;
use http::{Request, Response, StatusCode};
use http_body_util::BodyExt as _;
use hyper::body::Incoming;
use mockito::{Matcher, ServerGuard};
use pretty_assertions::assert_eq;
use tower_service::Service;
use super::{ProxyError, RevProxyFuture};
async fn call<S, B>(
service: &mut S,
(method, suffix, content_type, body): (&str, &str, Option<&str>, B),
expected: (StatusCode, &str),
) where
S: Service<
Request<String>,
Response = Result<Response<Incoming>, ProxyError>,
Error = Infallible,
Future = RevProxyFuture,
>,
B: Into<String>,
{
let mut builder = Request::builder()
.method(method)
.uri(format!("https://test.com{}", suffix));
if let Some(content_type) = content_type {
builder = builder.header("Content-Type", content_type);
}
let request = builder.body(body.into()).unwrap();
let result = service.call(request).await.unwrap();
assert!(result.is_ok());
let response = result.unwrap();
assert_eq!(response.status(), expected.0);
let body = response.into_body().collect().await;
assert!(body.is_ok());
assert_eq!(body.unwrap().to_bytes(), expected.1);
}
pub async fn match_path<S>(server: &mut ServerGuard, svc: &mut S)
where
S: Service<
Request<String>,
Response = Result<Response<Incoming>, ProxyError>,
Error = Infallible,
Future = RevProxyFuture,
>,
{
let _mk = server
.mock("GET", "/goo/bar/goo/baz/goo")
.with_body("ok")
.create_async()
.await;
call(
svc,
("GET", "/foo/bar/foo/baz/foo", None, ""),
(StatusCode::OK, "ok"),
)
.await;
call(
svc,
("GET", "/foo/bar/foo/baz", None, ""),
(StatusCode::NOT_IMPLEMENTED, ""),
)
.await;
}
pub async fn match_query<S>(server: &mut ServerGuard, svc: &mut S)
where
S: Service<
Request<String>,
Response = Result<Response<Incoming>, ProxyError>,
Error = Infallible,
Future = RevProxyFuture,
>,
{
let _mk = server
.mock("GET", "/goo")
.match_query(Matcher::UrlEncoded("greeting".into(), "good day".into()))
.with_body("ok")
.create_async()
.await;
call(
svc,
("GET", "/foo?greeting=good%20day", None, ""),
(StatusCode::OK, "ok"),
)
.await;
call(
svc,
("GET", "/foo", None, ""),
(StatusCode::NOT_IMPLEMENTED, ""),
)
.await;
}
pub async fn match_post<S>(server: &mut ServerGuard, svc: &mut S)
where
S: Service<
Request<String>,
Response = Result<Response<Incoming>, ProxyError>,
Error = Infallible,
Future = RevProxyFuture,
>,
{
let _mk = server
.mock("POST", "/goo")
.match_body("test")
.with_body("ok")
.create_async()
.await;
call(svc, ("POST", "/foo", None, "test"), (StatusCode::OK, "ok")).await;
call(
svc,
("PUT", "/foo", None, "test"),
(StatusCode::NOT_IMPLEMENTED, ""),
)
.await;
call(
svc,
("POST", "/foo", None, "tests"),
(StatusCode::NOT_IMPLEMENTED, ""),
)
.await;
}
pub async fn match_header<S>(server: &mut ServerGuard, svc: &mut S)
where
S: Service<
Request<String>,
Response = Result<Response<Incoming>, ProxyError>,
Error = Infallible,
Future = RevProxyFuture,
>,
{
let _mk = server
.mock("POST", "/goo")
.match_header("content-type", "application/json")
.match_body(r#"{"key":"value"}"#)
.with_body("ok")
.create_async()
.await;
call(
svc,
(
"POST",
"/foo",
Some("application/json"),
r#"{"key":"value"}"#,
),
(StatusCode::OK, "ok"),
)
.await;
call(
svc,
("POST", "/foo", None, r#"{"key":"value"}"#),
(StatusCode::NOT_IMPLEMENTED, ""),
)
.await;
call(
svc,
(
"POST",
"/foo",
Some("application/json"),
r#"{"key":"values"}"#,
),
(StatusCode::NOT_IMPLEMENTED, ""),
)
.await;
}
}