1use std::convert::Infallible;
2use std::sync::Arc;
3use std::task::{Context, Poll};
4
5use client::HttpConnector;
6#[cfg(feature = "__rustls")]
7use client::RustlsConnector;
8use http::uri::{Authority, Scheme};
9use http::{Error as HttpError, Request, Response};
10use hyper::body::{Body as HttpBody, Incoming};
11#[cfg(feature = "nativetls")]
12use hyper_tls::HttpsConnector as NativeTlsConnector;
13use hyper_util::client::legacy::Client;
14use hyper_util::client::legacy::connect::Connect;
15use tower_service::Service;
16
17use crate::future::RevProxyFuture;
18use crate::rewrite::PathRewriter;
19use crate::{ProxyError, client};
20
21type BoxErr = Box<dyn std::error::Error + Send + Sync>;
22
23#[derive(Debug)]
25pub struct Builder<C = HttpConnector, B = Incoming> {
26 client: Arc<Client<C, B>>,
27 scheme: Scheme,
28 authority: Authority,
29}
30
31impl<C, B> Clone for Builder<C, B> {
32 fn clone(&self) -> Self {
33 Self {
34 client: Arc::clone(&self.client),
35 scheme: self.scheme.clone(),
36 authority: self.authority.clone(),
37 }
38 }
39}
40
41impl<C, B> Builder<C, B> {
42 pub fn build<Pr>(&self, path: Pr) -> ReusedService<Pr, C, B> {
43 let Self {
44 client,
45 scheme,
46 authority,
47 } = Clone::clone(self);
48 ReusedService {
49 client,
50 scheme,
51 authority,
52 path,
53 }
54 }
55}
56
57pub fn builder_http<B, A>(authority: A) -> Result<Builder<HttpConnector, B>, HttpError>
65where
66 B: HttpBody + Send,
67 B::Data: Send,
68 Authority: TryFrom<A>,
69 <Authority as TryFrom<A>>::Error: Into<HttpError>,
70{
71 builder(client::http_default(), Scheme::HTTP, authority)
72}
73
74#[cfg(any(feature = "https", feature = "nativetls"))]
84#[cfg_attr(docsrs, doc(cfg(any(feature = "https", feature = "nativetls"))))]
85pub fn builder_https<B, A>(
86 authority: A,
87) -> Result<Builder<NativeTlsConnector<HttpConnector>, B>, HttpError>
88where
89 B: HttpBody + Send,
90 B::Data: Send,
91 Authority: TryFrom<A>,
92 <Authority as TryFrom<A>>::Error: Into<HttpError>,
93{
94 builder(client::https_default(), Scheme::HTTPS, authority)
95}
96
97#[cfg(feature = "nativetls")]
105#[cfg_attr(docsrs, doc(cfg(feature = "nativetls")))]
106pub fn builder_nativetls<B, A>(
107 authority: A,
108) -> Result<Builder<NativeTlsConnector<HttpConnector>, B>, HttpError>
109where
110 B: HttpBody + Send,
111 B::Data: Send,
112 Authority: TryFrom<A>,
113 <Authority as TryFrom<A>>::Error: Into<HttpError>,
114{
115 builder(client::nativetls_default(), Scheme::HTTPS, authority)
116}
117
118#[cfg(feature = "__rustls")]
126#[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
127pub fn builder_rustls<B, A>(
128 authority: A,
129) -> Result<Builder<RustlsConnector<HttpConnector>, B>, HttpError>
130where
131 B: HttpBody + Send,
132 B::Data: Send,
133 Authority: TryFrom<A>,
134 <Authority as TryFrom<A>>::Error: Into<HttpError>,
135{
136 builder(client::rustls_default(), Scheme::HTTPS, authority)
137}
138
139pub fn builder<C, B, S, A>(
148 client: Client<C, B>,
149 scheme: S,
150 authority: A,
151) -> Result<Builder<C, B>, HttpError>
152where
153 Scheme: TryFrom<S>,
154 <Scheme as TryFrom<S>>::Error: Into<HttpError>,
155 Authority: TryFrom<A>,
156 <Authority as TryFrom<A>>::Error: Into<HttpError>,
157{
158 let scheme = scheme.try_into().map_err(Into::into)?;
159 let authority = authority.try_into().map_err(Into::into)?;
160 Ok(Builder {
161 client: Arc::new(client),
162 scheme,
163 authority,
164 })
165}
166
167#[derive(Debug)]
198pub struct ReusedService<Pr, C, B = Incoming> {
199 client: Arc<Client<C, B>>,
200 scheme: Scheme,
201 authority: Authority,
202 path: Pr,
203}
204
205impl<Pr: Clone, C, B> Clone for ReusedService<Pr, C, B> {
206 #[inline]
207 fn clone(&self) -> Self {
208 Self {
209 client: Arc::clone(&self.client),
210 scheme: self.scheme.clone(),
211 authority: self.authority.clone(),
212 path: self.path.clone(),
213 }
214 }
215}
216
217impl<Pr, C, B> ReusedService<Pr, C, B> {
218 pub fn from<S, A>(
222 client: Arc<Client<C, B>>,
223 scheme: S,
224 authority: A,
225 path: Pr,
226 ) -> Result<Self, HttpError>
227 where
228 Scheme: TryFrom<S>,
229 <Scheme as TryFrom<S>>::Error: Into<HttpError>,
230 Authority: TryFrom<A>,
231 <Authority as TryFrom<A>>::Error: Into<HttpError>,
232 {
233 let scheme = scheme.try_into().map_err(Into::into)?;
234 let authority = authority.try_into().map_err(Into::into)?;
235 Ok(Self {
236 client,
237 scheme,
238 authority,
239 path,
240 })
241 }
242}
243
244impl<B, Pr> ReusedService<Pr, HttpConnector, B>
245where
246 B: HttpBody + Send,
247 B::Data: Send,
248{
249 pub fn with_http_client<A>(
253 client: Arc<Client<HttpConnector, B>>,
254 authority: A,
255 path: Pr,
256 ) -> Result<Self, HttpError>
257 where
258 Authority: TryFrom<A>,
259 <Authority as TryFrom<A>>::Error: Into<HttpError>,
260 {
261 let authority = authority.try_into().map_err(Into::into)?;
262 Ok(Self {
263 client,
264 scheme: Scheme::HTTP,
265 authority,
266 path,
267 })
268 }
269}
270
271#[cfg(feature = "nativetls")]
272impl<Pr, B> ReusedService<Pr, NativeTlsConnector<HttpConnector>, B>
273where
274 B: HttpBody + Send,
275 B::Data: Send,
276{
277 #[cfg_attr(docsrs, doc(cfg(any(feature = "https", feature = "nativetls"))))]
283 #[cfg(any(feature = "https", feature = "nativetls"))]
284 pub fn with_https_client<A>(
285 client: Arc<Client<NativeTlsConnector<HttpConnector>, B>>,
286 authority: A,
287 path: Pr,
288 ) -> Result<Self, HttpError>
289 where
290 Authority: TryFrom<A>,
291 <Authority as TryFrom<A>>::Error: Into<HttpError>,
292 {
293 let authority = authority.try_into().map_err(Into::into)?;
294 Ok(Self {
295 client,
296 scheme: Scheme::HTTPS,
297 authority,
298 path,
299 })
300 }
301
302 #[cfg_attr(docsrs, doc(cfg(feature = "nativetls")))]
307 #[cfg(feature = "nativetls")]
308 pub fn with_nativetls_client<A>(
309 client: Arc<Client<NativeTlsConnector<HttpConnector>, B>>,
310 authority: A,
311 path: Pr,
312 ) -> Result<Self, HttpError>
313 where
314 Authority: TryFrom<A>,
315 <Authority as TryFrom<A>>::Error: Into<HttpError>,
316 {
317 let authority = authority.try_into().map_err(Into::into)?;
318 Ok(Self {
319 client,
320 scheme: Scheme::HTTPS,
321 authority,
322 path,
323 })
324 }
325}
326
327#[cfg(feature = "__rustls")]
328impl<Pr, B> ReusedService<Pr, RustlsConnector<HttpConnector>, B>
329where
330 B: HttpBody + Send,
331 B::Data: Send,
332{
333 #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
338 pub fn with_rustls_client<A>(
339 client: Arc<Client<RustlsConnector<HttpConnector>, B>>,
340 authority: A,
341 path: Pr,
342 ) -> Result<Self, HttpError>
343 where
344 Authority: TryFrom<A>,
345 <Authority as TryFrom<A>>::Error: Into<HttpError>,
346 {
347 let authority = authority.try_into().map_err(Into::into)?;
348 Ok(Self {
349 client,
350 scheme: Scheme::HTTPS,
351 authority,
352 path,
353 })
354 }
355}
356
357impl<C, B, Pr> Service<Request<B>> for ReusedService<Pr, C, B>
358where
359 C: Connect + Clone + Send + Sync + 'static,
360 B: HttpBody + Send + 'static + Unpin,
361 B::Data: Send,
362 B::Error: Into<BoxErr>,
363 Pr: PathRewriter,
364{
365 type Response = Result<Response<Incoming>, ProxyError>;
366 type Error = Infallible;
367 type Future = RevProxyFuture;
368
369 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
370 Poll::Ready(Ok(()))
371 }
372
373 fn call(&mut self, req: Request<B>) -> Self::Future {
374 RevProxyFuture::new(
375 &self.client,
376 req,
377 &self.scheme,
378 &self.authority,
379 &mut self.path,
380 )
381 }
382}
383
384#[cfg(test)]
385mod test {
386 use http::uri::{Parts, Uri};
387 use mockito::ServerGuard;
388
389 use super::*;
390 use crate::{ReplaceAll, test_helper};
391
392 async fn make_svc() -> (
393 ServerGuard,
394 ReusedService<ReplaceAll<'static>, HttpConnector, String>,
395 ) {
396 let server = mockito::Server::new_async().await;
397 let uri = Uri::try_from(&server.url());
398 assert!(uri.is_ok());
399 let uri = uri.unwrap();
400
401 let Parts {
402 scheme, authority, ..
403 } = uri.into_parts();
404
405 let svc = ReusedService::from(
406 Arc::new(client::http_default()),
407 scheme.unwrap(),
408 authority.unwrap(),
409 ReplaceAll("foo", "goo"),
410 );
411 assert!(svc.is_ok());
412 (server, svc.unwrap())
413 }
414
415 #[tokio::test]
416 async fn match_path() {
417 let (mut server, mut svc) = make_svc().await;
418 test_helper::match_path(&mut server, &mut svc).await;
419 }
420
421 #[tokio::test]
422 async fn match_query() {
423 let (mut server, mut svc) = make_svc().await;
424 test_helper::match_query(&mut server, &mut svc).await;
425 }
426
427 #[tokio::test]
428 async fn match_post() {
429 let (mut server, mut svc) = make_svc().await;
430 test_helper::match_post(&mut server, &mut svc).await;
431 }
432
433 #[tokio::test]
434 async fn match_header() {
435 let (mut server, mut svc) = make_svc().await;
436 test_helper::match_header(&mut server, &mut svc).await;
437 }
438}