Skip to main content

hyper_util/client/pool/
negotiate.rs

1//! Negotiate a pool of services
2//!
3//! The negotiate pool allows for a service that can decide between two service
4//! types based on an intermediate return value. It differs from typical
5//! routing since it doesn't depend on the request, but the response.
6//!
7//! The original use case is support ALPN upgrades to HTTP/2, with a fallback
8//! to HTTP/1.
9//!
10//! # Example
11//!
12//! ```rust,ignore
13//! # async fn run() -> Result<(), Box<dyn std::error::Error>> {
14//! # struct Conn;
15//! # impl Conn { fn negotiated_protocol(&self) -> &[u8] { b"h2" } }
16//! # let some_tls_connector = tower::service::service_fn(|_| async move {
17//! #     Ok::<_, std::convert::Infallible>(Conn)
18//! # });
19//! # let http1_layer = tower::layer::layer_fn(|s| s);
20//! # let http2_layer = tower::layer::layer_fn(|s| s);
21//! let mut pool = hyper_util::client::pool::negotiate::builder()
22//!     .connect(some_tls_connector)
23//!     .inspect(|c| c.negotiated_protocol() == b"h2")
24//!     .fallback(http1_layer)
25//!     .upgrade(http2_layer)
26//!     .build();
27//!
28//! // connect
29//! let mut svc = pool.call(http::Uri::from_static("https://hyper.rs")).await?;
30//! svc.ready().await;
31//!
32//! // http1 or http2 is now set up
33//! # let some_http_req = http::Request::new(());
34//! let resp = svc.call(some_http_req).await?;
35//! # Ok(())
36//! # }
37//! ```
38
39pub use self::internal::builder;
40
41#[cfg(docsrs)]
42pub use self::internal::Builder;
43#[cfg(docsrs)]
44pub use self::internal::Negotiate;
45#[cfg(docsrs)]
46pub use self::internal::Negotiated;
47
48mod internal {
49    use std::pin::Pin;
50    use std::sync::{Arc, Mutex};
51    use std::task::{self, Poll, ready};
52
53    use pin_project_lite::pin_project;
54    use tower_layer::Layer;
55    use tower_service::Service;
56
57    type BoxError = Box<dyn std::error::Error + Send + Sync>;
58
59    /// A negotiating pool over an inner make service.
60    ///
61    /// Created with [`builder()`].
62    ///
63    /// # Unnameable
64    ///
65    /// This type is normally unnameable, forbidding naming of the type within
66    /// code. The type is exposed in the documentation to show which methods
67    /// can be publicly called.
68    #[derive(Clone)]
69    pub struct Negotiate<L, R> {
70        left: L,
71        right: R,
72    }
73
74    /// A negotiated service returned by [`Negotiate`].
75    ///
76    /// # Unnameable
77    ///
78    /// This type is normally unnameable, forbidding naming of the type within
79    /// code. The type is exposed in the documentation to show which methods
80    /// can be publicly called.
81    #[derive(Clone, Debug)]
82    pub enum Negotiated<L, R> {
83        #[doc(hidden)]
84        Fallback(L),
85        #[doc(hidden)]
86        Upgraded(R),
87    }
88
89    pin_project! {
90        pub struct Negotiating<Dst, L, R>
91        where
92            L: Service<Dst>,
93            R: Service<()>,
94        {
95            #[pin]
96            state: State<Dst, L::Future, R::Future>,
97            left: L,
98            right: R,
99        }
100    }
101
102    pin_project! {
103        #[project = StateProj]
104        enum State<Dst, FL, FR> {
105            Eager {
106                #[pin]
107                future: FR,
108                dst: Option<Dst>,
109            },
110            Fallback {
111                #[pin]
112                future: FL,
113            },
114            Upgrade {
115                #[pin]
116                future: FR,
117            }
118        }
119    }
120
121    pin_project! {
122        #[project = NegotiatedProj]
123        pub enum NegotiatedFuture<L, R> {
124            Fallback {
125                #[pin]
126                future: L
127            },
128            Upgraded {
129                #[pin]
130                future: R
131            },
132        }
133    }
134
135    /// A builder to configure a `Negotiate`.
136    ///
137    /// # Unnameable
138    ///
139    /// This type is normally unnameable, forbidding naming of the type within
140    /// code. The type is exposed in the documentation to show which methods
141    /// can be publicly called.
142    #[derive(Debug)]
143    pub struct Builder<C, I, L, R> {
144        connect: C,
145        inspect: I,
146        fallback: L,
147        upgrade: R,
148    }
149
150    #[derive(Debug)]
151    pub struct WantsConnect;
152    #[derive(Debug)]
153    pub struct WantsInspect;
154    #[derive(Debug)]
155    pub struct WantsFallback;
156    #[derive(Debug)]
157    pub struct WantsUpgrade;
158
159    /// Start a builder to construct a `Negotiate` pool.
160    pub fn builder() -> Builder<WantsConnect, WantsInspect, WantsFallback, WantsUpgrade> {
161        Builder {
162            connect: WantsConnect,
163            inspect: WantsInspect,
164            fallback: WantsFallback,
165            upgrade: WantsUpgrade,
166        }
167    }
168
169    impl<C, I, L, R> Builder<C, I, L, R> {
170        /// Provide the initial connector.
171        pub fn connect<CC>(self, connect: CC) -> Builder<CC, I, L, R> {
172            Builder {
173                connect,
174                inspect: self.inspect,
175                fallback: self.fallback,
176                upgrade: self.upgrade,
177            }
178        }
179
180        /// Provide the inspector that determines the result of the negotiation.
181        pub fn inspect<II>(self, inspect: II) -> Builder<C, II, L, R> {
182            Builder {
183                connect: self.connect,
184                inspect,
185                fallback: self.fallback,
186                upgrade: self.upgrade,
187            }
188        }
189
190        /// Provide the layer to fallback to if negotiation fails.
191        pub fn fallback<LL>(self, fallback: LL) -> Builder<C, I, LL, R> {
192            Builder {
193                connect: self.connect,
194                inspect: self.inspect,
195                fallback,
196                upgrade: self.upgrade,
197            }
198        }
199
200        /// Provide the layer to upgrade to if negotiation succeeds.
201        pub fn upgrade<RR>(self, upgrade: RR) -> Builder<C, I, L, RR> {
202            Builder {
203                connect: self.connect,
204                inspect: self.inspect,
205                fallback: self.fallback,
206                upgrade,
207            }
208        }
209
210        /// Build the `Negotiate` pool.
211        pub fn build<Dst>(self) -> Negotiate<L::Service, R::Service>
212        where
213            C: Service<Dst>,
214            C::Error: Into<BoxError>,
215            L: Layer<Inspector<C, C::Response, I>>,
216            L::Service: Service<Dst> + Clone,
217            <L::Service as Service<Dst>>::Error: Into<BoxError>,
218            R: Layer<Inspected<C::Response>>,
219            R::Service: Service<()> + Clone,
220            <R::Service as Service<()>>::Error: Into<BoxError>,
221            I: Fn(&C::Response) -> bool + Clone,
222        {
223            let Builder {
224                connect,
225                inspect,
226                fallback,
227                upgrade,
228            } = self;
229
230            let slot = Arc::new(Mutex::new(None));
231            let wrapped = Inspector {
232                svc: connect,
233                inspect,
234                slot: slot.clone(),
235            };
236            let left = fallback.layer(wrapped);
237
238            let right = upgrade.layer(Inspected { slot });
239
240            Negotiate { left, right }
241        }
242    }
243
244    impl<L, R> Negotiate<L, R> {
245        /// Get a mutable reference to the fallback service.
246        pub fn fallback_mut(&mut self) -> &mut L {
247            &mut self.left
248        }
249
250        /// Get a mutable reference to the upgrade service.
251        pub fn upgrade_mut(&mut self) -> &mut R {
252            &mut self.right
253        }
254    }
255
256    impl<L, R, Target> Service<Target> for Negotiate<L, R>
257    where
258        L: Service<Target> + Clone,
259        L::Error: Into<BoxError>,
260        R: Service<()> + Clone,
261        R::Error: Into<BoxError>,
262    {
263        type Response = Negotiated<L::Response, R::Response>;
264        type Error = BoxError;
265        type Future = Negotiating<Target, L, R>;
266
267        fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
268            self.left.poll_ready(cx).map_err(Into::into)
269        }
270
271        fn call(&mut self, dst: Target) -> Self::Future {
272            let left = self.left.clone();
273            Negotiating {
274                state: State::Eager {
275                    future: self.right.call(()),
276                    dst: Some(dst),
277                },
278                // place clone, take original that we already polled-ready.
279                left: std::mem::replace(&mut self.left, left),
280                right: self.right.clone(),
281            }
282        }
283    }
284
285    impl<Dst, L, R> Future for Negotiating<Dst, L, R>
286    where
287        L: Service<Dst>,
288        L::Error: Into<BoxError>,
289        R: Service<()>,
290        R::Error: Into<BoxError>,
291    {
292        type Output = Result<Negotiated<L::Response, R::Response>, BoxError>;
293
294        fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
295            // States:
296            // - `Eager`: try the "right" path first; on `UseOther` sentinel, fall back to left.
297            // - `Fallback`: try the left path; on `UseOther` sentinel, upgrade back to right.
298            // - `Upgrade`: retry the right path after a fallback.
299            // If all fail, give up.
300            let mut me = self.project();
301            loop {
302                match me.state.as_mut().project() {
303                    StateProj::Eager { future, dst } => match ready!(future.poll(cx)) {
304                        Ok(out) => return Poll::Ready(Ok(Negotiated::Upgraded(out))),
305                        Err(err) => {
306                            let err = err.into();
307                            if UseOther::is(&*err) {
308                                let dst = dst.take().unwrap();
309                                let f = me.left.call(dst);
310                                me.state.set(State::Fallback { future: f });
311                                continue;
312                            } else {
313                                return Poll::Ready(Err(err));
314                            }
315                        }
316                    },
317                    StateProj::Fallback { future } => match ready!(future.poll(cx)) {
318                        Ok(out) => return Poll::Ready(Ok(Negotiated::Fallback(out))),
319                        Err(err) => {
320                            let err = err.into();
321                            if UseOther::is(&*err) {
322                                let f = me.right.call(());
323                                me.state.set(State::Upgrade { future: f });
324                                continue;
325                            } else {
326                                return Poll::Ready(Err(err));
327                            }
328                        }
329                    },
330                    StateProj::Upgrade { future } => match ready!(future.poll(cx)) {
331                        Ok(out) => return Poll::Ready(Ok(Negotiated::Upgraded(out))),
332                        Err(err) => return Poll::Ready(Err(err.into())),
333                    },
334                }
335            }
336        }
337    }
338
339    impl<L, R> Negotiated<L, R> {
340        // Could be useful?
341        #[cfg(test)]
342        pub(super) fn is_fallback(&self) -> bool {
343            matches!(self, Negotiated::Fallback(_))
344        }
345
346        #[cfg(test)]
347        pub(super) fn is_upgraded(&self) -> bool {
348            matches!(self, Negotiated::Upgraded(_))
349        }
350
351        // TODO: are these the correct methods? Or .as_ref().fallback(), etc?
352
353        /// Get a reference to the fallback service if this is it.
354        pub fn fallback_ref(&self) -> Option<&L> {
355            if let Negotiated::Fallback(left) = self {
356                Some(left)
357            } else {
358                None
359            }
360        }
361
362        /// Get a mutable reference to the fallback service if this is it.
363        pub fn fallback_mut(&mut self) -> Option<&mut L> {
364            if let Negotiated::Fallback(left) = self {
365                Some(left)
366            } else {
367                None
368            }
369        }
370
371        /// Get a reference to the upgraded service if this is it.
372        pub fn upgraded_ref(&self) -> Option<&R> {
373            if let Negotiated::Upgraded(right) = self {
374                Some(right)
375            } else {
376                None
377            }
378        }
379
380        /// Get a mutable reference to the upgraded service if this is it.
381        pub fn upgraded_mut(&mut self) -> Option<&mut R> {
382            if let Negotiated::Upgraded(right) = self {
383                Some(right)
384            } else {
385                None
386            }
387        }
388    }
389
390    impl<L, R, Req, Res, E> Service<Req> for Negotiated<L, R>
391    where
392        L: Service<Req, Response = Res, Error = E>,
393        R: Service<Req, Response = Res, Error = E>,
394    {
395        type Response = Res;
396        type Error = E;
397        type Future = NegotiatedFuture<L::Future, R::Future>;
398
399        fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
400            match self {
401                Negotiated::Fallback(s) => s.poll_ready(cx),
402                Negotiated::Upgraded(s) => s.poll_ready(cx),
403            }
404        }
405
406        fn call(&mut self, req: Req) -> Self::Future {
407            match self {
408                Negotiated::Fallback(s) => NegotiatedFuture::Fallback {
409                    future: s.call(req),
410                },
411                Negotiated::Upgraded(s) => NegotiatedFuture::Upgraded {
412                    future: s.call(req),
413                },
414            }
415        }
416    }
417
418    impl<L, R, Out> Future for NegotiatedFuture<L, R>
419    where
420        L: Future<Output = Out>,
421        R: Future<Output = Out>,
422    {
423        type Output = Out;
424
425        fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
426            match self.project() {
427                NegotiatedProj::Fallback { future } => future.poll(cx),
428                NegotiatedProj::Upgraded { future } => future.poll(cx),
429            }
430        }
431    }
432
433    // ===== internal =====
434
435    pub struct Inspector<M, S, I> {
436        svc: M,
437        inspect: I,
438        slot: Arc<Mutex<Option<S>>>,
439    }
440
441    pin_project! {
442        pub struct InspectFuture<F, S, I> {
443            #[pin]
444            future: F,
445            inspect: I,
446            slot: Arc<Mutex<Option<S>>>,
447        }
448    }
449
450    impl<M: Clone, S, I: Clone> Clone for Inspector<M, S, I> {
451        fn clone(&self) -> Self {
452            Self {
453                svc: self.svc.clone(),
454                inspect: self.inspect.clone(),
455                slot: self.slot.clone(),
456            }
457        }
458    }
459
460    impl<M, S, I, Target> Service<Target> for Inspector<M, S, I>
461    where
462        M: Service<Target, Response = S>,
463        M::Error: Into<BoxError>,
464        I: Clone + Fn(&S) -> bool,
465    {
466        type Response = M::Response;
467        type Error = BoxError;
468        type Future = InspectFuture<M::Future, S, I>;
469
470        fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
471            self.svc.poll_ready(cx).map_err(Into::into)
472        }
473
474        fn call(&mut self, dst: Target) -> Self::Future {
475            InspectFuture {
476                future: self.svc.call(dst),
477                inspect: self.inspect.clone(),
478                slot: self.slot.clone(),
479            }
480        }
481    }
482
483    impl<F, I, S, E> Future for InspectFuture<F, S, I>
484    where
485        F: Future<Output = Result<S, E>>,
486        E: Into<BoxError>,
487        I: Fn(&S) -> bool,
488    {
489        type Output = Result<S, BoxError>;
490
491        fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
492            let me = self.project();
493            let s = ready!(me.future.poll(cx)).map_err(Into::into)?;
494            Poll::Ready(if (me.inspect)(&s) {
495                *me.slot.lock().unwrap() = Some(s);
496                Err(UseOther.into())
497            } else {
498                Ok(s)
499            })
500        }
501    }
502
503    pub struct Inspected<S> {
504        slot: Arc<Mutex<Option<S>>>,
505    }
506
507    impl<S, Target> Service<Target> for Inspected<S> {
508        type Response = S;
509        type Error = BoxError;
510        type Future = std::future::Ready<Result<S, BoxError>>;
511
512        fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
513            if self.slot.lock().unwrap().is_some() {
514                Poll::Ready(Ok(()))
515            } else {
516                Poll::Ready(Err(UseOther.into()))
517            }
518        }
519
520        fn call(&mut self, _dst: Target) -> Self::Future {
521            let s = self
522                .slot
523                .lock()
524                .unwrap()
525                .take()
526                .ok_or_else(|| UseOther.into());
527            std::future::ready(s)
528        }
529    }
530
531    impl<S> Clone for Inspected<S> {
532        fn clone(&self) -> Inspected<S> {
533            Inspected {
534                slot: self.slot.clone(),
535            }
536        }
537    }
538
539    #[derive(Debug)]
540    struct UseOther;
541
542    impl std::fmt::Display for UseOther {
543        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
544            f.write_str("sentinel error; using other")
545        }
546    }
547
548    impl std::error::Error for UseOther {}
549
550    impl UseOther {
551        fn is(err: &(dyn std::error::Error + 'static)) -> bool {
552            let mut source = Some(err);
553            while let Some(err) = source {
554                if err.is::<UseOther>() {
555                    return true;
556                }
557                source = err.source();
558            }
559            false
560        }
561    }
562}
563
564#[cfg(test)]
565mod tests {
566    use futures_util::future;
567    use tower_service::Service;
568    use tower_test::assert_request_eq;
569
570    #[tokio::test]
571    async fn not_negotiated_falls_back_to_left() {
572        let (mock_svc, mut handle) = tower_test::mock::pair::<(), &'static str>();
573
574        let mut negotiate = super::builder()
575            .connect(mock_svc)
576            .inspect(|_: &&str| false)
577            .fallback(layer_fn(|s| s))
578            .upgrade(layer_fn(|s| s))
579            .build();
580
581        std::future::poll_fn(|cx| negotiate.poll_ready(cx))
582            .await
583            .unwrap();
584
585        let fut = negotiate.call(());
586        let nsvc = future::join(fut, async move {
587            assert_request_eq!(handle, ()).send_response("one");
588        })
589        .await
590        .0
591        .expect("call");
592        assert!(nsvc.is_fallback());
593    }
594
595    #[tokio::test]
596    async fn negotiated_uses_right() {
597        let (mock_svc, mut handle) = tower_test::mock::pair::<(), &'static str>();
598
599        let mut negotiate = super::builder()
600            .connect(mock_svc)
601            .inspect(|_: &&str| true)
602            .fallback(layer_fn(|s| s))
603            .upgrade(layer_fn(|s| s))
604            .build();
605
606        std::future::poll_fn(|cx| negotiate.poll_ready(cx))
607            .await
608            .unwrap();
609
610        let fut = negotiate.call(());
611        let nsvc = future::join(fut, async move {
612            assert_request_eq!(handle, ()).send_response("one");
613        })
614        .await
615        .0
616        .expect("call");
617
618        assert!(nsvc.is_upgraded());
619    }
620
621    fn layer_fn<F>(f: F) -> LayerFn<F> {
622        LayerFn(f)
623    }
624
625    #[derive(Clone)]
626    struct LayerFn<F>(F);
627
628    impl<F, S, Out> tower_layer::Layer<S> for LayerFn<F>
629    where
630        F: Fn(S) -> Out,
631    {
632        type Service = Out;
633        fn layer(&self, inner: S) -> Self::Service {
634            (self.0)(inner)
635        }
636    }
637}