@@ -10,7 +10,7 @@ use futures_util::{TryFutureExt, FutureExt};
10
10
use net2:: TcpBuilder ;
11
11
use tokio_net:: driver:: Handle ;
12
12
use tokio_net:: tcp:: TcpStream ;
13
- use tokio_timer:: Delay ;
13
+ use tokio_timer:: { Delay , Timeout } ;
14
14
15
15
use crate :: common:: { Future , Pin , Poll , task} ;
16
16
use super :: { Connect , Connected , Destination } ;
@@ -32,6 +32,7 @@ type ConnectFuture = Pin<Box<dyn Future<Output = io::Result<TcpStream>> + Send>>
32
32
pub struct HttpConnector < R = GaiResolver > {
33
33
enforce_http : bool ,
34
34
handle : Option < Handle > ,
35
+ connect_timeout : Option < Duration > ,
35
36
happy_eyeballs_timeout : Option < Duration > ,
36
37
keep_alive_timeout : Option < Duration > ,
37
38
local_address : Option < IpAddr > ,
@@ -101,6 +102,7 @@ impl<R> HttpConnector<R> {
101
102
HttpConnector {
102
103
enforce_http : true ,
103
104
handle : None ,
105
+ connect_timeout : None ,
104
106
happy_eyeballs_timeout : Some ( Duration :: from_millis ( 300 ) ) ,
105
107
keep_alive_timeout : None ,
106
108
local_address : None ,
@@ -168,6 +170,17 @@ impl<R> HttpConnector<R> {
168
170
self . local_address = addr;
169
171
}
170
172
173
+ /// Set the connect timeout.
174
+ ///
175
+ /// If a domain resolves to multiple IP addresses, the timeout will be
176
+ /// evenly divided across them.
177
+ ///
178
+ /// Default is `None`.
179
+ #[ inline]
180
+ pub fn set_connect_timeout ( & mut self , dur : Option < Duration > ) {
181
+ self . connect_timeout = dur;
182
+ }
183
+
171
184
/// Set timeout for [RFC 6555 (Happy Eyeballs)][RFC 6555] algorithm.
172
185
///
173
186
/// If hostname resolves to both IPv4 and IPv6 addresses and connection
@@ -240,6 +253,7 @@ where
240
253
HttpConnecting {
241
254
state : State :: Lazy ( self . resolver . clone ( ) , host. into ( ) , self . local_address ) ,
242
255
handle : self . handle . clone ( ) ,
256
+ connect_timeout : self . connect_timeout ,
243
257
happy_eyeballs_timeout : self . happy_eyeballs_timeout ,
244
258
keep_alive_timeout : self . keep_alive_timeout ,
245
259
nodelay : self . nodelay ,
@@ -295,6 +309,7 @@ where
295
309
let fut = HttpConnecting {
296
310
state : State :: Lazy ( self . resolver . clone ( ) , host. into ( ) , self . local_address ) ,
297
311
handle : self . handle . clone ( ) ,
312
+ connect_timeout : self . connect_timeout ,
298
313
happy_eyeballs_timeout : self . happy_eyeballs_timeout ,
299
314
keep_alive_timeout : self . keep_alive_timeout ,
300
315
nodelay : self . nodelay ,
@@ -323,6 +338,7 @@ fn invalid_url<R: Resolve>(err: InvalidUrl, handle: &Option<Handle>) -> HttpConn
323
338
keep_alive_timeout : None ,
324
339
nodelay : false ,
325
340
port : 0 ,
341
+ connect_timeout : None ,
326
342
happy_eyeballs_timeout : None ,
327
343
reuse_address : false ,
328
344
send_buffer_size : None ,
@@ -357,6 +373,7 @@ impl StdError for InvalidUrl {
357
373
pub struct HttpConnecting < R : Resolve = GaiResolver > {
358
374
state : State < R > ,
359
375
handle : Option < Handle > ,
376
+ connect_timeout : Option < Duration > ,
360
377
happy_eyeballs_timeout : Option < Duration > ,
361
378
keep_alive_timeout : Option < Duration > ,
362
379
nodelay : bool ,
@@ -389,7 +406,7 @@ where
389
406
// skip resolving the dns and start connecting right away.
390
407
if let Some ( addrs) = dns:: IpAddrs :: try_parse ( host, me. port ) {
391
408
state = State :: Connecting ( ConnectingTcp :: new (
392
- local_addr, addrs, me. happy_eyeballs_timeout , me. reuse_address ) ) ;
409
+ local_addr, addrs, me. connect_timeout , me . happy_eyeballs_timeout , me. reuse_address ) ) ;
393
410
} else {
394
411
let name = dns:: Name :: new ( mem:: replace ( host, String :: new ( ) ) ) ;
395
412
state = State :: Resolving ( resolver. resolve ( name) , local_addr) ;
@@ -403,7 +420,7 @@ where
403
420
. collect ( ) ;
404
421
let addrs = dns:: IpAddrs :: new ( addrs) ;
405
422
state = State :: Connecting ( ConnectingTcp :: new (
406
- local_addr, addrs, me. happy_eyeballs_timeout , me. reuse_address ) ) ;
423
+ local_addr, addrs, me. connect_timeout , me . happy_eyeballs_timeout , me. reuse_address ) ) ;
407
424
} ,
408
425
State :: Connecting ( ref mut c) => {
409
426
let sock = ready ! ( c. poll( cx, & me. handle) ) ?;
@@ -454,6 +471,7 @@ impl ConnectingTcp {
454
471
fn new (
455
472
local_addr : Option < IpAddr > ,
456
473
remote_addrs : dns:: IpAddrs ,
474
+ connect_timeout : Option < Duration > ,
457
475
fallback_timeout : Option < Duration > ,
458
476
reuse_address : bool ,
459
477
) -> ConnectingTcp {
@@ -462,25 +480,25 @@ impl ConnectingTcp {
462
480
if fallback_addrs. is_empty ( ) {
463
481
return ConnectingTcp {
464
482
local_addr,
465
- preferred : ConnectingTcpRemote :: new ( preferred_addrs) ,
483
+ preferred : ConnectingTcpRemote :: new ( preferred_addrs, connect_timeout ) ,
466
484
fallback : None ,
467
485
reuse_address,
468
486
} ;
469
487
}
470
488
471
489
ConnectingTcp {
472
490
local_addr,
473
- preferred : ConnectingTcpRemote :: new ( preferred_addrs) ,
491
+ preferred : ConnectingTcpRemote :: new ( preferred_addrs, connect_timeout ) ,
474
492
fallback : Some ( ConnectingTcpFallback {
475
493
delay : tokio_timer:: delay_for ( fallback_timeout) ,
476
- remote : ConnectingTcpRemote :: new ( fallback_addrs) ,
494
+ remote : ConnectingTcpRemote :: new ( fallback_addrs, connect_timeout ) ,
477
495
} ) ,
478
496
reuse_address,
479
497
}
480
498
} else {
481
499
ConnectingTcp {
482
500
local_addr,
483
- preferred : ConnectingTcpRemote :: new ( remote_addrs) ,
501
+ preferred : ConnectingTcpRemote :: new ( remote_addrs, connect_timeout ) ,
484
502
fallback : None ,
485
503
reuse_address,
486
504
}
@@ -495,13 +513,17 @@ struct ConnectingTcpFallback {
495
513
496
514
struct ConnectingTcpRemote {
497
515
addrs : dns:: IpAddrs ,
516
+ connect_timeout : Option < Duration > ,
498
517
current : Option < ConnectFuture > ,
499
518
}
500
519
501
520
impl ConnectingTcpRemote {
502
- fn new ( addrs : dns:: IpAddrs ) -> Self {
521
+ fn new ( addrs : dns:: IpAddrs , connect_timeout : Option < Duration > ) -> Self {
522
+ let connect_timeout = connect_timeout. map ( |t| t / ( addrs. len ( ) as u32 ) ) ;
523
+
503
524
Self {
504
525
addrs,
526
+ connect_timeout,
505
527
current : None ,
506
528
}
507
529
}
@@ -530,14 +552,14 @@ impl ConnectingTcpRemote {
530
552
err = Some ( e) ;
531
553
if let Some ( addr) = self . addrs . next ( ) {
532
554
debug ! ( "connecting to {}" , addr) ;
533
- * current = connect ( & addr, local_addr, handle, reuse_address) ?;
555
+ * current = connect ( & addr, local_addr, handle, reuse_address, self . connect_timeout ) ?;
534
556
continue ;
535
557
}
536
558
}
537
559
}
538
560
} else if let Some ( addr) = self . addrs . next ( ) {
539
561
debug ! ( "connecting to {}" , addr) ;
540
- self . current = Some ( connect ( & addr, local_addr, handle, reuse_address) ?) ;
562
+ self . current = Some ( connect ( & addr, local_addr, handle, reuse_address, self . connect_timeout ) ?) ;
541
563
continue ;
542
564
}
543
565
@@ -546,7 +568,13 @@ impl ConnectingTcpRemote {
546
568
}
547
569
}
548
570
549
- fn connect ( addr : & SocketAddr , local_addr : & Option < IpAddr > , handle : & Option < Handle > , reuse_address : bool ) -> io:: Result < ConnectFuture > {
571
+ fn connect (
572
+ addr : & SocketAddr ,
573
+ local_addr : & Option < IpAddr > ,
574
+ handle : & Option < Handle > ,
575
+ reuse_address : bool ,
576
+ connect_timeout : Option < Duration > ,
577
+ ) -> io:: Result < ConnectFuture > {
550
578
let builder = match addr {
551
579
& SocketAddr :: V4 ( _) => TcpBuilder :: new_v4 ( ) ?,
552
580
& SocketAddr :: V6 ( _) => TcpBuilder :: new_v6 ( ) ?,
@@ -581,10 +609,16 @@ fn connect(addr: &SocketAddr, local_addr: &Option<IpAddr>, handle: &Option<Handl
581
609
let std_tcp = builder. to_tcp_stream ( ) ?;
582
610
583
611
Ok ( Box :: pin ( async move {
584
- TcpStream :: connect_std ( std_tcp, & addr, & handle) . await
612
+ let connect = TcpStream :: connect_std ( std_tcp, & addr, & handle) ;
613
+ match connect_timeout {
614
+ Some ( timeout) => match Timeout :: new ( connect, timeout) . await {
615
+ Ok ( Ok ( s) ) => Ok ( s) ,
616
+ Ok ( Err ( e) ) => Err ( e) ,
617
+ Err ( e) => Err ( io:: Error :: new ( io:: ErrorKind :: TimedOut , e) ) ,
618
+ }
619
+ None => connect. await ,
620
+ }
585
621
} ) )
586
-
587
- //Ok(Box::pin(TcpStream::connect_std(std_tcp, addr, &handle)))
588
622
}
589
623
590
624
impl ConnectingTcp {
@@ -673,7 +707,6 @@ mod tests {
673
707
} )
674
708
}
675
709
676
-
677
710
#[ test]
678
711
fn test_errors_missing_scheme ( ) {
679
712
let mut rt = Runtime :: new ( ) . unwrap ( ) ;
@@ -765,7 +798,7 @@ mod tests {
765
798
}
766
799
767
800
let addrs = hosts. iter ( ) . map ( |host| ( host. clone ( ) , addr. port ( ) ) . into ( ) ) . collect ( ) ;
768
- let connecting_tcp = ConnectingTcp :: new ( None , dns:: IpAddrs :: new ( addrs) , Some ( fallback_timeout) , false ) ;
801
+ let connecting_tcp = ConnectingTcp :: new ( None , dns:: IpAddrs :: new ( addrs) , None , Some ( fallback_timeout) , false ) ;
769
802
let fut = ConnectingTcpFuture ( connecting_tcp) ;
770
803
771
804
let start = Instant :: now ( ) ;
0 commit comments