diff --git a/Cargo.toml b/Cargo.toml index 3e7d9f9fb..4f7d6df19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -119,7 +119,7 @@ encoding_rs = { version = "0.8", optional = true } http-body = "1" http-body-util = "0.1" hyper = { version = "1.1", features = ["http1", "client"] } -hyper-util = { version = "0.1.10", features = ["http1", "client", "client-legacy", "tokio"] } +hyper-util = { version = "0.1.11", features = ["http1", "client", "client-legacy", "tokio"] } h2 = { version = "0.4", optional = true } once_cell = "1.18" log = "0.4.17" diff --git a/src/async_impl/client.rs b/src/async_impl/client.rs index 17e15a5d6..c321d5695 100644 --- a/src/async_impl/client.rs +++ b/src/async_impl/client.rs @@ -158,7 +158,18 @@ struct Config { #[cfg(feature = "http2")] http2_keep_alive_while_idle: bool, local_address: Option, - #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "solaris", + target_os = "tvos", + target_os = "visionos", + target_os = "watchos", + ))] interface: Option, nodelay: bool, #[cfg(feature = "cookies")] @@ -262,7 +273,18 @@ impl ClientBuilder { #[cfg(feature = "http2")] http2_keep_alive_while_idle: false, local_address: None, - #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "solaris", + target_os = "tvos", + target_os = "visionos", + target_os = "watchos", + ))] interface: None, nodelay: true, hickory_dns: cfg!(feature = "hickory-dns"), @@ -462,7 +484,14 @@ impl ClientBuilder { #[cfg(any( target_os = "android", target_os = "fuchsia", - target_os = "linux" + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "solaris", + target_os = "tvos", + target_os = "visionos", + target_os = "watchos", ))] config.interface.as_deref(), config.nodelay, @@ -506,7 +535,14 @@ impl ClientBuilder { #[cfg(any( target_os = "android", target_os = "fuchsia", - target_os = "linux" + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "solaris", + target_os = "tvos", + target_os = "visionos", + target_os = "watchos", ))] config.interface.as_deref(), config.nodelay, @@ -701,7 +737,14 @@ impl ClientBuilder { #[cfg(any( target_os = "android", target_os = "fuchsia", - target_os = "linux" + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "solaris", + target_os = "tvos", + target_os = "visionos", + target_os = "watchos", ))] config.interface.as_deref(), config.nodelay, @@ -721,7 +764,18 @@ impl ClientBuilder { http, proxies.clone(), config.local_address, - #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "solaris", + target_os = "tvos", + target_os = "visionos", + target_os = "watchos", + ))] config.interface.as_deref(), config.nodelay, ) @@ -1403,7 +1457,23 @@ impl ClientBuilder { self } - /// Bind to an interface by `SO_BINDTODEVICE`. + /// Bind connections only on the specified network interface. + /// + /// This option is only available on the following operating systems: + /// + /// - Android + /// - Fuchsia + /// - Linux, + /// - macOS and macOS-like systems (iOS, tvOS, watchOS and visionOS) + /// - Solaris and illumos + /// + /// On Android, Linux, and Fuchsia, this uses the + /// [`SO_BINDTODEVICE`][man-7-socket] socket option. On macOS and macOS-like + /// systems, Solaris, and illumos, this instead uses the [`IP_BOUND_IF` and + /// `IPV6_BOUND_IF`][man-7p-ip] socket options (as appropriate). + /// + /// Note that connections will fail if the provided interface name is not a + /// network interface that currently exists when a connection is established. /// /// # Example /// @@ -1415,7 +1485,21 @@ impl ClientBuilder { /// .interface(interface) /// .build().unwrap(); /// ``` - #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + /// + /// [man-7-socket]: https://man7.org/linux/man-pages/man7/socket.7.html + /// [man-7p-ip]: https://docs.oracle.com/cd/E86824_01/html/E54777/ip-7p.html + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "solaris", + target_os = "tvos", + target_os = "visionos", + target_os = "watchos", + ))] pub fn interface(mut self, interface: &str) -> ClientBuilder { self.config.interface = Some(interface.to_string()); self @@ -2355,7 +2439,18 @@ impl Config { f.field("local_address", v); } - #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "solaris", + target_os = "tvos", + target_os = "visionos", + target_os = "watchos", + ))] if let Some(ref v) = self.interface { f.field("interface", v); } diff --git a/src/connect.rs b/src/connect.rs index 609982b50..e8628b4a9 100644 --- a/src/connect.rs +++ b/src/connect.rs @@ -148,7 +148,18 @@ where { mut http: HttpConnector, proxies: Arc>, local_addr: T, - #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "solaris", + target_os = "tvos", + target_os = "visionos", + target_os = "watchos", + ))] interface: Option<&str>, nodelay: bool, ) -> ConnectorBuilder @@ -156,7 +167,18 @@ where { T: Into>, { http.set_local_address(local_addr.into()); - #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "solaris", + target_os = "tvos", + target_os = "visionos", + target_os = "watchos", + ))] if let Some(interface) = interface { http.set_interface(interface.to_owned()); } @@ -177,7 +199,18 @@ where { proxies: Arc>, user_agent: Option, local_addr: T, - #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "solaris", + target_os = "tvos", + target_os = "visionos", + target_os = "watchos", + ))] interface: Option<&str>, nodelay: bool, tls_info: bool, @@ -192,7 +225,18 @@ where { proxies, user_agent, local_addr, - #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "solaris", + target_os = "tvos", + target_os = "visionos", + target_os = "watchos", + ))] interface, nodelay, tls_info, @@ -206,7 +250,18 @@ where { proxies: Arc>, user_agent: Option, local_addr: T, - #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "solaris", + target_os = "tvos", + target_os = "visionos", + target_os = "watchos", + ))] interface: Option<&str>, nodelay: bool, tls_info: bool, @@ -215,7 +270,18 @@ where { T: Into>, { http.set_local_address(local_addr.into()); - #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "solaris", + target_os = "tvos", + target_os = "visionos", + target_os = "watchos", + ))] if let Some(interface) = interface { http.set_interface(interface); } @@ -240,7 +306,18 @@ where { proxies: Arc>, user_agent: Option, local_addr: T, - #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "solaris", + target_os = "tvos", + target_os = "visionos", + target_os = "watchos", + ))] interface: Option<&str>, nodelay: bool, tls_info: bool, @@ -249,7 +326,18 @@ where { T: Into>, { http.set_local_address(local_addr.into()); - #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "solaris", + target_os = "tvos", + target_os = "visionos", + target_os = "watchos", + ))] if let Some(interface) = interface { http.set_interface(interface.to_owned()); }