1use std::error::Error as StdError;
2use std::fmt;
3use std::io;
4use std::marker::PhantomData;
5use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
6use std::pin::{Pin, pin};
7use std::sync::Arc;
8use std::task::{self, Poll, ready};
9use std::time::Duration;
10
11use futures_util::future::Either;
12use http::uri::{Scheme, Uri};
13use pin_project_lite::pin_project;
14use socket2::TcpKeepalive;
15use tokio::net::{TcpSocket, TcpStream};
16use tokio::time::Sleep;
17use tracing::{debug, trace, warn};
18
19use super::dns::{self, GaiResolver, Resolve, resolve};
20use super::{Connected, Connection};
21use crate::rt::TokioIo;
22
23#[derive(Clone)]
32pub struct HttpConnector<R = GaiResolver> {
33 config: Arc<Config>,
34 resolver: R,
35}
36
37#[derive(Clone, Debug)]
61pub struct HttpInfo {
62 remote_addr: SocketAddr,
63 local_addr: SocketAddr,
64}
65
66#[derive(Clone)]
67struct Config {
68 connect_timeout: Option<Duration>,
69 enforce_http: bool,
70 happy_eyeballs_timeout: Option<Duration>,
71 tcp_keepalive_config: TcpKeepaliveConfig,
72 local_address_ipv4: Option<Ipv4Addr>,
73 local_address_ipv6: Option<Ipv6Addr>,
74 nodelay: bool,
75 reuse_address: bool,
76 send_buffer_size: Option<usize>,
77 recv_buffer_size: Option<usize>,
78 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
79 interface: Option<String>,
80 #[cfg(any(
81 target_os = "illumos",
82 target_os = "ios",
83 target_os = "macos",
84 target_os = "solaris",
85 target_os = "tvos",
86 target_os = "visionos",
87 target_os = "watchos",
88 ))]
89 interface: Option<std::ffi::CString>,
90 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
91 mark: Option<u32>,
92 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
93 tcp_user_timeout: Option<Duration>,
94}
95
96#[derive(Default, Debug, Clone, Copy)]
97struct TcpKeepaliveConfig {
98 time: Option<Duration>,
99 interval: Option<Duration>,
100 retries: Option<u32>,
101}
102
103impl TcpKeepaliveConfig {
104 fn into_tcpkeepalive(self) -> Option<TcpKeepalive> {
106 let mut dirty = false;
107 let mut ka = TcpKeepalive::new();
108 if let Some(time) = self.time {
109 ka = ka.with_time(time);
110 dirty = true
111 }
112 if let Some(interval) = self.interval {
113 ka = Self::ka_with_interval(ka, interval, &mut dirty)
114 };
115 if let Some(retries) = self.retries {
116 ka = Self::ka_with_retries(ka, retries, &mut dirty)
117 };
118 if dirty { Some(ka) } else { None }
119 }
120
121 #[cfg(
122 any(
124 target_os = "android",
125 target_os = "dragonfly",
126 target_os = "freebsd",
127 target_os = "fuchsia",
128 target_os = "illumos",
129 target_os = "ios",
130 target_os = "visionos",
131 target_os = "linux",
132 target_os = "macos",
133 target_os = "netbsd",
134 target_os = "tvos",
135 target_os = "watchos",
136 target_os = "windows",
137 )
138 )]
139 fn ka_with_interval(ka: TcpKeepalive, interval: Duration, dirty: &mut bool) -> TcpKeepalive {
140 *dirty = true;
141 ka.with_interval(interval)
142 }
143
144 #[cfg(not(
145 any(
147 target_os = "android",
148 target_os = "dragonfly",
149 target_os = "freebsd",
150 target_os = "fuchsia",
151 target_os = "illumos",
152 target_os = "ios",
153 target_os = "visionos",
154 target_os = "linux",
155 target_os = "macos",
156 target_os = "netbsd",
157 target_os = "tvos",
158 target_os = "watchos",
159 target_os = "windows",
160 )
161 ))]
162 fn ka_with_interval(ka: TcpKeepalive, _: Duration, _: &mut bool) -> TcpKeepalive {
163 ka }
165
166 #[cfg(
167 any(
169 target_os = "android",
170 target_os = "dragonfly",
171 target_os = "freebsd",
172 target_os = "fuchsia",
173 target_os = "illumos",
174 target_os = "ios",
175 target_os = "visionos",
176 target_os = "linux",
177 target_os = "macos",
178 target_os = "netbsd",
179 target_os = "tvos",
180 target_os = "watchos",
181 )
182 )]
183 fn ka_with_retries(ka: TcpKeepalive, retries: u32, dirty: &mut bool) -> TcpKeepalive {
184 *dirty = true;
185 ka.with_retries(retries)
186 }
187
188 #[cfg(not(
189 any(
191 target_os = "android",
192 target_os = "dragonfly",
193 target_os = "freebsd",
194 target_os = "fuchsia",
195 target_os = "illumos",
196 target_os = "ios",
197 target_os = "visionos",
198 target_os = "linux",
199 target_os = "macos",
200 target_os = "netbsd",
201 target_os = "tvos",
202 target_os = "watchos",
203 )
204 ))]
205 fn ka_with_retries(ka: TcpKeepalive, _: u32, _: &mut bool) -> TcpKeepalive {
206 ka }
208}
209
210impl HttpConnector {
213 pub fn new() -> HttpConnector {
215 HttpConnector::new_with_resolver(GaiResolver::new())
216 }
217}
218
219impl<R> HttpConnector<R> {
220 pub fn new_with_resolver(resolver: R) -> HttpConnector<R> {
224 HttpConnector {
225 config: Arc::new(Config {
226 connect_timeout: None,
227 enforce_http: true,
228 happy_eyeballs_timeout: Some(Duration::from_millis(300)),
229 tcp_keepalive_config: TcpKeepaliveConfig::default(),
230 local_address_ipv4: None,
231 local_address_ipv6: None,
232 nodelay: false,
233 reuse_address: false,
234 send_buffer_size: None,
235 recv_buffer_size: None,
236 #[cfg(any(
237 target_os = "android",
238 target_os = "fuchsia",
239 target_os = "illumos",
240 target_os = "ios",
241 target_os = "linux",
242 target_os = "macos",
243 target_os = "solaris",
244 target_os = "tvos",
245 target_os = "visionos",
246 target_os = "watchos",
247 ))]
248 interface: None,
249 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
250 mark: None,
251 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
252 tcp_user_timeout: None,
253 }),
254 resolver,
255 }
256 }
257
258 #[inline]
262 pub fn enforce_http(&mut self, is_enforced: bool) {
263 self.config_mut().enforce_http = is_enforced;
264 }
265
266 #[inline]
273 pub fn set_keepalive(&mut self, time: Option<Duration>) {
274 self.config_mut().tcp_keepalive_config.time = time;
275 }
276
277 #[inline]
280 pub fn set_keepalive_interval(&mut self, interval: Option<Duration>) {
281 self.config_mut().tcp_keepalive_config.interval = interval;
282 }
283
284 #[inline]
286 pub fn set_keepalive_retries(&mut self, retries: Option<u32>) {
287 self.config_mut().tcp_keepalive_config.retries = retries;
288 }
289
290 #[inline]
294 pub fn set_nodelay(&mut self, nodelay: bool) {
295 self.config_mut().nodelay = nodelay;
296 }
297
298 #[inline]
300 pub fn set_send_buffer_size(&mut self, size: Option<usize>) {
301 self.config_mut().send_buffer_size = size;
302 }
303
304 #[inline]
306 pub fn set_recv_buffer_size(&mut self, size: Option<usize>) {
307 self.config_mut().recv_buffer_size = size;
308 }
309
310 #[inline]
316 pub fn set_local_address(&mut self, addr: Option<IpAddr>) {
317 let (v4, v6) = match addr {
318 Some(IpAddr::V4(a)) => (Some(a), None),
319 Some(IpAddr::V6(a)) => (None, Some(a)),
320 _ => (None, None),
321 };
322
323 let cfg = self.config_mut();
324
325 cfg.local_address_ipv4 = v4;
326 cfg.local_address_ipv6 = v6;
327 }
328
329 #[inline]
332 pub fn set_local_addresses(&mut self, addr_ipv4: Ipv4Addr, addr_ipv6: Ipv6Addr) {
333 let cfg = self.config_mut();
334
335 cfg.local_address_ipv4 = Some(addr_ipv4);
336 cfg.local_address_ipv6 = Some(addr_ipv6);
337 }
338
339 #[inline]
346 pub fn set_connect_timeout(&mut self, dur: Option<Duration>) {
347 self.config_mut().connect_timeout = dur;
348 }
349
350 #[inline]
363 pub fn set_happy_eyeballs_timeout(&mut self, dur: Option<Duration>) {
364 self.config_mut().happy_eyeballs_timeout = dur;
365 }
366
367 #[inline]
371 pub fn set_reuse_address(&mut self, reuse_address: bool) -> &mut Self {
372 self.config_mut().reuse_address = reuse_address;
373 self
374 }
375
376 #[cfg(any(
401 target_os = "android",
402 target_os = "fuchsia",
403 target_os = "illumos",
404 target_os = "ios",
405 target_os = "linux",
406 target_os = "macos",
407 target_os = "solaris",
408 target_os = "tvos",
409 target_os = "visionos",
410 target_os = "watchos",
411 ))]
412 #[inline]
413 pub fn set_interface<S: Into<String>>(&mut self, interface: S) -> &mut Self {
414 let interface = interface.into();
415 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
416 {
417 self.config_mut().interface = Some(interface);
418 }
419 #[cfg(not(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))]
420 {
421 let interface = std::ffi::CString::new(interface)
422 .expect("interface name should not have nulls in it");
423 self.config_mut().interface = Some(interface);
424 }
425 self
426 }
427
428 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
443 #[inline]
444 pub fn set_mark(&mut self, mark: Option<u32>) {
445 self.config_mut().mark = mark;
446 }
447
448 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
450 #[inline]
451 pub fn set_tcp_user_timeout(&mut self, time: Option<Duration>) {
452 self.config_mut().tcp_user_timeout = time;
453 }
454
455 fn config_mut(&mut self) -> &mut Config {
458 Arc::make_mut(&mut self.config)
462 }
463}
464
465static INVALID_NOT_HTTP: &str = "invalid URL, scheme is not http";
466static INVALID_MISSING_SCHEME: &str = "invalid URL, scheme is missing";
467static INVALID_MISSING_HOST: &str = "invalid URL, host is missing";
468
469impl<R: fmt::Debug> fmt::Debug for HttpConnector<R> {
471 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
472 f.debug_struct("HttpConnector").finish()
473 }
474}
475
476impl<R> tower_service::Service<Uri> for HttpConnector<R>
477where
478 R: Resolve + Clone + Send + Sync + 'static,
479 R::Future: Send,
480{
481 type Response = TokioIo<TcpStream>;
482 type Error = ConnectError;
483 type Future = HttpConnecting<R>;
484
485 fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
486 ready!(self.resolver.poll_ready(cx)).map_err(ConnectError::dns)?;
487 Poll::Ready(Ok(()))
488 }
489
490 fn call(&mut self, dst: Uri) -> Self::Future {
491 let mut self_ = self.clone();
492 HttpConnecting {
493 fut: Box::pin(async move { self_.call_async(dst).await }),
494 _marker: PhantomData,
495 }
496 }
497}
498
499fn get_host_port<'u>(config: &Config, dst: &'u Uri) -> Result<(&'u str, u16), ConnectError> {
500 trace!(
501 "Http::connect; scheme={:?}, host={:?}, port={:?}",
502 dst.scheme(),
503 dst.host(),
504 dst.port(),
505 );
506
507 if config.enforce_http {
508 if dst.scheme() != Some(&Scheme::HTTP) {
509 return Err(ConnectError {
510 msg: INVALID_NOT_HTTP,
511 addr: None,
512 cause: None,
513 });
514 }
515 } else if dst.scheme().is_none() {
516 return Err(ConnectError {
517 msg: INVALID_MISSING_SCHEME,
518 addr: None,
519 cause: None,
520 });
521 }
522
523 let host = match dst.host() {
524 Some(s) => s,
525 None => {
526 return Err(ConnectError {
527 msg: INVALID_MISSING_HOST,
528 addr: None,
529 cause: None,
530 });
531 }
532 };
533 let port = match dst.port() {
534 Some(port) => port.as_u16(),
535 None => {
536 if dst.scheme() == Some(&Scheme::HTTPS) {
537 443
538 } else {
539 80
540 }
541 }
542 };
543
544 Ok((host, port))
545}
546
547impl<R> HttpConnector<R>
548where
549 R: Resolve,
550{
551 async fn call_async(&mut self, dst: Uri) -> Result<TokioIo<TcpStream>, ConnectError> {
552 let config = &self.config;
553
554 let (host, port) = get_host_port(config, &dst)?;
555 let host = crate::client::strip_ipv6_brackets(host);
556
557 let addrs = if let Some(addrs) = dns::SocketAddrs::try_parse(host, port) {
560 addrs
561 } else {
562 let addrs = resolve(&mut self.resolver, dns::Name::new(host.into()))
563 .await
564 .map_err(ConnectError::dns)?;
565 let addrs = addrs
566 .map(|mut addr| {
567 set_port(&mut addr, port, dst.port().is_some());
568
569 addr
570 })
571 .collect();
572 dns::SocketAddrs::new(addrs)
573 };
574
575 let c = ConnectingTcp::new(addrs, config);
576
577 let sock = c.connect().await?;
578
579 if let Err(e) = sock.set_nodelay(config.nodelay) {
580 warn!("tcp set_nodelay error: {}", e);
581 }
582
583 Ok(TokioIo::new(sock))
584 }
585}
586
587impl Connection for TcpStream {
588 fn connected(&self) -> Connected {
589 let connected = Connected::new();
590 if let (Ok(remote_addr), Ok(local_addr)) = (self.peer_addr(), self.local_addr()) {
591 connected.extra(HttpInfo {
592 remote_addr,
593 local_addr,
594 })
595 } else {
596 connected
597 }
598 }
599}
600
601#[cfg(unix)]
602impl Connection for tokio::net::UnixStream {
603 fn connected(&self) -> Connected {
604 Connected::new()
605 }
606}
607
608#[cfg(windows)]
609impl Connection for tokio::net::windows::named_pipe::NamedPipeClient {
610 fn connected(&self) -> Connected {
611 Connected::new()
612 }
613}
614
615impl<T> Connection for TokioIo<T>
618where
619 T: Connection,
620{
621 fn connected(&self) -> Connected {
622 self.inner().connected()
623 }
624}
625
626impl HttpInfo {
627 pub fn remote_addr(&self) -> SocketAddr {
629 self.remote_addr
630 }
631
632 pub fn local_addr(&self) -> SocketAddr {
634 self.local_addr
635 }
636}
637
638pin_project! {
639 #[must_use = "futures do nothing unless polled"]
645 #[allow(missing_debug_implementations)]
646 pub struct HttpConnecting<R> {
647 #[pin]
648 fut: BoxConnecting,
649 _marker: PhantomData<R>,
650 }
651}
652
653type ConnectResult = Result<TokioIo<TcpStream>, ConnectError>;
654type BoxConnecting = Pin<Box<dyn Future<Output = ConnectResult> + Send>>;
655
656impl<R: Resolve> Future for HttpConnecting<R> {
657 type Output = ConnectResult;
658
659 fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
660 self.project().fut.poll(cx)
661 }
662}
663
664pub struct ConnectError {
666 msg: &'static str,
667 addr: Option<SocketAddr>,
668 cause: Option<Box<dyn StdError + Send + Sync>>,
669}
670
671impl ConnectError {
672 fn new<E>(msg: &'static str, cause: E) -> ConnectError
673 where
674 E: Into<Box<dyn StdError + Send + Sync>>,
675 {
676 ConnectError {
677 msg,
678 addr: None,
679 cause: Some(cause.into()),
680 }
681 }
682
683 fn dns<E>(cause: E) -> ConnectError
684 where
685 E: Into<Box<dyn StdError + Send + Sync>>,
686 {
687 ConnectError::new("dns error", cause)
688 }
689
690 fn m<E>(msg: &'static str) -> impl FnOnce(E) -> ConnectError
691 where
692 E: Into<Box<dyn StdError + Send + Sync>>,
693 {
694 move |cause| ConnectError::new(msg, cause)
695 }
696}
697
698impl fmt::Debug for ConnectError {
699 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
700 let mut b = f.debug_tuple("ConnectError");
701 b.field(&self.msg);
702 if let Some(ref addr) = self.addr {
703 b.field(addr);
704 }
705 if let Some(ref cause) = self.cause {
706 b.field(cause);
707 }
708 b.finish()
709 }
710}
711
712impl fmt::Display for ConnectError {
713 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
714 f.write_str(self.msg)
715 }
716}
717
718impl StdError for ConnectError {
719 fn source(&self) -> Option<&(dyn StdError + 'static)> {
720 self.cause.as_ref().map(|e| &**e as _)
721 }
722}
723
724struct ConnectingTcp<'a> {
725 preferred: ConnectingTcpRemote,
726 fallback: Option<ConnectingTcpFallback>,
727 config: &'a Config,
728}
729
730impl<'a> ConnectingTcp<'a> {
731 fn new(remote_addrs: dns::SocketAddrs, config: &'a Config) -> Self {
732 if let Some(fallback_timeout) = config.happy_eyeballs_timeout {
733 let (preferred_addrs, fallback_addrs) = remote_addrs
734 .split_by_preference(config.local_address_ipv4, config.local_address_ipv6);
735 if fallback_addrs.is_empty() {
736 return ConnectingTcp {
737 preferred: ConnectingTcpRemote::new(preferred_addrs, config.connect_timeout),
738 fallback: None,
739 config,
740 };
741 }
742
743 ConnectingTcp {
744 preferred: ConnectingTcpRemote::new(preferred_addrs, config.connect_timeout),
745 fallback: Some(ConnectingTcpFallback {
746 delay: tokio::time::sleep(fallback_timeout),
747 remote: ConnectingTcpRemote::new(fallback_addrs, config.connect_timeout),
748 }),
749 config,
750 }
751 } else {
752 ConnectingTcp {
753 preferred: ConnectingTcpRemote::new(remote_addrs, config.connect_timeout),
754 fallback: None,
755 config,
756 }
757 }
758 }
759}
760
761struct ConnectingTcpFallback {
762 delay: Sleep,
763 remote: ConnectingTcpRemote,
764}
765
766struct ConnectingTcpRemote {
767 addrs: dns::SocketAddrs,
768 connect_timeout: Option<Duration>,
769}
770
771impl ConnectingTcpRemote {
772 fn new(addrs: dns::SocketAddrs, connect_timeout: Option<Duration>) -> Self {
773 let connect_timeout = connect_timeout.and_then(|t| t.checked_div(addrs.len() as u32));
774
775 Self {
776 addrs,
777 connect_timeout,
778 }
779 }
780}
781
782impl ConnectingTcpRemote {
783 async fn connect(&mut self, config: &Config) -> Result<TcpStream, ConnectError> {
784 let mut err = None;
785 for addr in &mut self.addrs {
786 debug!("connecting to {}", addr);
787 match connect(&addr, config, self.connect_timeout)?.await {
788 Ok(tcp) => {
789 debug!("connected to {}", addr);
790 return Ok(tcp);
791 }
792 Err(mut e) => {
793 trace!("connect error for {}: {:?}", addr, e);
794 e.addr = Some(addr);
795 if err.is_none() {
797 err = Some(e);
798 }
799 }
800 }
801 }
802
803 match err {
804 Some(e) => Err(e),
805 None => Err(ConnectError::new(
806 "tcp connect error",
807 std::io::Error::new(std::io::ErrorKind::NotConnected, "Network unreachable"),
808 )),
809 }
810 }
811}
812
813fn bind_local_address(
814 socket: &socket2::Socket,
815 dst_addr: &SocketAddr,
816 local_addr_ipv4: &Option<Ipv4Addr>,
817 local_addr_ipv6: &Option<Ipv6Addr>,
818) -> io::Result<()> {
819 match (*dst_addr, local_addr_ipv4, local_addr_ipv6) {
820 (SocketAddr::V4(_), Some(addr), _) => {
821 socket.bind(&SocketAddr::new((*addr).into(), 0).into())?;
822 }
823 (SocketAddr::V6(_), _, Some(addr)) => {
824 socket.bind(&SocketAddr::new((*addr).into(), 0).into())?;
825 }
826 _ => {
827 if cfg!(windows) {
828 let any: SocketAddr = match *dst_addr {
830 SocketAddr::V4(_) => ([0, 0, 0, 0], 0).into(),
831 SocketAddr::V6(_) => ([0, 0, 0, 0, 0, 0, 0, 0], 0).into(),
832 };
833 socket.bind(&any.into())?;
834 }
835 }
836 }
837
838 Ok(())
839}
840
841fn connect(
842 addr: &SocketAddr,
843 config: &Config,
844 connect_timeout: Option<Duration>,
845) -> Result<impl Future<Output = Result<TcpStream, ConnectError>>, ConnectError> {
846 use socket2::{Domain, Protocol, Socket, Type};
850
851 let domain = Domain::for_address(*addr);
852 let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))
853 .map_err(ConnectError::m("tcp open error"))?;
854
855 socket
858 .set_nonblocking(true)
859 .map_err(ConnectError::m("tcp set_nonblocking error"))?;
860
861 if let Some(tcp_keepalive) = &config.tcp_keepalive_config.into_tcpkeepalive() {
862 if let Err(e) = socket.set_tcp_keepalive(tcp_keepalive) {
863 warn!("tcp set_keepalive error: {}", e);
864 }
865 }
866
867 #[cfg(any(
869 target_os = "android",
870 target_os = "fuchsia",
871 target_os = "illumos",
872 target_os = "ios",
873 target_os = "linux",
874 target_os = "macos",
875 target_os = "solaris",
876 target_os = "tvos",
877 target_os = "visionos",
878 target_os = "watchos",
879 ))]
880 if let Some(interface) = &config.interface {
881 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
884 socket
885 .bind_device(Some(interface.as_bytes()))
886 .map_err(ConnectError::m("tcp bind interface error"))?;
887
888 #[cfg(any(
893 target_os = "illumos",
894 target_os = "ios",
895 target_os = "macos",
896 target_os = "solaris",
897 target_os = "tvos",
898 target_os = "visionos",
899 target_os = "watchos",
900 ))]
901 {
902 let idx = unsafe { libc::if_nametoindex(interface.as_ptr()) };
903 let idx = std::num::NonZeroU32::new(idx).ok_or_else(|| {
904 ConnectError::new(
906 "error converting interface name to index",
907 io::Error::last_os_error(),
908 )
909 })?;
910 match addr {
913 SocketAddr::V4(_) => socket.bind_device_by_index_v4(Some(idx)),
914 SocketAddr::V6(_) => socket.bind_device_by_index_v6(Some(idx)),
915 }
916 .map_err(ConnectError::m("tcp bind interface error"))?;
917 }
918 }
919
920 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
922 if let Some(mark) = config.mark {
923 socket
924 .set_mark(mark)
925 .map_err(ConnectError::m("tcp set_mark error"))?;
926 }
927
928 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
929 if let Some(tcp_user_timeout) = &config.tcp_user_timeout {
930 if let Err(e) = socket.set_tcp_user_timeout(Some(*tcp_user_timeout)) {
931 warn!("tcp set_tcp_user_timeout error: {}", e);
932 }
933 }
934
935 bind_local_address(
936 &socket,
937 addr,
938 &config.local_address_ipv4,
939 &config.local_address_ipv6,
940 )
941 .map_err(ConnectError::m("tcp bind local error"))?;
942
943 let socket = TcpSocket::from_std_stream(socket.into());
945
946 if config.reuse_address {
947 if let Err(e) = socket.set_reuseaddr(true) {
948 warn!("tcp set_reuse_address error: {}", e);
949 }
950 }
951
952 if let Some(size) = config.send_buffer_size {
953 if let Err(e) = socket.set_send_buffer_size(size.try_into().unwrap_or(u32::MAX)) {
954 warn!("tcp set_buffer_size error: {}", e);
955 }
956 }
957
958 if let Some(size) = config.recv_buffer_size {
959 if let Err(e) = socket.set_recv_buffer_size(size.try_into().unwrap_or(u32::MAX)) {
960 warn!("tcp set_recv_buffer_size error: {}", e);
961 }
962 }
963
964 let connect = socket.connect(*addr);
965 Ok(async move {
966 match connect_timeout {
967 Some(dur) => match tokio::time::timeout(dur, connect).await {
968 Ok(Ok(s)) => Ok(s),
969 Ok(Err(e)) => Err(e),
970 Err(e) => Err(io::Error::new(io::ErrorKind::TimedOut, e)),
971 },
972 None => connect.await,
973 }
974 .map_err(ConnectError::m("tcp connect error"))
975 })
976}
977
978impl ConnectingTcp<'_> {
979 async fn connect(mut self) -> Result<TcpStream, ConnectError> {
980 match self.fallback {
981 None => self.preferred.connect(self.config).await,
982 Some(mut fallback) => {
983 let preferred_fut = pin!(self.preferred.connect(self.config));
984 let fallback_fut = pin!(fallback.remote.connect(self.config));
985 let fallback_delay = pin!(fallback.delay);
986
987 let (result, future) =
988 match futures_util::future::select(preferred_fut, fallback_delay).await {
989 Either::Left((result, _fallback_delay)) => {
990 (result, Either::Right(fallback_fut))
991 }
992 Either::Right(((), preferred_fut)) => {
993 futures_util::future::select(preferred_fut, fallback_fut)
995 .await
996 .factor_first()
997 }
998 };
999
1000 if result.is_err() {
1001 future.await
1004 } else {
1005 result
1006 }
1007 }
1008 }
1009 }
1010}
1011
1012fn set_port(addr: &mut SocketAddr, host_port: u16, explicit: bool) {
1016 if explicit || addr.port() == 0 {
1017 addr.set_port(host_port)
1018 };
1019}
1020
1021#[cfg(test)]
1022mod tests {
1023 use std::io;
1024 use std::net::SocketAddr;
1025
1026 use ::http::Uri;
1027
1028 use crate::client::legacy::connect::http::TcpKeepaliveConfig;
1029
1030 use super::super::sealed::{Connect, ConnectSvc};
1031 use super::{Config, ConnectError, HttpConnector};
1032
1033 use super::set_port;
1034
1035 async fn connect<C>(
1036 connector: C,
1037 dst: Uri,
1038 ) -> Result<<C::_Svc as ConnectSvc>::Connection, <C::_Svc as ConnectSvc>::Error>
1039 where
1040 C: Connect,
1041 {
1042 connector.connect(super::super::sealed::Internal, dst).await
1043 }
1044
1045 #[tokio::test]
1046 async fn test_errors_enforce_http() {
1047 let dst = "https://example.domain/foo/bar?baz".parse().unwrap();
1048 let connector = HttpConnector::new();
1049
1050 let err = connect(connector, dst).await.unwrap_err();
1051 assert_eq!(&*err.msg, super::INVALID_NOT_HTTP);
1052 }
1053
1054 #[cfg(any(target_os = "linux", target_os = "macos"))]
1055 fn get_local_ips() -> (Option<std::net::Ipv4Addr>, Option<std::net::Ipv6Addr>) {
1056 use std::net::{IpAddr, TcpListener};
1057
1058 let mut ip_v4 = None;
1059 let mut ip_v6 = None;
1060
1061 let ips = pnet_datalink::interfaces()
1062 .into_iter()
1063 .flat_map(|i| i.ips.into_iter().map(|n| n.ip()));
1064
1065 for ip in ips {
1066 match ip {
1067 IpAddr::V4(ip) if TcpListener::bind((ip, 0)).is_ok() => ip_v4 = Some(ip),
1068 IpAddr::V6(ip) if TcpListener::bind((ip, 0)).is_ok() => ip_v6 = Some(ip),
1069 _ => (),
1070 }
1071
1072 if ip_v4.is_some() && ip_v6.is_some() {
1073 break;
1074 }
1075 }
1076
1077 (ip_v4, ip_v6)
1078 }
1079
1080 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
1081 fn default_interface() -> Option<String> {
1082 pnet_datalink::interfaces()
1083 .iter()
1084 .find(|e| e.is_up() && !e.is_loopback() && !e.ips.is_empty())
1085 .map(|e| e.name.clone())
1086 }
1087
1088 #[tokio::test]
1089 async fn test_errors_missing_scheme() {
1090 let dst = "example.domain".parse().unwrap();
1091 let mut connector = HttpConnector::new();
1092 connector.enforce_http(false);
1093
1094 let err = connect(connector, dst).await.unwrap_err();
1095 assert_eq!(&*err.msg, super::INVALID_MISSING_SCHEME);
1096 }
1097
1098 #[cfg(any(target_os = "linux", target_os = "macos"))]
1100 #[cfg_attr(miri, ignore)]
1101 #[tokio::test]
1102 async fn local_address() {
1103 use std::net::{IpAddr, TcpListener};
1104
1105 let (bind_ip_v4, bind_ip_v6) = get_local_ips();
1106 let server4 = TcpListener::bind("127.0.0.1:0").unwrap();
1107 let port = server4.local_addr().unwrap().port();
1108 let server6 = TcpListener::bind(format!("[::1]:{port}")).unwrap();
1109
1110 let assert_client_ip = |dst: String, server: TcpListener, expected_ip: IpAddr| async move {
1111 let mut connector = HttpConnector::new();
1112
1113 match (bind_ip_v4, bind_ip_v6) {
1114 (Some(v4), Some(v6)) => connector.set_local_addresses(v4, v6),
1115 (Some(v4), None) => connector.set_local_address(Some(v4.into())),
1116 (None, Some(v6)) => connector.set_local_address(Some(v6.into())),
1117 _ => unreachable!(),
1118 }
1119
1120 connect(connector, dst.parse().unwrap()).await.unwrap();
1121
1122 let (_, client_addr) = server.accept().unwrap();
1123
1124 assert_eq!(client_addr.ip(), expected_ip);
1125 };
1126
1127 if let Some(ip) = bind_ip_v4 {
1128 assert_client_ip(format!("http://127.0.0.1:{port}"), server4, ip.into()).await;
1129 }
1130
1131 if let Some(ip) = bind_ip_v6 {
1132 assert_client_ip(format!("http://[::1]:{port}"), server6, ip.into()).await;
1133 }
1134 }
1135
1136 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
1138 #[tokio::test]
1139 #[ignore = "setting `SO_BINDTODEVICE` requires the `CAP_NET_RAW` capability (works when running as root)"]
1140 async fn interface() {
1141 use socket2::{Domain, Protocol, Socket, Type};
1142 use std::net::TcpListener;
1143
1144 let interface: Option<String> = default_interface();
1145
1146 let server4 = TcpListener::bind("127.0.0.1:0").unwrap();
1147 let port = server4.local_addr().unwrap().port();
1148
1149 let server6 = TcpListener::bind(format!("[::1]:{port}")).unwrap();
1150
1151 let assert_interface_name =
1152 |dst: String,
1153 server: TcpListener,
1154 bind_iface: Option<String>,
1155 expected_interface: Option<String>| async move {
1156 let mut connector = HttpConnector::new();
1157 if let Some(iface) = bind_iface {
1158 connector.set_interface(iface);
1159 }
1160
1161 connect(connector, dst.parse().unwrap()).await.unwrap();
1162 let domain = Domain::for_address(server.local_addr().unwrap());
1163 let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP)).unwrap();
1164
1165 assert_eq!(
1166 socket.device().unwrap().as_deref(),
1167 expected_interface.as_deref().map(|val| val.as_bytes())
1168 );
1169 };
1170
1171 assert_interface_name(
1172 format!("http://127.0.0.1:{port}"),
1173 server4,
1174 interface.clone(),
1175 interface.clone(),
1176 )
1177 .await;
1178 assert_interface_name(
1179 format!("http://[::1]:{port}"),
1180 server6,
1181 interface.clone(),
1182 interface.clone(),
1183 )
1184 .await;
1185 }
1186
1187 #[test]
1188 #[ignore] #[cfg_attr(not(feature = "__internal_happy_eyeballs_tests"), ignore)]
1190 fn client_happy_eyeballs() {
1191 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, TcpListener};
1192 use std::time::{Duration, Instant};
1193
1194 use super::ConnectingTcp;
1195 use super::dns;
1196
1197 let server4 = TcpListener::bind("127.0.0.1:0").unwrap();
1198 let addr = server4.local_addr().unwrap();
1199 let _server6 = TcpListener::bind(format!("[::1]:{}", addr.port())).unwrap();
1200 let rt = tokio::runtime::Builder::new_current_thread()
1201 .enable_all()
1202 .build()
1203 .unwrap();
1204
1205 let local_timeout = Duration::default();
1206 let unreachable_v4_timeout = measure_connect(unreachable_ipv4_addr()).1;
1207 let unreachable_v6_timeout = measure_connect(unreachable_ipv6_addr()).1;
1208 let fallback_timeout = std::cmp::max(unreachable_v4_timeout, unreachable_v6_timeout)
1209 + Duration::from_millis(250);
1210
1211 let scenarios = &[
1212 (&[local_ipv4_addr()][..], 4, local_timeout, false),
1214 (&[local_ipv6_addr()][..], 6, local_timeout, false),
1215 (
1217 &[local_ipv4_addr(), local_ipv6_addr()][..],
1218 4,
1219 local_timeout,
1220 false,
1221 ),
1222 (
1223 &[local_ipv6_addr(), local_ipv4_addr()][..],
1224 6,
1225 local_timeout,
1226 false,
1227 ),
1228 (
1230 &[unreachable_ipv4_addr(), local_ipv4_addr()][..],
1231 4,
1232 unreachable_v4_timeout,
1233 false,
1234 ),
1235 (
1236 &[unreachable_ipv6_addr(), local_ipv6_addr()][..],
1237 6,
1238 unreachable_v6_timeout,
1239 false,
1240 ),
1241 (
1243 &[
1244 unreachable_ipv4_addr(),
1245 local_ipv4_addr(),
1246 local_ipv6_addr(),
1247 ][..],
1248 4,
1249 unreachable_v4_timeout,
1250 false,
1251 ),
1252 (
1253 &[
1254 unreachable_ipv6_addr(),
1255 local_ipv6_addr(),
1256 local_ipv4_addr(),
1257 ][..],
1258 6,
1259 unreachable_v6_timeout,
1260 true,
1261 ),
1262 (
1264 &[slow_ipv4_addr(), local_ipv4_addr(), local_ipv6_addr()][..],
1265 6,
1266 fallback_timeout,
1267 false,
1268 ),
1269 (
1270 &[slow_ipv6_addr(), local_ipv6_addr(), local_ipv4_addr()][..],
1271 4,
1272 fallback_timeout,
1273 true,
1274 ),
1275 (
1277 &[slow_ipv4_addr(), unreachable_ipv6_addr(), local_ipv6_addr()][..],
1278 6,
1279 fallback_timeout + unreachable_v6_timeout,
1280 false,
1281 ),
1282 (
1283 &[slow_ipv6_addr(), unreachable_ipv4_addr(), local_ipv4_addr()][..],
1284 4,
1285 fallback_timeout + unreachable_v4_timeout,
1286 true,
1287 ),
1288 ];
1289
1290 let ipv6_accessible = measure_connect(slow_ipv6_addr()).0;
1293
1294 for &(hosts, family, timeout, needs_ipv6_access) in scenarios {
1295 if needs_ipv6_access && !ipv6_accessible {
1296 continue;
1297 }
1298
1299 let (start, stream) = rt
1300 .block_on(async move {
1301 let addrs = hosts
1302 .iter()
1303 .map(|host| (*host, addr.port()).into())
1304 .collect();
1305 let cfg = Config {
1306 local_address_ipv4: None,
1307 local_address_ipv6: None,
1308 connect_timeout: None,
1309 tcp_keepalive_config: TcpKeepaliveConfig::default(),
1310 happy_eyeballs_timeout: Some(fallback_timeout),
1311 nodelay: false,
1312 reuse_address: false,
1313 enforce_http: false,
1314 send_buffer_size: None,
1315 recv_buffer_size: None,
1316 #[cfg(any(
1317 target_os = "android",
1318 target_os = "fuchsia",
1319 target_os = "linux"
1320 ))]
1321 interface: None,
1322 #[cfg(any(
1323 target_os = "illumos",
1324 target_os = "ios",
1325 target_os = "macos",
1326 target_os = "solaris",
1327 target_os = "tvos",
1328 target_os = "visionos",
1329 target_os = "watchos",
1330 ))]
1331 interface: None,
1332 #[cfg(any(
1333 target_os = "android",
1334 target_os = "fuchsia",
1335 target_os = "linux"
1336 ))]
1337 mark: None,
1338 #[cfg(any(
1339 target_os = "android",
1340 target_os = "fuchsia",
1341 target_os = "linux"
1342 ))]
1343 tcp_user_timeout: None,
1344 };
1345 let connecting_tcp = ConnectingTcp::new(dns::SocketAddrs::new(addrs), &cfg);
1346 let start = Instant::now();
1347 Ok::<_, ConnectError>((start, ConnectingTcp::connect(connecting_tcp).await?))
1348 })
1349 .unwrap();
1350 let res = if stream.peer_addr().unwrap().is_ipv4() {
1351 4
1352 } else {
1353 6
1354 };
1355 let duration = start.elapsed();
1356
1357 let min_duration = if timeout >= Duration::from_millis(150) {
1359 timeout - Duration::from_millis(150)
1360 } else {
1361 Duration::default()
1362 };
1363 let max_duration = timeout + Duration::from_millis(150);
1364
1365 assert_eq!(res, family);
1366 assert!(duration >= min_duration);
1367 assert!(duration <= max_duration);
1368 }
1369
1370 fn local_ipv4_addr() -> IpAddr {
1371 Ipv4Addr::new(127, 0, 0, 1).into()
1372 }
1373
1374 fn local_ipv6_addr() -> IpAddr {
1375 Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).into()
1376 }
1377
1378 fn unreachable_ipv4_addr() -> IpAddr {
1379 Ipv4Addr::new(127, 0, 0, 2).into()
1380 }
1381
1382 fn unreachable_ipv6_addr() -> IpAddr {
1383 Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 2).into()
1384 }
1385
1386 fn slow_ipv4_addr() -> IpAddr {
1387 Ipv4Addr::new(198, 18, 0, 25).into()
1389 }
1390
1391 fn slow_ipv6_addr() -> IpAddr {
1392 Ipv6Addr::new(2001, 2, 0, 0, 0, 0, 0, 254).into()
1394 }
1395
1396 fn measure_connect(addr: IpAddr) -> (bool, Duration) {
1397 let start = Instant::now();
1398 let result =
1399 std::net::TcpStream::connect_timeout(&(addr, 80).into(), Duration::from_secs(1));
1400
1401 let reachable = result.is_ok() || result.unwrap_err().kind() == io::ErrorKind::TimedOut;
1402 let duration = start.elapsed();
1403 (reachable, duration)
1404 }
1405 }
1406
1407 use std::time::Duration;
1408
1409 #[test]
1410 fn no_tcp_keepalive_config() {
1411 assert!(TcpKeepaliveConfig::default().into_tcpkeepalive().is_none());
1412 }
1413
1414 #[test]
1415 fn tcp_keepalive_time_config() {
1416 let kac = TcpKeepaliveConfig {
1417 time: Some(Duration::from_secs(60)),
1418 ..Default::default()
1419 };
1420 if let Some(tcp_keepalive) = kac.into_tcpkeepalive() {
1421 assert!(format!("{tcp_keepalive:?}").contains("time: Some(60s)"));
1422 } else {
1423 panic!("test failed");
1424 }
1425 }
1426
1427 #[cfg(not(any(target_os = "openbsd", target_os = "redox", target_os = "solaris")))]
1428 #[test]
1429 fn tcp_keepalive_interval_config() {
1430 let kac = TcpKeepaliveConfig {
1431 interval: Some(Duration::from_secs(1)),
1432 ..Default::default()
1433 };
1434 if let Some(tcp_keepalive) = kac.into_tcpkeepalive() {
1435 assert!(format!("{tcp_keepalive:?}").contains("interval: Some(1s)"));
1436 } else {
1437 panic!("test failed");
1438 }
1439 }
1440
1441 #[cfg(not(any(
1442 target_os = "openbsd",
1443 target_os = "redox",
1444 target_os = "solaris",
1445 target_os = "windows"
1446 )))]
1447 #[test]
1448 fn tcp_keepalive_retries_config() {
1449 let kac = TcpKeepaliveConfig {
1450 retries: Some(3),
1451 ..Default::default()
1452 };
1453 if let Some(tcp_keepalive) = kac.into_tcpkeepalive() {
1454 assert!(format!("{tcp_keepalive:?}").contains("retries: Some(3)"));
1455 } else {
1456 panic!("test failed");
1457 }
1458 }
1459
1460 #[test]
1461 fn test_set_port() {
1462 let mut addr = SocketAddr::from(([0, 0, 0, 0], 6881));
1464 set_port(&mut addr, 42, true);
1465 assert_eq!(addr.port(), 42);
1466
1467 let mut addr = SocketAddr::from(([0, 0, 0, 0], 6881));
1469 set_port(&mut addr, 443, false);
1470 assert_eq!(addr.port(), 6881);
1471
1472 let mut addr = SocketAddr::from(([0, 0, 0, 0], 0));
1474 set_port(&mut addr, 443, false);
1475 assert_eq!(addr.port(), 443);
1476 }
1477}