|
| 1 | +//! A middleware that switches between two underlying stacks, depending on the |
| 2 | +//! target type. |
| 3 | +
|
| 4 | +use futures::{future, prelude::*}; |
| 5 | +use linkerd2_error::Error; |
| 6 | +use std::task::{Context, Poll}; |
| 7 | +use tower::util::ServiceExt; |
| 8 | + |
| 9 | +/// Determines whether the primary stack should be used. |
| 10 | +pub trait Switch<T> { |
| 11 | + fn use_primary(&self, target: &T) -> bool; |
| 12 | +} |
| 13 | + |
| 14 | +/// Makes either the primary or fallback stack, as determined by an `S`-typed |
| 15 | +/// `Switch`. |
| 16 | +pub struct MakeSwitch<S, P, F> { |
| 17 | + switch: S, |
| 18 | + primary: P, |
| 19 | + fallback: F, |
| 20 | +} |
| 21 | + |
| 22 | +impl<S, P, F> MakeSwitch<S, P, F> { |
| 23 | + pub fn new(switch: S, primary: P, fallback: F) -> Self { |
| 24 | + MakeSwitch { |
| 25 | + switch, |
| 26 | + primary, |
| 27 | + fallback, |
| 28 | + } |
| 29 | + } |
| 30 | + |
| 31 | + pub fn layer(switch: S, fallback: F) -> impl super::layer::Layer<P, Service = Self> + Clone |
| 32 | + where |
| 33 | + S: Clone, |
| 34 | + F: Clone, |
| 35 | + { |
| 36 | + super::layer::mk(move |primary| Self::new(switch.clone(), primary, fallback.clone())) |
| 37 | + } |
| 38 | +} |
| 39 | + |
| 40 | +impl<T, S, P, F> tower::Service<T> for MakeSwitch<S, P, F> |
| 41 | +where |
| 42 | + S: Switch<T>, |
| 43 | + P: tower::Service<T> + Clone, |
| 44 | + P::Error: Into<Error>, |
| 45 | + F: tower::Service<T> + Clone, |
| 46 | + F::Error: Into<Error>, |
| 47 | +{ |
| 48 | + type Response = tower::util::Either<P::Response, F::Response>; |
| 49 | + type Error = Error; |
| 50 | + type Future = future::Either< |
| 51 | + future::MapOk< |
| 52 | + future::ErrInto<tower::util::Oneshot<P, T>, Error>, |
| 53 | + fn(P::Response) -> tower::util::Either<P::Response, F::Response>, |
| 54 | + >, |
| 55 | + future::MapOk< |
| 56 | + future::ErrInto<tower::util::Oneshot<F, T>, Error>, |
| 57 | + fn(F::Response) -> tower::util::Either<P::Response, F::Response>, |
| 58 | + >, |
| 59 | + >; |
| 60 | + |
| 61 | + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Error>> { |
| 62 | + Poll::Ready(Ok(())) |
| 63 | + } |
| 64 | + |
| 65 | + fn call(&mut self, target: T) -> Self::Future { |
| 66 | + if self.switch.use_primary(&target) { |
| 67 | + future::Either::Left( |
| 68 | + self.primary |
| 69 | + .clone() |
| 70 | + .oneshot(target) |
| 71 | + .err_into::<Error>() |
| 72 | + .map_ok(tower::util::Either::A), |
| 73 | + ) |
| 74 | + } else { |
| 75 | + future::Either::Right( |
| 76 | + self.fallback |
| 77 | + .clone() |
| 78 | + .oneshot(target) |
| 79 | + .err_into::<Error>() |
| 80 | + .map_ok(tower::util::Either::B), |
| 81 | + ) |
| 82 | + } |
| 83 | + } |
| 84 | +} |
0 commit comments