diff --git a/src/async_impl/client.rs b/src/async_impl/client.rs index 41e85b4..5259149 100644 --- a/src/async_impl/client.rs +++ b/src/async_impl/client.rs @@ -1,6 +1,7 @@ use std::{fmt, str}; use std::sync::Arc; use std::time::Duration; +use std::net::IpAddr; use bytes::Bytes; use futures::{Async, Future, Poll}; @@ -76,6 +77,7 @@ struct Config { #[cfg(feature = "tls")] tls: TlsBackend, http2_only: bool, + local_address: Option, } impl ClientBuilder { @@ -106,6 +108,7 @@ impl ClientBuilder { #[cfg(feature = "tls")] tls: TlsBackend::default(), http2_only: false, + local_address: None, }, } } @@ -137,7 +140,7 @@ impl ClientBuilder { id.add_to_native_tls(&mut tls)?; } - Connector::new_default_tls(tls, proxies.clone())? + Connector::new_default_tls(tls, proxies.clone(), config.local_address)? }, #[cfg(feature = "rustls-tls")] TlsBackend::Rustls => { @@ -166,18 +169,19 @@ impl ClientBuilder { id.add_to_rustls(&mut tls)?; } - Connector::new_rustls_tls(tls, proxies.clone())? + Connector::new_rustls_tls(tls, proxies.clone(), config.local_address)? } } #[cfg(not(feature = "tls"))] - Connector::new(proxies.clone())? + Connector::new(proxies.clone(), config.local_address)? }; let mut builder = ::hyper::Client::builder(); if config.http2_only { builder.http2_only(true); } + let hyper_client = builder.build(connector); let proxies_maybe_http_auth = proxies @@ -325,6 +329,15 @@ impl ClientBuilder { pub fn dns_threads(self, _threads: usize) -> ClientBuilder { self } + + /// Bind to a local IP Address + pub fn local_address(mut self, addr: T) -> ClientBuilder + where + T: Into>, + { + self.config.local_address = addr.into(); + self + } } type HyperClient = ::hyper::Client; diff --git a/src/client.rs b/src/client.rs index b93deee..455ba39 100644 --- a/src/client.rs +++ b/src/client.rs @@ -2,6 +2,7 @@ use std::fmt; use std::sync::Arc; use std::time::Duration; use std::thread; +use std::net::IpAddr; use futures::{Async, Future, Stream}; use futures::future::{self, Either}; @@ -306,6 +307,24 @@ impl ClientBuilder { pub fn h2_prior_knowledge(self) -> ClientBuilder { self.with_inner(|inner| inner.h2_prior_knowledge()) } + + /// Bind to a local IP Address + /// + /// # Example + /// + /// ``` + /// use std::net::IpAddr; + /// let local_addr = IpAddr::from([12, 4, 1, 8]); + /// let client = reqwest::Client::builder() + /// .local_address(local_addr) + /// .build().unwrap(); + /// ``` + pub fn local_address(self, addr: T) -> ClientBuilder + where + T: Into>, + { + self.with_inner(move |inner| inner.local_address(addr)) + } } diff --git a/src/connect.rs b/src/connect.rs index 86f512f..351bd73 100644 --- a/src/connect.rs +++ b/src/connect.rs @@ -3,6 +3,7 @@ use http::uri::Scheme; use hyper::client::connect::{Connect, Connected, Destination}; use tokio_io::{AsyncRead, AsyncWrite}; + #[cfg(feature = "default-tls")] use native_tls::{TlsConnector, TlsConnectorBuilder}; #[cfg(feature = "tls")] @@ -12,6 +13,7 @@ use bytes::BufMut; use std::io; use std::sync::Arc; +use std::net::IpAddr; #[cfg(feature = "trust-dns")] use dns::TrustDnsResolver; @@ -39,8 +41,13 @@ enum Inner { impl Connector { #[cfg(not(feature = "tls"))] - pub(crate) fn new(proxies: Arc>) -> ::Result { - let http = http_connector()?; + pub(crate) fn new(proxies: Arc>, local_addr: T) -> ::Result + where + T: Into> + { + + let mut http = http_connector()?; + http.set_local_address(local_addr.into()); Ok(Connector { proxies, inner: Inner::Http(http) @@ -48,10 +55,17 @@ impl Connector { } #[cfg(feature = "default-tls")] - pub(crate) fn new_default_tls(tls: TlsConnectorBuilder, proxies: Arc>) -> ::Result { + pub(crate) fn new_default_tls( + tls: TlsConnectorBuilder, + proxies: Arc>, + local_addr: T) -> ::Result + where + T: Into>, + { let tls = try_!(tls.build()); let mut http = http_connector()?; + http.set_local_address(local_addr.into()); http.enforce_http(false); let http = ::hyper_tls::HttpsConnector::from((http, tls.clone())); @@ -62,8 +76,15 @@ impl Connector { } #[cfg(feature = "rustls-tls")] - pub(crate) fn new_rustls_tls(tls: rustls::ClientConfig, proxies: Arc>) -> ::Result { + pub(crate) fn new_rustls_tls( + tls: rustls::ClientConfig, + proxies: Arc>, + local_addr: T) -> ::Result + where + T: Into>, + { let mut http = http_connector()?; + http.set_local_address(local_addr.into()); http.enforce_http(false); let http = ::hyper_rustls::HttpsConnector::from((http, tls.clone()));