@@ -13,6 +13,7 @@ use std::time::Duration;
13
13
14
14
use futures_util:: future:: { self , Either , FutureExt , TryFutureExt } ;
15
15
use http:: uri:: Scheme ;
16
+ use hyper:: client:: conn:: TrySendError as ConnTrySendError ;
16
17
use hyper:: header:: { HeaderValue , HOST } ;
17
18
use hyper:: rt:: Timer ;
18
19
use hyper:: { body:: Body , Method , Request , Response , Uri , Version } ;
@@ -86,6 +87,11 @@ macro_rules! e {
86
87
// We might change this... :shrug:
87
88
type PoolKey = ( http:: uri:: Scheme , http:: uri:: Authority ) ;
88
89
90
+ enum TrySendError < B > {
91
+ Retryable { error : Error , req : Request < B > } ,
92
+ Nope ( Error ) ,
93
+ }
94
+
89
95
/// A `Future` that will resolve to an HTTP Response.
90
96
///
91
97
/// This is returned by `Client::request` (and `Client::get`).
@@ -223,47 +229,46 @@ where
223
229
ResponseFuture :: new ( self . clone ( ) . send_request ( req, pool_key) )
224
230
}
225
231
226
- /*
227
- async fn retryably_send_request(
232
+ async fn send_request (
228
233
self ,
229
234
mut req : Request < B > ,
230
235
pool_key : PoolKey ,
231
236
) -> Result < Response < hyper:: body:: Incoming > , Error > {
232
237
let uri = req. uri ( ) . clone ( ) ;
233
238
234
239
loop {
235
- req = match self.send_request (req, pool_key.clone()).await {
240
+ req = match self . try_send_request ( req, pool_key. clone ( ) ) . await {
236
241
Ok ( resp) => return Ok ( resp) ,
237
- Err(ClientError::Normal(err)) => return Err(err),
238
- Err(ClientError::Canceled {
239
- connection_reused,
240
- mut req,
241
- reason,
242
- }) => {
243
- if !self.config.retry_canceled_requests || !connection_reused {
242
+ Err ( TrySendError :: Nope ( err) ) => return Err ( err) ,
243
+ Err ( TrySendError :: Retryable { mut req, error } ) => {
244
+ if !self . config . retry_canceled_requests {
244
245
// if client disabled, don't retry
245
246
// a fresh connection means we definitely can't retry
246
- return Err(reason );
247
+ return Err ( error ) ;
247
248
}
248
249
249
250
trace ! (
250
251
"unstarted request canceled, trying again (reason={:?})" ,
251
- reason
252
+ error
252
253
) ;
253
254
* req. uri_mut ( ) = uri. clone ( ) ;
254
255
req
255
256
}
256
257
}
257
258
}
258
259
}
259
- */
260
260
261
- async fn send_request (
262
- self ,
261
+ async fn try_send_request (
262
+ & self ,
263
263
mut req : Request < B > ,
264
264
pool_key : PoolKey ,
265
- ) -> Result < Response < hyper:: body:: Incoming > , Error > {
266
- let mut pooled = self . connection_for ( pool_key) . await ?;
265
+ ) -> Result < Response < hyper:: body:: Incoming > , TrySendError < B > > {
266
+ let mut pooled = self
267
+ . connection_for ( pool_key)
268
+ . await
269
+ // `connection_for` already retries checkout errors, so if
270
+ // it returns an error, there's not much else to retry
271
+ . map_err ( TrySendError :: Nope ) ?;
267
272
268
273
req. extensions_mut ( )
269
274
. get_mut :: < CaptureConnectionExtension > ( )
@@ -272,7 +277,7 @@ where
272
277
if pooled. is_http1 ( ) {
273
278
if req. version ( ) == Version :: HTTP_2 {
274
279
warn ! ( "Connection is HTTP/1, but request requires HTTP/2" ) ;
275
- return Err ( e ! ( UserUnsupportedVersion ) ) ;
280
+ return Err ( TrySendError :: Nope ( e ! ( UserUnsupportedVersion ) ) ) ;
276
281
}
277
282
278
283
if self . config . set_host {
@@ -301,18 +306,26 @@ where
301
306
authority_form ( req. uri_mut ( ) ) ;
302
307
}
303
308
304
- let fut = pooled. send_request ( req) ;
309
+ let mut res = match pooled. try_send_request ( req) . await {
310
+ Ok ( res) => res,
311
+ Err ( mut err) => {
312
+ return if let Some ( req) = err. take_message ( ) {
313
+ Err ( TrySendError :: Retryable {
314
+ error : e ! ( Canceled , err. into_error( ) ) ,
315
+ req,
316
+ } )
317
+ } else {
318
+ Err ( TrySendError :: Nope ( e ! ( SendRequest , err. into_error( ) ) ) )
319
+ }
320
+ }
321
+ } ;
305
322
//.send_request_retryable(req)
306
323
//.map_err(ClientError::map_with_reused(pooled.is_reused()));
307
324
308
325
// If the Connector included 'extra' info, add to Response...
309
- let extra_info = pooled. conn_info . extra . clone ( ) ;
310
- let fut = fut. map_ok ( move |mut res| {
311
- if let Some ( extra) = extra_info {
312
- extra. set ( res. extensions_mut ( ) ) ;
313
- }
314
- res
315
- } ) ;
326
+ if let Some ( extra) = & pooled. conn_info . extra {
327
+ extra. set ( res. extensions_mut ( ) ) ;
328
+ }
316
329
317
330
// As of [email protected] , there is a race condition in the mpsc
318
331
// channel, such that sending when the receiver is closing can
@@ -322,11 +335,9 @@ where
322
335
// To counteract this, we must check if our senders 'want' channel
323
336
// has been closed after having tried to send. If so, error out...
324
337
if pooled. is_closed ( ) {
325
- return fut . await ;
338
+ return Ok ( res ) ;
326
339
}
327
340
328
- let res = fut. await ?;
329
-
330
341
// If pooled is HTTP/2, we can toss this reference immediately.
331
342
//
332
343
// when pooled is dropped, it will try to insert back into the
@@ -749,37 +760,34 @@ impl<B> PoolClient<B> {
749
760
}
750
761
751
762
impl < B : Body + ' static > PoolClient < B > {
752
- fn send_request (
763
+ fn try_send_request (
753
764
& mut self ,
754
765
req : Request < B > ,
755
- ) -> impl Future < Output = Result < Response < hyper:: body:: Incoming > , Error > >
766
+ ) -> impl Future < Output = Result < Response < hyper:: body:: Incoming > , ConnTrySendError < Request < B > > > >
756
767
where
757
768
B : Send ,
758
769
{
759
770
#[ cfg( all( feature = "http1" , feature = "http2" ) ) ]
760
771
return match self . tx {
761
772
#[ cfg( feature = "http1" ) ]
762
- PoolTx :: Http1 ( ref mut tx) => Either :: Left ( tx. send_request ( req) ) ,
773
+ PoolTx :: Http1 ( ref mut tx) => Either :: Left ( tx. try_send_request ( req) ) ,
763
774
#[ cfg( feature = "http2" ) ]
764
- PoolTx :: Http2 ( ref mut tx) => Either :: Right ( tx. send_request ( req) ) ,
765
- }
766
- . map_err ( Error :: tx) ;
775
+ PoolTx :: Http2 ( ref mut tx) => Either :: Right ( tx. try_send_request ( req) ) ,
776
+ } ;
767
777
768
778
#[ cfg( feature = "http1" ) ]
769
779
#[ cfg( not( feature = "http2" ) ) ]
770
780
return match self . tx {
771
781
#[ cfg( feature = "http1" ) ]
772
- PoolTx :: Http1 ( ref mut tx) => tx. send_request ( req) ,
773
- }
774
- . map_err ( Error :: tx) ;
782
+ PoolTx :: Http1 ( ref mut tx) => tx. try_send_request ( req) ,
783
+ } ;
775
784
776
785
#[ cfg( not( feature = "http1" ) ) ]
777
786
#[ cfg( feature = "http2" ) ]
778
787
return match self . tx {
779
788
#[ cfg( feature = "http2" ) ]
780
- PoolTx :: Http2 ( ref mut tx) => tx. send_request ( req) ,
781
- }
782
- . map_err ( Error :: tx) ;
789
+ PoolTx :: Http2 ( ref mut tx) => tx. try_send_request ( req) ,
790
+ } ;
783
791
}
784
792
/*
785
793
//TODO: can we re-introduce this somehow? Or must people use tower::retry?
0 commit comments