Skip to main content

tower_proxy/
reused.rs

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#[cfg_attr(
24    not(any(feature = "https", feature = "nativetls", feature = "__rustls")),
25    doc = "The return type of [`builder()`], [`builder_http()`]."
26)]
27#[cfg_attr(
28    all(
29        feature = "__rustls",
30        not(any(feature = "https", feature = "nativetls"))
31    ),
32    doc = "The return type of [`builder()`], [`builder_http()`] and [`builder_rustls()`]."
33)]
34#[cfg_attr(
35    all(
36        any(feature = "https", feature = "nativetls"),
37        not(feature = "__rustls")
38    ),
39    doc = "The return type of [`builder()`], [`builder_http()`], [`builder_https()`] and [`builder_nativetls()`]."
40)]
41#[cfg_attr(
42    all(any(feature = "https", feature = "nativetls"), feature = "__rustls"),
43    doc = "The return type of [`builder()`], [`builder_http()`], [`builder_https()`], [`builder_nativetls()`] and [`builder_rustls()`]."
44)]
45#[derive(Debug)]
46pub struct Builder<C = HttpConnector, B = Incoming> {
47    client: Arc<Client<C, B>>,
48    scheme: Scheme,
49    authority: Authority,
50}
51
52impl<C, B> Clone for Builder<C, B> {
53    fn clone(&self) -> Self {
54        Self {
55            client: Arc::clone(&self.client),
56            scheme: self.scheme.clone(),
57            authority: self.authority.clone(),
58        }
59    }
60}
61
62impl<C, B> Builder<C, B> {
63    pub fn build<Pr>(&self, path: Pr) -> ReusedService<Pr, C, B> {
64        let Self {
65            client,
66            scheme,
67            authority,
68        } = Clone::clone(self);
69        ReusedService {
70            client,
71            scheme,
72            authority,
73            path,
74        }
75    }
76}
77
78/// Builder of [`ReusedService`], with [`client::http_default()`].
79///
80/// For the meaning of "authority", refer to the documentation of [`Uri`](http::uri::Uri).
81///
82/// # Errors
83///
84/// When `authority` cannot be converted into an [`Authority`].
85pub fn builder_http<B, A>(authority: A) -> Result<Builder<HttpConnector, B>, HttpError>
86where
87    B: HttpBody + Send,
88    B::Data: Send,
89    Authority: TryFrom<A>,
90    <Authority as TryFrom<A>>::Error: Into<HttpError>,
91{
92    builder(client::http_default(), Scheme::HTTP, authority)
93}
94
95/// Builder of [`ReusedService`], with [`client::https_default()`].
96///
97/// This is the same as [`builder_nativetls()`].
98///
99/// For the meaning of "authority", refer to the documentation of [`Uri`](http::uri::Uri).
100///
101/// # Errors
102///
103/// When `authority` cannot be converted into an [`Authority`].
104#[cfg(any(feature = "https", feature = "nativetls"))]
105#[cfg_attr(docsrs, doc(cfg(any(feature = "https", feature = "nativetls"))))]
106pub fn builder_https<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::https_default(), Scheme::HTTPS, authority)
116}
117
118/// Builder of [`ReusedService`], with [`client::nativetls_default()`].
119///
120/// For the meaning of "authority", refer to the documentation of [`Uri`](http::uri::Uri).
121///
122/// # Errors
123///
124/// When `authority` cannot be converted into an [`Authority`].
125#[cfg(feature = "nativetls")]
126#[cfg_attr(docsrs, doc(cfg(feature = "nativetls")))]
127pub fn builder_nativetls<B, A>(
128    authority: A,
129) -> Result<Builder<NativeTlsConnector<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::nativetls_default(), Scheme::HTTPS, authority)
137}
138
139/// Builder of [`ReusedService`], with [`client::rustls_default()`].
140///
141/// For the meaning of "authority", refer to the documentation of [`Uri`](http::uri::Uri).
142///
143/// # Errors
144///
145/// When `authority` cannot be converted into an [`Authority`].
146#[cfg(feature = "__rustls")]
147#[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
148pub fn builder_rustls<B, A>(
149    authority: A,
150) -> Result<Builder<RustlsConnector<HttpConnector>, B>, HttpError>
151where
152    B: HttpBody + Send,
153    B::Data: Send,
154    Authority: TryFrom<A>,
155    <Authority as TryFrom<A>>::Error: Into<HttpError>,
156{
157    builder(client::rustls_default(), Scheme::HTTPS, authority)
158}
159
160/// Builder of [`ReusedService`].
161///
162/// For the meaning of "scheme" and "authority", refer to the documentation of
163/// [`Uri`](http::uri::Uri).
164///
165/// # Errors
166///
167/// When `scheme` or `authority` cannot be converted into a [`Scheme`] or [`Authority`].
168pub fn builder<C, B, S, A>(
169    client: Client<C, B>,
170    scheme: S,
171    authority: A,
172) -> Result<Builder<C, B>, HttpError>
173where
174    Scheme: TryFrom<S>,
175    <Scheme as TryFrom<S>>::Error: Into<HttpError>,
176    Authority: TryFrom<A>,
177    <Authority as TryFrom<A>>::Error: Into<HttpError>,
178{
179    let scheme = scheme.try_into().map_err(Into::into)?;
180    let authority = authority.try_into().map_err(Into::into)?;
181    Ok(Builder {
182        client: Arc::new(client),
183        scheme,
184        authority,
185    })
186}
187
188/// A [`Service<Request<B>>`] that sends a request and returns the response, sharing a [`Client`].
189///
190/// ```
191/// # async fn run_test() {
192/// # use tower_proxy::ReusedService;
193/// # use tower_proxy::Static;
194/// # use tower_service::Service;
195/// # use http_body_util::Empty;
196/// # use http::Request;
197/// # use hyper::body::{Body, Bytes};
198/// let svc_builder = tower_proxy::builder_http("example.com:1234").unwrap();
199///
200/// let mut svc1 = svc_builder.build(Static("bar"));
201/// let mut svc2 = svc_builder.build(Static("baz"));
202///
203/// let req = Request::builder()
204///     .uri("https://myserver.com/foo")
205///     .body(Empty::<Bytes>::new())
206///     .unwrap();
207/// // http://example.com:1234/bar
208/// let _res = svc1.call(req).await.unwrap();
209///
210/// let req = Request::builder()
211///     .uri("https://myserver.com/foo")
212///     .body(Empty::<Bytes>::new())
213///     .unwrap();
214/// // http://example.com:1234/baz
215/// let _res = svc2.call(req).await.unwrap();
216/// # }
217/// ```
218#[derive(Debug)]
219pub struct ReusedService<Pr, C, B = Incoming> {
220    client: Arc<Client<C, B>>,
221    scheme: Scheme,
222    authority: Authority,
223    path: Pr,
224}
225
226impl<Pr: Clone, C, B> Clone for ReusedService<Pr, C, B> {
227    #[inline]
228    fn clone(&self) -> Self {
229        Self {
230            client: Arc::clone(&self.client),
231            scheme: self.scheme.clone(),
232            authority: self.authority.clone(),
233            path: self.path.clone(),
234        }
235    }
236}
237
238impl<Pr, C, B> ReusedService<Pr, C, B> {
239    /// # Errors
240    ///
241    /// When `scheme` or `authority` cannot be converted into a [`Scheme`] or [`Authority`].
242    pub fn from<S, A>(
243        client: Arc<Client<C, B>>,
244        scheme: S,
245        authority: A,
246        path: Pr,
247    ) -> Result<Self, HttpError>
248    where
249        Scheme: TryFrom<S>,
250        <Scheme as TryFrom<S>>::Error: Into<HttpError>,
251        Authority: TryFrom<A>,
252        <Authority as TryFrom<A>>::Error: Into<HttpError>,
253    {
254        let scheme = scheme.try_into().map_err(Into::into)?;
255        let authority = authority.try_into().map_err(Into::into)?;
256        Ok(Self {
257            client,
258            scheme,
259            authority,
260            path,
261        })
262    }
263}
264
265impl<B, Pr> ReusedService<Pr, HttpConnector, B>
266where
267    B: HttpBody + Send,
268    B::Data: Send,
269{
270    /// # Errors
271    ///
272    /// When `authority` cannot be converted into an [`Authority`].
273    pub fn with_http_client<A>(
274        client: Arc<Client<HttpConnector, B>>,
275        authority: A,
276        path: Pr,
277    ) -> Result<Self, HttpError>
278    where
279        Authority: TryFrom<A>,
280        <Authority as TryFrom<A>>::Error: Into<HttpError>,
281    {
282        let authority = authority.try_into().map_err(Into::into)?;
283        Ok(Self {
284            client,
285            scheme: Scheme::HTTP,
286            authority,
287            path,
288        })
289    }
290}
291
292#[cfg(feature = "nativetls")]
293impl<Pr, B> ReusedService<Pr, NativeTlsConnector<HttpConnector>, B>
294where
295    B: HttpBody + Send,
296    B::Data: Send,
297{
298    /// Alias to [`Self::with_nativetls_client()`].
299    ///
300    /// # Errors
301    ///
302    /// When `authority` cannot be converted into an [`Authority`].
303    #[cfg_attr(docsrs, doc(cfg(any(feature = "https", feature = "nativetls"))))]
304    #[cfg(any(feature = "https", feature = "nativetls"))]
305    pub fn with_https_client<A>(
306        client: Arc<Client<NativeTlsConnector<HttpConnector>, B>>,
307        authority: A,
308        path: Pr,
309    ) -> Result<Self, HttpError>
310    where
311        Authority: TryFrom<A>,
312        <Authority as TryFrom<A>>::Error: Into<HttpError>,
313    {
314        let authority = authority.try_into().map_err(Into::into)?;
315        Ok(Self {
316            client,
317            scheme: Scheme::HTTPS,
318            authority,
319            path,
320        })
321    }
322
323    ///
324    /// # Errors
325    ///
326    /// When `authority` cannot be converted into an [`Authority`].
327    #[cfg_attr(docsrs, doc(cfg(feature = "nativetls")))]
328    #[cfg(feature = "nativetls")]
329    pub fn with_nativetls_client<A>(
330        client: Arc<Client<NativeTlsConnector<HttpConnector>, B>>,
331        authority: A,
332        path: Pr,
333    ) -> Result<Self, HttpError>
334    where
335        Authority: TryFrom<A>,
336        <Authority as TryFrom<A>>::Error: Into<HttpError>,
337    {
338        let authority = authority.try_into().map_err(Into::into)?;
339        Ok(Self {
340            client,
341            scheme: Scheme::HTTPS,
342            authority,
343            path,
344        })
345    }
346}
347
348#[cfg(feature = "__rustls")]
349impl<Pr, B> ReusedService<Pr, RustlsConnector<HttpConnector>, B>
350where
351    B: HttpBody + Send,
352    B::Data: Send,
353{
354    ///
355    /// # Errors
356    ///
357    /// When `authority` cannot be converted into an [`Authority`].
358    #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
359    pub fn with_rustls_client<A>(
360        client: Arc<Client<RustlsConnector<HttpConnector>, B>>,
361        authority: A,
362        path: Pr,
363    ) -> Result<Self, HttpError>
364    where
365        Authority: TryFrom<A>,
366        <Authority as TryFrom<A>>::Error: Into<HttpError>,
367    {
368        let authority = authority.try_into().map_err(Into::into)?;
369        Ok(Self {
370            client,
371            scheme: Scheme::HTTPS,
372            authority,
373            path,
374        })
375    }
376}
377
378impl<C, B, Pr> Service<Request<B>> for ReusedService<Pr, C, B>
379where
380    C: Connect + Clone + Send + Sync + 'static,
381    B: HttpBody + Send + 'static + Unpin,
382    B::Data: Send,
383    B::Error: Into<BoxErr>,
384    Pr: PathRewriter,
385{
386    type Response = Result<Response<Incoming>, ProxyError>;
387    type Error = Infallible;
388    type Future = RevProxyFuture;
389
390    fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
391        Poll::Ready(Ok(()))
392    }
393
394    fn call(&mut self, req: Request<B>) -> Self::Future {
395        RevProxyFuture::new(
396            &self.client,
397            req,
398            &self.scheme,
399            &self.authority,
400            &mut self.path,
401        )
402    }
403}
404
405#[cfg(test)]
406mod test {
407    use http::uri::{Parts, Uri};
408    use mockito::ServerGuard;
409
410    use super::*;
411    use crate::{ReplaceAll, test_helper};
412
413    async fn make_svc() -> (
414        ServerGuard,
415        ReusedService<ReplaceAll<'static>, HttpConnector, String>,
416    ) {
417        let server = mockito::Server::new_async().await;
418        let uri = Uri::try_from(&server.url());
419        assert!(uri.is_ok());
420        let uri = uri.unwrap();
421
422        let Parts {
423            scheme, authority, ..
424        } = uri.into_parts();
425
426        let svc = ReusedService::from(
427            Arc::new(client::http_default()),
428            scheme.unwrap(),
429            authority.unwrap(),
430            ReplaceAll("foo", "goo"),
431        );
432        assert!(svc.is_ok());
433        (server, svc.unwrap())
434    }
435
436    #[tokio::test]
437    async fn match_path() {
438        let (mut server, mut svc) = make_svc().await;
439        test_helper::match_path(&mut server, &mut svc).await;
440    }
441
442    #[tokio::test]
443    async fn match_query() {
444        let (mut server, mut svc) = make_svc().await;
445        test_helper::match_query(&mut server, &mut svc).await;
446    }
447
448    #[tokio::test]
449    async fn match_post() {
450        let (mut server, mut svc) = make_svc().await;
451        test_helper::match_post(&mut server, &mut svc).await;
452    }
453
454    #[tokio::test]
455    async fn match_header() {
456        let (mut server, mut svc) = make_svc().await;
457        test_helper::match_header(&mut server, &mut svc).await;
458    }
459}