1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3#,"
11)]
12#, [`HttpsConnector`](hyper_tls::HttpsConnector),"
18)]
19#, [`client::RustlsConnector`],"
25)]
26#, [`HttpsConnector`](hyper_tls::HttpsConnector), [`client::RustlsConnector`],"
29)]
30# accepts only such `Service`s."
120)]
121#![cfg_attr(
123 feature = "axum",
124 doc = "The [`ProxyError`] type implements [`IntoResponse`](axum::response::IntoResponse) if you enable the \
125 `axum` feature. \
126 It returns an empty body, with the status code `INTERNAL_SERVER_ERROR`. The description of this \
127 error will be logged out with [`tracing::event!`] at the [`tracing::Level::ERROR`] level in the \
128 [`IntoResponse::into_response`](axum::response::IntoResponse::into_response) method. \
129"
130)]
131# for [`ProxyError`]"
148)]
149mod error;
156pub use error::ProxyError;
157
158#[cfg(any(feature = "http1", feature = "http2"))]
159#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]
160pub mod client;
161
162pub mod rewrite;
163pub use rewrite::*;
164
165mod future;
166pub use future::RevProxyFuture;
167
168#[cfg(any(feature = "http1", feature = "http2"))]
169mod oneshot;
170#[cfg(any(feature = "http1", feature = "http2"))]
171#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]
172pub use oneshot::OneshotService;
173
174#[cfg(any(feature = "http1", feature = "http2"))]
175mod reused;
176#[cfg(any(feature = "http1", feature = "http2"))]
177#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]
178pub use reused::Builder as ReusedServiceBuilder;
179#[cfg(any(feature = "http1", feature = "http2"))]
180#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]
181pub use reused::ReusedService;
182#[cfg(all(
183 any(feature = "http1", feature = "http2"),
184 any(feature = "https", feature = "nativetls")
185))]
186#[cfg_attr(
187 docsrs,
188 doc(cfg(all(
189 any(feature = "http1", feature = "http2"),
190 any(feature = "https", feature = "nativetls")
191 )))
192)]
193pub use reused::builder_https;
194#[cfg(all(any(feature = "http1", feature = "http2"), feature = "nativetls"))]
195#[cfg_attr(
196 docsrs,
197 doc(cfg(all(any(feature = "http1", feature = "http2"), feature = "nativetls")))
198)]
199pub use reused::builder_nativetls;
200#[cfg(all(any(feature = "http1", feature = "http2"), feature = "__rustls"))]
201#[cfg_attr(
202 docsrs,
203 doc(cfg(all(any(feature = "http1", feature = "http2"), feature = "rustls")))
204)]
205pub use reused::builder_rustls;
206#[cfg(any(feature = "http1", feature = "http2"))]
207#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]
208pub use reused::{builder, builder_http};
209
210#[cfg(not(feature = "http1"))]
211compile_error!("http1 is a mandatory feature");
212
213#[cfg(all(
214 any(feature = "rustls-ring", feature = "rustls-aws-lc"),
215 not(any(feature = "rustls-webpki-roots", feature = "rustls-native-roots"))
216))]
217compile_error!(
218 "When enabling rustls-ring and/or rustls-aws-lc, you must enable rustls-webpki-roots and/or rustls-native-roots"
219);
220
221#[cfg(test)]
222mod test_helper {
223 use std::convert::Infallible;
224
225 use http::{Request, Response, StatusCode};
226 use http_body_util::BodyExt as _;
227 use hyper::body::Incoming;
228 use mockito::{Matcher, ServerGuard};
229 use pretty_assertions::assert_eq;
230 use tower_service::Service;
231
232 use super::{ProxyError, RevProxyFuture};
233
234 async fn call<S, B>(
235 service: &mut S,
236 (method, suffix, content_type, body): (&str, &str, Option<&str>, B),
237 expected: (StatusCode, &str),
238 ) where
239 S: Service<
240 Request<String>,
241 Response = Result<Response<Incoming>, ProxyError>,
242 Error = Infallible,
243 Future = RevProxyFuture,
244 >,
245 B: Into<String>,
246 {
247 let mut builder = Request::builder()
248 .method(method)
249 .uri(format!("https://test.com{}", suffix));
250
251 if let Some(content_type) = content_type {
252 builder = builder.header("Content-Type", content_type);
253 }
254
255 let request = builder.body(body.into()).unwrap();
256
257 let result = service.call(request).await.unwrap();
258 assert!(result.is_ok());
259
260 let response = result.unwrap();
261 assert_eq!(response.status(), expected.0);
262
263 let body = response.into_body().collect().await;
264 assert!(body.is_ok());
265
266 assert_eq!(body.unwrap().to_bytes(), expected.1);
267 }
268
269 pub async fn match_path<S>(server: &mut ServerGuard, svc: &mut S)
270 where
271 S: Service<
272 Request<String>,
273 Response = Result<Response<Incoming>, ProxyError>,
274 Error = Infallible,
275 Future = RevProxyFuture,
276 >,
277 {
278 let _mk = server
279 .mock("GET", "/goo/bar/goo/baz/goo")
280 .with_body("ok")
281 .create_async()
282 .await;
283
284 call(
285 svc,
286 ("GET", "/foo/bar/foo/baz/foo", None, ""),
287 (StatusCode::OK, "ok"),
288 )
289 .await;
290
291 call(
292 svc,
293 ("GET", "/foo/bar/foo/baz", None, ""),
294 (StatusCode::NOT_IMPLEMENTED, ""),
295 )
296 .await;
297 }
298
299 pub async fn match_query<S>(server: &mut ServerGuard, svc: &mut S)
300 where
301 S: Service<
302 Request<String>,
303 Response = Result<Response<Incoming>, ProxyError>,
304 Error = Infallible,
305 Future = RevProxyFuture,
306 >,
307 {
308 let _mk = server
309 .mock("GET", "/goo")
310 .match_query(Matcher::UrlEncoded("greeting".into(), "good day".into()))
311 .with_body("ok")
312 .create_async()
313 .await;
314
315 call(
316 svc,
317 ("GET", "/foo?greeting=good%20day", None, ""),
318 (StatusCode::OK, "ok"),
319 )
320 .await;
321
322 call(
323 svc,
324 ("GET", "/foo", None, ""),
325 (StatusCode::NOT_IMPLEMENTED, ""),
326 )
327 .await;
328 }
329
330 pub async fn match_post<S>(server: &mut ServerGuard, svc: &mut S)
331 where
332 S: Service<
333 Request<String>,
334 Response = Result<Response<Incoming>, ProxyError>,
335 Error = Infallible,
336 Future = RevProxyFuture,
337 >,
338 {
339 let _mk = server
340 .mock("POST", "/goo")
341 .match_body("test")
342 .with_body("ok")
343 .create_async()
344 .await;
345
346 call(svc, ("POST", "/foo", None, "test"), (StatusCode::OK, "ok")).await;
347
348 call(
349 svc,
350 ("PUT", "/foo", None, "test"),
351 (StatusCode::NOT_IMPLEMENTED, ""),
352 )
353 .await;
354
355 call(
356 svc,
357 ("POST", "/foo", None, "tests"),
358 (StatusCode::NOT_IMPLEMENTED, ""),
359 )
360 .await;
361 }
362
363 pub async fn match_header<S>(server: &mut ServerGuard, svc: &mut S)
364 where
365 S: Service<
366 Request<String>,
367 Response = Result<Response<Incoming>, ProxyError>,
368 Error = Infallible,
369 Future = RevProxyFuture,
370 >,
371 {
372 let _mk = server
373 .mock("POST", "/goo")
374 .match_header("content-type", "application/json")
375 .match_body(r#"{"key":"value"}"#)
376 .with_body("ok")
377 .create_async()
378 .await;
379
380 call(
381 svc,
382 (
383 "POST",
384 "/foo",
385 Some("application/json"),
386 r#"{"key":"value"}"#,
387 ),
388 (StatusCode::OK, "ok"),
389 )
390 .await;
391
392 call(
393 svc,
394 ("POST", "/foo", None, r#"{"key":"value"}"#),
395 (StatusCode::NOT_IMPLEMENTED, ""),
396 )
397 .await;
398
399 call(
400 svc,
401 (
402 "POST",
403 "/foo",
404 Some("application/json"),
405 r#"{"key":"values"}"#,
406 ),
407 (StatusCode::NOT_IMPLEMENTED, ""),
408 )
409 .await;
410 }
411}