tower_http/follow_redirect/policy/
or.rs

1use super::{Action, Attempt, Policy};
2use http::Request;
3
4/// A redirection [`Policy`] that combines the results of two `Policy`s.
5///
6/// See [`PolicyExt::or`][super::PolicyExt::or] for more details.
7#[derive(Clone, Copy, Debug, Default)]
8pub struct Or<A, B> {
9    a: A,
10    b: B,
11}
12
13impl<A, B> Or<A, B> {
14    pub(crate) fn new<Bd, E>(a: A, b: B) -> Self
15    where
16        A: Policy<Bd, E>,
17        B: Policy<Bd, E>,
18    {
19        Or { a, b }
20    }
21}
22
23impl<Bd, E, A, B> Policy<Bd, E> for Or<A, B>
24where
25    A: Policy<Bd, E>,
26    B: Policy<Bd, E>,
27{
28    fn redirect(&mut self, attempt: &Attempt<'_>) -> Result<Action, E> {
29        match self.a.redirect(attempt) {
30            Ok(Action::Stop) | Err(_) => self.b.redirect(attempt),
31            a => a,
32        }
33    }
34
35    fn on_request(&mut self, request: &mut Request<Bd>) {
36        self.a.on_request(request);
37        self.b.on_request(request);
38    }
39
40    fn clone_body(&self, body: &Bd) -> Option<Bd> {
41        self.a.clone_body(body).or_else(|| self.b.clone_body(body))
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48    use http::Uri;
49
50    struct Taint<P> {
51        policy: P,
52        used: bool,
53    }
54
55    impl<P> Taint<P> {
56        fn new(policy: P) -> Self {
57            Taint {
58                policy,
59                used: false,
60            }
61        }
62    }
63
64    impl<B, E, P> Policy<B, E> for Taint<P>
65    where
66        P: Policy<B, E>,
67    {
68        fn redirect(&mut self, attempt: &Attempt<'_>) -> Result<Action, E> {
69            self.used = true;
70            self.policy.redirect(attempt)
71        }
72    }
73
74    #[test]
75    fn redirect() {
76        let attempt = Attempt {
77            status: Default::default(),
78            location: &Uri::from_static("*"),
79            previous: &Uri::from_static("*"),
80        };
81
82        let mut a = Taint::new(Action::Follow);
83        let mut b = Taint::new(Action::Follow);
84        let mut policy = Or::new::<(), ()>(&mut a, &mut b);
85        assert!(Policy::<(), ()>::redirect(&mut policy, &attempt)
86            .unwrap()
87            .is_follow());
88        assert!(a.used);
89        assert!(!b.used); // short-circuiting
90
91        let mut a = Taint::new(Action::Stop);
92        let mut b = Taint::new(Action::Follow);
93        let mut policy = Or::new::<(), ()>(&mut a, &mut b);
94        assert!(Policy::<(), ()>::redirect(&mut policy, &attempt)
95            .unwrap()
96            .is_follow());
97        assert!(a.used);
98        assert!(b.used);
99
100        let mut a = Taint::new(Action::Follow);
101        let mut b = Taint::new(Action::Stop);
102        let mut policy = Or::new::<(), ()>(&mut a, &mut b);
103        assert!(Policy::<(), ()>::redirect(&mut policy, &attempt)
104            .unwrap()
105            .is_follow());
106        assert!(a.used);
107        assert!(!b.used);
108
109        let mut a = Taint::new(Action::Stop);
110        let mut b = Taint::new(Action::Stop);
111        let mut policy = Or::new::<(), ()>(&mut a, &mut b);
112        assert!(Policy::<(), ()>::redirect(&mut policy, &attempt)
113            .unwrap()
114            .is_stop());
115        assert!(a.used);
116        assert!(b.used);
117    }
118}