diff --git a/src/client/connect/http.rs b/src/client/connect/http.rs index afe7b155eb..569cfc1b56 100644 --- a/src/client/connect/http.rs +++ b/src/client/connect/http.rs @@ -77,6 +77,7 @@ struct Config { keep_alive_timeout: Option, local_address_ipv4: Option, local_address_ipv6: Option, + local_device: Option>, nodelay: bool, reuse_address: bool, send_buffer_size: Option, @@ -117,6 +118,7 @@ impl HttpConnector { keep_alive_timeout: None, local_address_ipv4: None, local_address_ipv6: None, + local_device: None, nodelay: false, reuse_address: false, send_buffer_size: None, @@ -193,6 +195,21 @@ impl HttpConnector { cfg.local_address_ipv6 = Some(addr_ipv6); } + /// Set that all sockets are bound to the configured interface + /// + /// # Example + /// + /// ``` + /// let mut http = HttpConnector::new(); + /// http.set_local_device(Some("wlan0".into())); + /// ``` + #[inline] + pub fn set_local_device(&mut self, device: Option>) { + let cfg = self.config_mut(); + + cfg.local_device = device; + } + /// Set the connect timeout. /// /// If a domain resolves to multiple IP addresses, the timeout will be @@ -564,7 +581,11 @@ fn bind_local_address( local_addr_ipv6: &Option, ) -> io::Result<()> { match (*dst_addr, local_addr_ipv4, local_addr_ipv6) { - (SocketAddr::V4(_), Some(addr), _) => { + (SocketAddr::V4(_), Some(addr), None) => { + socket.bind(&SocketAddr::new(addr.clone().into(), 0).into())?; + } + (SocketAddr::V4(_), None, Some(addr)) => { + // Connection from IPv6 local to IPv4 remote socket.bind(&SocketAddr::new(addr.clone().into(), 0).into())?; } (SocketAddr::V6(_), _, Some(addr)) => { @@ -585,6 +606,17 @@ fn bind_local_address( Ok(()) } +fn bind_local_device( + socket: &socket2::Socket, + device_interface: &Option>, +) -> io::Result<()> { + // None value would unbind the device + if let Some(device) = device_interface { + socket.bind_device(Some(device.as_slice()))?; + } + Ok(()) +} + fn connect( addr: &SocketAddr, config: &Config, @@ -619,7 +651,15 @@ fn connect( &config.local_address_ipv4, &config.local_address_ipv6, ) - .map_err(ConnectError::m("tcp bind local error"))?; + .map_err(ConnectError::m("tcp bind local IP error"))?; + + + bind_local_device( + &socket, + &config.local_device, + ) + .map_err(ConnectError::m("tcp bind local device error"))?; + #[cfg(unix)] let socket = unsafe { @@ -934,6 +974,7 @@ mod tests { let cfg = Config { local_address_ipv4: None, local_address_ipv6: None, + local_device: None, connect_timeout: None, keep_alive_timeout: None, happy_eyeballs_timeout: Some(fallback_timeout),