1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3mod error;
126pub use error::ProxyError;
127
128#[cfg(any(feature = "http1", feature = "http2"))]
129#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]
130pub mod client;
131
132pub mod rewrite;
133pub use rewrite::*;
134
135mod future;
136pub use future::RevProxyFuture;
137
138#[cfg(any(feature = "http1", feature = "http2"))]
139mod oneshot;
140#[cfg(any(feature = "http1", feature = "http2"))]
141#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]
142pub use oneshot::OneshotService;
143
144#[cfg(any(feature = "http1", feature = "http2"))]
145mod reused;
146#[cfg(any(feature = "http1", feature = "http2"))]
147#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]
148pub use reused::Builder as ReusedServiceBuilder;
149#[cfg(any(feature = "http1", feature = "http2"))]
150#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]
151pub use reused::ReusedService;
152#[cfg(all(
153 any(feature = "http1", feature = "http2"),
154 any(feature = "https", feature = "nativetls")
155))]
156#[cfg_attr(
157 docsrs,
158 doc(cfg(all(
159 any(feature = "http1", feature = "http2"),
160 any(feature = "https", feature = "nativetls")
161 )))
162)]
163pub use reused::builder_https;
164#[cfg(all(any(feature = "http1", feature = "http2"), feature = "nativetls"))]
165#[cfg_attr(
166 docsrs,
167 doc(cfg(all(any(feature = "http1", feature = "http2"), feature = "nativetls")))
168)]
169pub use reused::builder_nativetls;
170#[cfg(all(any(feature = "http1", feature = "http2"), feature = "__rustls"))]
171#[cfg_attr(
172 docsrs,
173 doc(cfg(all(any(feature = "http1", feature = "http2"), feature = "rustls")))
174)]
175pub use reused::builder_rustls;
176#[cfg(any(feature = "http1", feature = "http2"))]
177#[cfg_attr(docsrs, doc(cfg(any(feature = "http1", feature = "http2"))))]
178pub use reused::{builder, builder_http};
179
180#[cfg(not(feature = "http1"))]
181compile_error!("http1 is a mandatory feature");
182
183#[cfg(all(
184 any(feature = "rustls-ring", feature = "rustls-aws-lc"),
185 not(any(feature = "rustls-webpki-roots", feature = "rustls-native-roots"))
186))]
187compile_error!(
188 "When enabling rustls-ring and/or rustls-aws-lc, you must enable rustls-webpki-roots and/or rustls-native-roots"
189);
190
191#[cfg(test)]
192mod test_helper {
193 use std::convert::Infallible;
194
195 use http::{Request, Response, StatusCode};
196 use http_body_util::BodyExt as _;
197 use hyper::body::Incoming;
198 use mockito::{Matcher, ServerGuard};
199 use tower_service::Service;
200
201 use super::{ProxyError, RevProxyFuture};
202
203 async fn call<S, B>(
204 service: &mut S,
205 (method, suffix, content_type, body): (&str, &str, Option<&str>, B),
206 expected: (StatusCode, &str),
207 ) where
208 S: Service<
209 Request<String>,
210 Response = Result<Response<Incoming>, ProxyError>,
211 Error = Infallible,
212 Future = RevProxyFuture,
213 >,
214 B: Into<String>,
215 {
216 let mut builder = Request::builder()
217 .method(method)
218 .uri(format!("https://test.com{}", suffix));
219
220 if let Some(content_type) = content_type {
221 builder = builder.header("Content-Type", content_type);
222 }
223
224 let request = builder.body(body.into()).unwrap();
225
226 let result = service.call(request).await.unwrap();
227 assert!(result.is_ok());
228
229 let response = result.unwrap();
230 assert_eq!(response.status(), expected.0);
231
232 let body = response.into_body().collect().await;
233 assert!(body.is_ok());
234
235 assert_eq!(body.unwrap().to_bytes(), expected.1);
236 }
237
238 pub async fn match_path<S>(server: &mut ServerGuard, svc: &mut S)
239 where
240 S: Service<
241 Request<String>,
242 Response = Result<Response<Incoming>, ProxyError>,
243 Error = Infallible,
244 Future = RevProxyFuture,
245 >,
246 {
247 let _mk = server
248 .mock("GET", "/goo/bar/goo/baz/goo")
249 .with_body("ok")
250 .create_async()
251 .await;
252
253 call(
254 svc,
255 ("GET", "/foo/bar/foo/baz/foo", None, ""),
256 (StatusCode::OK, "ok"),
257 )
258 .await;
259
260 call(
261 svc,
262 ("GET", "/foo/bar/foo/baz", None, ""),
263 (StatusCode::NOT_IMPLEMENTED, ""),
264 )
265 .await;
266 }
267
268 pub async fn match_query<S>(server: &mut ServerGuard, svc: &mut S)
269 where
270 S: Service<
271 Request<String>,
272 Response = Result<Response<Incoming>, ProxyError>,
273 Error = Infallible,
274 Future = RevProxyFuture,
275 >,
276 {
277 let _mk = server
278 .mock("GET", "/goo")
279 .match_query(Matcher::UrlEncoded("greeting".into(), "good day".into()))
280 .with_body("ok")
281 .create_async()
282 .await;
283
284 call(
285 svc,
286 ("GET", "/foo?greeting=good%20day", None, ""),
287 (StatusCode::OK, "ok"),
288 )
289 .await;
290
291 call(
292 svc,
293 ("GET", "/foo", None, ""),
294 (StatusCode::NOT_IMPLEMENTED, ""),
295 )
296 .await;
297 }
298
299 pub async fn match_post<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("POST", "/goo")
310 .match_body("test")
311 .with_body("ok")
312 .create_async()
313 .await;
314
315 call(svc, ("POST", "/foo", None, "test"), (StatusCode::OK, "ok")).await;
316
317 call(
318 svc,
319 ("PUT", "/foo", None, "test"),
320 (StatusCode::NOT_IMPLEMENTED, ""),
321 )
322 .await;
323
324 call(
325 svc,
326 ("POST", "/foo", None, "tests"),
327 (StatusCode::NOT_IMPLEMENTED, ""),
328 )
329 .await;
330 }
331
332 pub async fn match_header<S>(server: &mut ServerGuard, svc: &mut S)
333 where
334 S: Service<
335 Request<String>,
336 Response = Result<Response<Incoming>, ProxyError>,
337 Error = Infallible,
338 Future = RevProxyFuture,
339 >,
340 {
341 let _mk = server
342 .mock("POST", "/goo")
343 .match_header("content-type", "application/json")
344 .match_body(r#"{"key":"value"}"#)
345 .with_body("ok")
346 .create_async()
347 .await;
348
349 call(
350 svc,
351 (
352 "POST",
353 "/foo",
354 Some("application/json"),
355 r#"{"key":"value"}"#,
356 ),
357 (StatusCode::OK, "ok"),
358 )
359 .await;
360
361 call(
362 svc,
363 ("POST", "/foo", None, r#"{"key":"value"}"#),
364 (StatusCode::NOT_IMPLEMENTED, ""),
365 )
366 .await;
367
368 call(
369 svc,
370 (
371 "POST",
372 "/foo",
373 Some("application/json"),
374 r#"{"key":"values"}"#,
375 ),
376 (StatusCode::NOT_IMPLEMENTED, ""),
377 )
378 .await;
379 }
380}