@@ -7,7 +7,8 @@ use std::marker::PhantomData;
7
7
use std:: rc:: Rc ;
8
8
use std:: time:: Duration ;
9
9
10
- use futures:: { future, Poll , Future , Stream } ;
10
+ use futures:: { Future , Poll , Stream } ;
11
+ use futures:: future:: { self , Executor } ;
11
12
#[ cfg( feature = "compat" ) ]
12
13
use http;
13
14
use tokio:: reactor:: Handle ;
@@ -25,6 +26,8 @@ pub use proto::response::Response;
25
26
pub use proto:: request:: Request ;
26
27
pub use self :: connect:: { HttpConnector , Connect } ;
27
28
29
+ use self :: background:: { bg, Background } ;
30
+
28
31
mod connect;
29
32
mod dns;
30
33
mod pool;
@@ -37,7 +40,7 @@ pub mod compat;
37
40
// If the Connector is clone, then the Client can be clone easily.
38
41
pub struct Client < C , B = proto:: Body > {
39
42
connector : C ,
40
- handle : Handle ,
43
+ executor : Exec ,
41
44
pool : Pool < HyperClient < B > > ,
42
45
}
43
46
@@ -74,18 +77,24 @@ impl Client<HttpConnector, proto::Body> {
74
77
}
75
78
76
79
impl < C , B > Client < C , B > {
77
- /// Return a reference to a handle to the event loop this Client is associated with.
78
- #[ inline]
80
+ // Eventually, a Client won't really care about a tokio Handle, and only
81
+ // the executor used to spawn background tasks. Removing this method is
82
+ // a breaking change, so for now, it's just deprecated.
83
+ #[ doc( hidden) ]
84
+ #[ deprecated]
79
85
pub fn handle ( & self ) -> & Handle {
80
- & self . handle
86
+ match self . executor {
87
+ Exec :: Handle ( ref h) => h,
88
+ Exec :: Executor ( ..) => panic ! ( "Client not built with a Handle" ) ,
89
+ }
81
90
}
82
91
83
92
/// Create a new client with a specific connector.
84
93
#[ inline]
85
- fn configured ( config : Config < C , B > , handle : & Handle ) -> Client < C , B > {
94
+ fn configured ( config : Config < C , B > , exec : Exec ) -> Client < C , B > {
86
95
Client {
87
96
connector : config. connector ,
88
- handle : handle . clone ( ) ,
97
+ executor : exec ,
89
98
pool : Pool :: new ( config. keep_alive , config. keep_alive_timeout )
90
99
}
91
100
}
@@ -185,11 +194,11 @@ where C: Connect,
185
194
186
195
let checkout = self . pool . checkout ( domain. as_ref ( ) ) ;
187
196
let connect = {
188
- let handle = self . handle . clone ( ) ;
197
+ let executor = self . executor . clone ( ) ;
189
198
let pool = self . pool . clone ( ) ;
190
199
let pool_key = Rc :: new ( domain. to_string ( ) ) ;
191
200
self . connector . connect ( url)
192
- . map ( move |io| {
201
+ . and_then ( move |io| {
193
202
let ( tx, rx) = mpsc:: channel ( 0 ) ;
194
203
let tx = HyperClient {
195
204
tx : RefCell :: new ( tx) ,
@@ -198,8 +207,8 @@ where C: Connect,
198
207
let pooled = pool. pooled ( pool_key, tx) ;
199
208
let conn = proto:: Conn :: < _ , _ , proto:: ClientTransaction , _ > :: new ( io, pooled. clone ( ) ) ;
200
209
let dispatch = proto:: dispatch:: Dispatcher :: new ( proto:: dispatch:: Client :: new ( rx) , conn) ;
201
- handle . spawn ( dispatch. map_err ( |err | debug ! ( "client connection error: {}" , err ) ) ) ;
202
- pooled
210
+ executor . execute ( dispatch. map_err ( |e | debug ! ( "client connection error: {}" , e ) ) ) ? ;
211
+ Ok ( pooled)
203
212
} )
204
213
} ;
205
214
@@ -236,7 +245,7 @@ impl<C: Clone, B> Clone for Client<C, B> {
236
245
fn clone ( & self ) -> Client < C , B > {
237
246
Client {
238
247
connector : self . connector . clone ( ) ,
239
- handle : self . handle . clone ( ) ,
248
+ executor : self . executor . clone ( ) ,
240
249
pool : self . pool . clone ( ) ,
241
250
}
242
251
}
@@ -384,7 +393,18 @@ where C: Connect,
384
393
/// Construct the Client with this configuration.
385
394
#[ inline]
386
395
pub fn build ( self , handle : & Handle ) -> Client < C , B > {
387
- Client :: configured ( self , handle)
396
+ Client :: configured ( self , Exec :: Handle ( handle. clone ( ) ) )
397
+ }
398
+
399
+ /// Construct a Client with this configuration and an executor.
400
+ ///
401
+ /// The executor will be used to spawn "background" connection tasks
402
+ /// to drive requests and responses.
403
+ pub fn executor < E > ( self , executor : E ) -> Client < C , B >
404
+ where
405
+ E : Executor < Background > + ' static ,
406
+ {
407
+ Client :: configured ( self , Exec :: Executor ( Rc :: new ( executor) ) )
388
408
}
389
409
}
390
410
@@ -417,3 +437,65 @@ impl<C: Clone, B> Clone for Config<C, B> {
417
437
}
418
438
}
419
439
}
440
+
441
+
442
+ // ===== impl Exec =====
443
+
444
+ #[ derive( Clone ) ]
445
+ enum Exec {
446
+ Handle ( Handle ) ,
447
+ Executor ( Rc < Executor < Background > > ) ,
448
+ }
449
+
450
+
451
+ impl Exec {
452
+ fn execute < F > ( & self , fut : F ) -> io:: Result < ( ) >
453
+ where
454
+ F : Future < Item =( ) , Error =( ) > + ' static ,
455
+ {
456
+ match * self {
457
+ Exec :: Handle ( ref h) => h. spawn ( fut) ,
458
+ Exec :: Executor ( ref e) => {
459
+ e. execute ( bg ( Box :: new ( fut) ) )
460
+ . map_err ( |err| {
461
+ debug ! ( "executor error: {:?}" , err. kind( ) ) ;
462
+ io:: Error :: new (
463
+ io:: ErrorKind :: Other ,
464
+ "executor error" ,
465
+ )
466
+ } ) ?
467
+ } ,
468
+ }
469
+ Ok ( ( ) )
470
+ }
471
+ }
472
+
473
+ // ===== impl Background =====
474
+
475
+ // The types inside this module are not exported out of the crate,
476
+ // so they are in essence un-nameable.
477
+ mod background {
478
+ use futures:: { Future , Poll } ;
479
+
480
+ // This is basically `impl Future`, since the type is un-nameable,
481
+ // and only implementeds `Future`.
482
+ #[ allow( missing_debug_implementations) ]
483
+ pub struct Background {
484
+ inner : Box < Future < Item =( ) , Error =( ) > > ,
485
+ }
486
+
487
+ pub fn bg ( fut : Box < Future < Item =( ) , Error =( ) > > ) -> Background {
488
+ Background {
489
+ inner : fut,
490
+ }
491
+ }
492
+
493
+ impl Future for Background {
494
+ type Item = ( ) ;
495
+ type Error = ( ) ;
496
+
497
+ fn poll ( & mut self ) -> Poll < Self :: Item , Self :: Error > {
498
+ self . inner . poll ( )
499
+ }
500
+ }
501
+ }
0 commit comments