11use std:: {
22 io:: { self , Read , Write } ,
3- net:: { TcpStream , ToSocketAddrs } ,
3+ net:: { SocketAddr , TcpStream , ToSocketAddrs } ,
44 sync:: Arc ,
55 time:: Duration ,
66} ;
@@ -10,7 +10,7 @@ use derivative::Derivative;
1010use webpki:: DNSNameRef ;
1111
1212use crate :: {
13- error:: Result ,
13+ error:: { ErrorKind , Result } ,
1414 options:: { StreamAddress , TlsOptions } ,
1515} ;
1616
@@ -38,27 +38,52 @@ pub(super) enum Stream {
3838 ) ,
3939}
4040
41+ fn try_connect ( address : & SocketAddr , timeout : Duration ) -> Result < TcpStream > {
42+ // The URI options spec requires that the default connect timeout is 10 seconds, but that 0
43+ // should indicate no timeout.
44+ let stream = if timeout == Duration :: from_secs ( 0 ) {
45+ TcpStream :: connect ( address) ?
46+ } else {
47+ TcpStream :: connect_timeout ( address, timeout) ?
48+ } ;
49+
50+ Ok ( stream)
51+ }
52+
53+ fn connect_stream ( address : & StreamAddress , connect_timeout : Option < Duration > ) -> Result < TcpStream > {
54+ let timeout = connect_timeout. unwrap_or ( DEFAULT_CONNECT_TIMEOUT ) ;
55+
56+ let mut socket_addrs: Vec < _ > = address. to_socket_addrs ( ) ?. collect ( ) ;
57+
58+ if socket_addrs. is_empty ( ) {
59+ return Err ( ErrorKind :: NoDnsResults ( address. clone ( ) ) . into ( ) ) ;
60+ }
61+
62+ // After considering various approaches, we decided to do what other drivers do, namely try each
63+ // of the addresses in sequence with a preference for IPv4.
64+ socket_addrs. sort_by_key ( |addr| if addr. is_ipv4 ( ) { 0 } else { 1 } ) ;
65+
66+ let mut connect_error = None ;
67+
68+ for address in & socket_addrs {
69+ connect_error = match try_connect ( address, timeout) {
70+ Ok ( stream) => return Ok ( stream) ,
71+ Err ( err) => Some ( err) ,
72+ } ;
73+ }
74+
75+ Err ( connect_error. unwrap_or_else ( || ErrorKind :: NoDnsResults ( address. clone ( ) ) . into ( ) ) )
76+ }
77+
4178impl Stream {
4279 /// Creates a new stream connected to `address`.
4380 pub ( super ) fn connect (
4481 host : StreamAddress ,
4582 connect_timeout : Option < Duration > ,
4683 tls_options : Option < TlsOptions > ,
4784 ) -> Result < Self > {
48- let timeout = connect_timeout. unwrap_or ( DEFAULT_CONNECT_TIMEOUT ) ;
49-
50- // The URI options spec requires that the default is 10 seconds, but that 0 should indicate
51- // no timeout.
52- let inner = if timeout == Duration :: from_secs ( 0 ) {
53- TcpStream :: connect ( & host) ?
54- } else {
55- let mut socket_addrs: Vec < _ > = host. to_socket_addrs ( ) ?. collect ( ) ;
56- socket_addrs. sort_by_key ( |addr| if addr. is_ipv4 ( ) { 0 } else { 1 } ) ;
57-
58- TcpStream :: connect_timeout ( & socket_addrs[ 0 ] , timeout) ?
59- } ;
85+ let inner = connect_stream ( & host, connect_timeout) ?;
6086 inner. set_nodelay ( true ) ?;
61- let inner = inner;
6287
6388 match tls_options {
6489 Some ( cfg) => {
0 commit comments