@@ -11,19 +11,22 @@ use futures_core::ready;
1111use pin_project:: pin_project;
1212use tower:: Service ;
1313
14- use crate :: BoxedError ;
14+ use crate :: { BoxedError , FallbackPolicy , OnError } ;
1515
1616/// Future that completes either with the first service's successful response, or
1717/// with the second service's response.
1818#[ pin_project]
19- pub struct ResponseFuture < S1 , S2 , Request >
19+ pub struct ResponseFuture < S1 , S2 , Request , F = OnError >
2020where
2121 S1 : Service < Request > ,
2222 S2 : Service < Request , Response = <S1 as Service < Request > >:: Response > ,
23+ F : FallbackPolicy < <S1 as Service < Request > >:: Response > ,
24+ S1 :: Error : Into < BoxedError > ,
2325 S2 :: Error : Into < BoxedError > ,
2426{
2527 #[ pin]
2628 state : ResponseState < S1 , S2 , Request > ,
29+ policy : F ,
2730}
2831
2932#[ pin_project( project_replace = __ResponseStateProjectionOwned, project = ResponseStateProj ) ]
@@ -51,23 +54,28 @@ where
5154 Tmp ,
5255}
5356
54- impl < S1 , S2 , Request > ResponseFuture < S1 , S2 , Request >
57+ impl < S1 , S2 , Request , F > ResponseFuture < S1 , S2 , Request , F >
5558where
5659 S1 : Service < Request > ,
5760 S2 : Service < Request , Response = <S1 as Service < Request > >:: Response > ,
61+ F : FallbackPolicy < <S1 as Service < Request > >:: Response > ,
62+ S1 :: Error : Into < BoxedError > ,
5863 S2 :: Error : Into < BoxedError > ,
5964{
60- pub ( crate ) fn new ( fut : S1 :: Future , req : Request , svc2 : S2 ) -> Self {
65+ pub ( crate ) fn new ( fut : S1 :: Future , req : Request , svc2 : S2 , policy : F ) -> Self {
6166 ResponseFuture {
6267 state : ResponseState :: PollResponse1 { fut, req, svc2 } ,
68+ policy,
6369 }
6470 }
6571}
6672
67- impl < S1 , S2 , Request > Future for ResponseFuture < S1 , S2 , Request >
73+ impl < S1 , S2 , Request , F > Future for ResponseFuture < S1 , S2 , Request , F >
6874where
6975 S1 : Service < Request > ,
7076 S2 : Service < Request , Response = <S1 as Service < Request > >:: Response > ,
77+ F : FallbackPolicy < <S1 as Service < Request > >:: Response > ,
78+ S1 :: Error : Into < BoxedError > ,
7179 S2 :: Error : Into < BoxedError > ,
7280{
7381 type Output = Result < <S1 as Service < Request > >:: Response , BoxedError > ;
@@ -83,19 +91,22 @@ where
8391 // only returns Pending when a future or service returns Pending.
8492 loop {
8593 match this. state . as_mut ( ) . project ( ) {
86- ResponseStateProj :: PollResponse1 { fut, .. } => match ready ! ( fut. poll( cx) ) {
87- Ok ( rsp) => return Poll :: Ready ( Ok ( rsp) ) ,
88- Err ( _) => {
89- tracing:: debug!( "got error from svc1, retrying on svc2" ) ;
94+ ResponseStateProj :: PollResponse1 { fut, .. } => {
95+ let result = ready ! ( fut. poll( cx) ) . map_err ( Into :: into) ;
96+
97+ if this. policy . should_fallback ( & result) {
98+ tracing:: debug!( "fallback policy selected svc2" ) ;
9099 if let __ResponseStateProjectionOwned:: PollResponse1 { req, svc2, .. } =
91100 this. state . as_mut ( ) . project_replace ( ResponseState :: Tmp )
92101 {
93102 this. state . set ( ResponseState :: PollReady2 { req, svc2 } ) ;
94103 } else {
95104 unreachable ! ( ) ;
96105 }
106+ } else {
107+ return Poll :: Ready ( result) ;
97108 }
98- } ,
109+ }
99110 ResponseStateProj :: PollReady2 { svc2, .. } => match ready ! ( svc2. poll_ready( cx) ) {
100111 Err ( e) => return Poll :: Ready ( Err ( e. into ( ) ) ) ,
101112 Ok ( ( ) ) => {
@@ -119,10 +130,12 @@ where
119130 }
120131}
121132
122- impl < S1 , S2 , Request > Debug for ResponseFuture < S1 , S2 , Request >
133+ impl < S1 , S2 , Request , F > Debug for ResponseFuture < S1 , S2 , Request , F >
123134where
124135 S1 : Service < Request > ,
125136 S2 : Service < Request , Response = <S1 as Service < Request > >:: Response > ,
137+ F : FallbackPolicy < <S1 as Service < Request > >:: Response > ,
138+ S1 :: Error : Into < BoxedError > ,
126139 Request : Debug ,
127140 S1 :: Future : Debug ,
128141 S2 : Debug ,
0 commit comments