feat(client): add ability to include SO_REUSEADDR option on sockets
				
					
				
			Closes #1599
This commit is contained in:
		| @@ -403,12 +403,16 @@ mod http { | ||||
|     use self::http_connector::HttpConnectorBlockingTask; | ||||
|  | ||||
|  | ||||
|     fn connect(addr: &SocketAddr, local_addr: &Option<IpAddr>, handle: &Option<Handle>) -> io::Result<ConnectFuture> { | ||||
|     fn connect(addr: &SocketAddr, local_addr: &Option<IpAddr>, handle: &Option<Handle>, reuse_address: bool) -> io::Result<ConnectFuture> { | ||||
|         let builder = match addr { | ||||
|             &SocketAddr::V4(_) => TcpBuilder::new_v4()?, | ||||
|             &SocketAddr::V6(_) => TcpBuilder::new_v6()?, | ||||
|         }; | ||||
|  | ||||
|         if reuse_address { | ||||
|             builder.reuse_address(reuse_address)?; | ||||
|         } | ||||
|  | ||||
|         if let Some(ref local_addr) = *local_addr { | ||||
|             // Caller has requested this socket be bound before calling connect | ||||
|             builder.bind(SocketAddr::new(local_addr.clone(), 0))?; | ||||
| @@ -446,6 +450,7 @@ mod http { | ||||
|         nodelay: bool, | ||||
|         local_address: Option<IpAddr>, | ||||
|         happy_eyeballs_timeout: Option<Duration>, | ||||
|         reuse_address: bool, | ||||
|     } | ||||
|  | ||||
|     impl HttpConnector { | ||||
| @@ -484,6 +489,7 @@ mod http { | ||||
|                 nodelay: false, | ||||
|                 local_address: None, | ||||
|                 happy_eyeballs_timeout: Some(Duration::from_millis(300)), | ||||
|                 reuse_address: false, | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -539,6 +545,15 @@ mod http { | ||||
|         pub fn set_happy_eyeballs_timeout(&mut self, dur: Option<Duration>) { | ||||
|             self.happy_eyeballs_timeout = dur; | ||||
|         } | ||||
|  | ||||
|         /// Set that all socket have `SO_REUSEADDR` set to the supplied value `reuse_address`. | ||||
|         /// | ||||
|         /// Default is `false`. | ||||
|         #[inline] | ||||
|         pub fn set_reuse_address(&mut self, reuse_address: bool) -> &mut Self { | ||||
|             self.reuse_address = reuse_address; | ||||
|             self | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl fmt::Debug for HttpConnector { | ||||
| @@ -585,6 +600,7 @@ mod http { | ||||
|                 keep_alive_timeout: self.keep_alive_timeout, | ||||
|                 nodelay: self.nodelay, | ||||
|                 happy_eyeballs_timeout: self.happy_eyeballs_timeout, | ||||
|                 reuse_address: self.reuse_address, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -597,6 +613,7 @@ mod http { | ||||
|             keep_alive_timeout: None, | ||||
|             nodelay: false, | ||||
|             happy_eyeballs_timeout: None, | ||||
|             reuse_address: false, | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -630,6 +647,7 @@ mod http { | ||||
|         keep_alive_timeout: Option<Duration>, | ||||
|         nodelay: bool, | ||||
|         happy_eyeballs_timeout: Option<Duration>, | ||||
|         reuse_address: bool, | ||||
|     } | ||||
|  | ||||
|     enum State { | ||||
| @@ -652,7 +670,7 @@ mod http { | ||||
|                         // skip resolving the dns and start connecting right away. | ||||
|                         if let Some(addrs) = dns::IpAddrs::try_parse(host, port) { | ||||
|                             state = State::Connecting(ConnectingTcp::new( | ||||
|                                 local_addr, addrs, self.happy_eyeballs_timeout)); | ||||
|                                 local_addr, addrs, self.happy_eyeballs_timeout, self.reuse_address)); | ||||
|                         } else { | ||||
|                             let host = mem::replace(host, String::new()); | ||||
|                             let work = dns::Work::new(host, port); | ||||
| @@ -664,7 +682,7 @@ mod http { | ||||
|                             Async::NotReady => return Ok(Async::NotReady), | ||||
|                             Async::Ready(addrs) => { | ||||
|                                 state = State::Connecting(ConnectingTcp::new( | ||||
|                                     local_addr, addrs, self.happy_eyeballs_timeout)); | ||||
|                                     local_addr, addrs, self.happy_eyeballs_timeout, self.reuse_address)); | ||||
|                             } | ||||
|                         }; | ||||
|                     }, | ||||
| @@ -696,6 +714,7 @@ mod http { | ||||
|         local_addr: Option<IpAddr>, | ||||
|         preferred: ConnectingTcpRemote, | ||||
|         fallback: Option<ConnectingTcpFallback>, | ||||
|         reuse_address: bool, | ||||
|     } | ||||
|  | ||||
|     impl ConnectingTcp { | ||||
| @@ -703,6 +722,7 @@ mod http { | ||||
|             local_addr: Option<IpAddr>, | ||||
|             remote_addrs: dns::IpAddrs, | ||||
|             fallback_timeout: Option<Duration>, | ||||
|             reuse_address: bool, | ||||
|         ) -> ConnectingTcp { | ||||
|             if let Some(fallback_timeout) = fallback_timeout { | ||||
|                 let (preferred_addrs, fallback_addrs) = remote_addrs.split_by_preference(); | ||||
| @@ -711,6 +731,7 @@ mod http { | ||||
|                         local_addr, | ||||
|                         preferred: ConnectingTcpRemote::new(preferred_addrs), | ||||
|                         fallback: None, | ||||
|                         reuse_address, | ||||
|                     }; | ||||
|                 } | ||||
|  | ||||
| @@ -721,12 +742,14 @@ mod http { | ||||
|                         delay: Delay::new(Instant::now() + fallback_timeout), | ||||
|                         remote: ConnectingTcpRemote::new(fallback_addrs), | ||||
|                     }), | ||||
|                     reuse_address, | ||||
|                 } | ||||
|             } else { | ||||
|                 ConnectingTcp { | ||||
|                     local_addr, | ||||
|                     preferred: ConnectingTcpRemote::new(remote_addrs), | ||||
|                     fallback: None, | ||||
|                     reuse_address, | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -757,6 +780,7 @@ mod http { | ||||
|             &mut self, | ||||
|             local_addr: &Option<IpAddr>, | ||||
|             handle: &Option<Handle>, | ||||
|             reuse_address: bool, | ||||
|         ) -> Poll<TcpStream, io::Error> { | ||||
|             let mut err = None; | ||||
|             loop { | ||||
| @@ -768,14 +792,14 @@ mod http { | ||||
|                             err = Some(e); | ||||
|                             if let Some(addr) = self.addrs.next() { | ||||
|                                 debug!("connecting to {}", addr); | ||||
|                                 *current = connect(&addr, local_addr, handle)?; | ||||
|                                 *current = connect(&addr, local_addr, handle, reuse_address)?; | ||||
|                                 continue; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } else if let Some(addr) = self.addrs.next() { | ||||
|                     debug!("connecting to {}", addr); | ||||
|                     self.current = Some(connect(&addr, local_addr, handle)?); | ||||
|                     self.current = Some(connect(&addr, local_addr, handle, reuse_address)?); | ||||
|                     continue; | ||||
|                 } | ||||
|  | ||||
| @@ -788,14 +812,14 @@ mod http { | ||||
|         // not a Future, since passing a &Handle to poll | ||||
|         fn poll(&mut self, handle: &Option<Handle>) -> Poll<TcpStream, io::Error> { | ||||
|             match self.fallback.take() { | ||||
|                 None => self.preferred.poll(&self.local_addr, handle), | ||||
|                 Some(mut fallback) => match self.preferred.poll(&self.local_addr, handle) { | ||||
|                 None => self.preferred.poll(&self.local_addr, handle, self.reuse_address), | ||||
|                 Some(mut fallback) => match self.preferred.poll(&self.local_addr, handle, self.reuse_address) { | ||||
|                     Ok(Async::Ready(stream)) => { | ||||
|                         // Preferred successful - drop fallback. | ||||
|                         Ok(Async::Ready(stream)) | ||||
|                     } | ||||
|                     Ok(Async::NotReady) => match fallback.delay.poll() { | ||||
|                         Ok(Async::Ready(_)) => match fallback.remote.poll(&self.local_addr, handle) { | ||||
|                         Ok(Async::Ready(_)) => match fallback.remote.poll(&self.local_addr, handle, self.reuse_address) { | ||||
|                             Ok(Async::Ready(stream)) => { | ||||
|                                 // Fallback successful - drop current preferred, | ||||
|                                 // but keep fallback as new preferred. | ||||
| @@ -825,7 +849,7 @@ mod http { | ||||
|                     Err(_) => { | ||||
|                         // Preferred failed - use fallback as new preferred. | ||||
|                         self.preferred = fallback.remote; | ||||
|                         self.preferred.poll(&self.local_addr, handle) | ||||
|                         self.preferred.poll(&self.local_addr, handle, self.reuse_address) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| @@ -980,7 +1004,7 @@ mod http { | ||||
|                 } | ||||
|  | ||||
|                 let addrs = hosts.iter().map(|host| (host.clone(), addr.port()).into()).collect(); | ||||
|                 let connecting_tcp = ConnectingTcp::new(None, dns::IpAddrs::new(addrs), Some(fallback_timeout)); | ||||
|                 let connecting_tcp = ConnectingTcp::new(None, dns::IpAddrs::new(addrs), Some(fallback_timeout), false); | ||||
|                 let fut = ConnectingTcpFuture(connecting_tcp); | ||||
|  | ||||
|                 let start = Instant::now(); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user