wreq-rb 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +1922 -397
  3. data/LICENSE +203 -0
  4. data/README.md +19 -15
  5. data/ext/wreq_rb/Cargo.toml +4 -6
  6. data/ext/wreq_rb/src/client.rs +41 -48
  7. data/lib/wreq-rb/version.rb +1 -1
  8. data/patches/0001-add-transfer-size-tracking.patch +76 -67
  9. data/vendor/wreq/Cargo.toml +119 -71
  10. data/vendor/wreq/README.md +25 -20
  11. data/vendor/wreq/bench/http1.rs +25 -0
  12. data/vendor/wreq/bench/http1_over_tls.rs +25 -0
  13. data/vendor/wreq/bench/http2.rs +25 -0
  14. data/vendor/wreq/bench/http2_over_tls.rs +25 -0
  15. data/vendor/wreq/bench/support/bench.rs +91 -0
  16. data/vendor/wreq/bench/support/client.rs +217 -0
  17. data/vendor/wreq/bench/support/server.rs +188 -0
  18. data/vendor/wreq/bench/support.rs +56 -0
  19. data/vendor/wreq/examples/cert_store.rs +4 -4
  20. data/vendor/wreq/examples/{emulation.rs → emulate.rs} +2 -2
  21. data/vendor/wreq/examples/http2_websocket.rs +2 -2
  22. data/vendor/wreq/examples/keylog.rs +3 -3
  23. data/vendor/wreq/examples/{request_with_emulation.rs → request_with_emulate.rs} +2 -2
  24. data/vendor/wreq/examples/rt.rs +23 -0
  25. data/vendor/wreq/src/client/body.rs +23 -61
  26. data/vendor/wreq/src/client/emulate.rs +119 -0
  27. data/vendor/wreq/src/client/{http/future.rs → future.rs} +11 -32
  28. data/vendor/wreq/src/client/{http → layer}/client/pool.rs +66 -61
  29. data/vendor/wreq/src/client/{http → layer}/client.rs +416 -270
  30. data/vendor/wreq/src/client/layer/config.rs +27 -6
  31. data/vendor/wreq/src/client/layer/decoder.rs +9 -4
  32. data/vendor/wreq/src/client/layer/redirect/future.rs +6 -3
  33. data/vendor/wreq/src/client/layer/redirect.rs +4 -5
  34. data/vendor/wreq/src/client/layer/retry.rs +8 -5
  35. data/vendor/wreq/src/client/layer/timeout/body.rs +15 -6
  36. data/vendor/wreq/src/client/layer/timeout/future.rs +23 -18
  37. data/vendor/wreq/src/client/layer/timeout.rs +24 -74
  38. data/vendor/wreq/src/client/layer.rs +1 -2
  39. data/vendor/wreq/src/client/multipart.rs +137 -154
  40. data/vendor/wreq/src/client/request.rs +202 -118
  41. data/vendor/wreq/src/client/response.rs +46 -45
  42. data/vendor/wreq/src/client/upgrade.rs +15 -0
  43. data/vendor/wreq/src/client/ws.rs +73 -25
  44. data/vendor/wreq/src/client.rs +1655 -17
  45. data/vendor/wreq/src/config.rs +11 -11
  46. data/vendor/wreq/src/{client/conn → conn}/connector.rs +139 -137
  47. data/vendor/wreq/src/conn/descriptor.rs +143 -0
  48. data/vendor/wreq/src/conn/http.rs +484 -0
  49. data/vendor/wreq/src/conn/net/io.rs +75 -0
  50. data/vendor/wreq/src/conn/net/tcp/compio.rs +71 -0
  51. data/vendor/wreq/src/conn/net/tcp/tokio.rs +57 -0
  52. data/vendor/wreq/src/conn/net/tcp.rs +561 -0
  53. data/vendor/wreq/src/conn/net/uds/compio.rs +60 -0
  54. data/vendor/wreq/src/{client/conn/uds.rs → conn/net/uds/tokio.rs} +18 -12
  55. data/vendor/wreq/src/conn/net/uds.rs +11 -0
  56. data/vendor/wreq/src/conn/net.rs +130 -0
  57. data/vendor/wreq/src/{client/conn → conn}/proxy/socks.rs +2 -9
  58. data/vendor/wreq/src/{client/conn → conn}/proxy/tunnel.rs +21 -56
  59. data/vendor/wreq/src/conn/tls_info.rs +47 -0
  60. data/vendor/wreq/src/{client/conn.rs → conn.rs} +202 -54
  61. data/vendor/wreq/src/cookie.rs +302 -142
  62. data/vendor/wreq/src/dns/gai/compio.rs +77 -0
  63. data/vendor/wreq/src/dns/gai/tokio.rs +90 -0
  64. data/vendor/wreq/src/dns/gai.rs +14 -164
  65. data/vendor/wreq/src/dns/hickory.rs +16 -23
  66. data/vendor/wreq/src/dns/resolve.rs +7 -41
  67. data/vendor/wreq/src/dns.rs +90 -7
  68. data/vendor/wreq/src/error.rs +57 -31
  69. data/vendor/wreq/src/ext.rs +25 -0
  70. data/vendor/wreq/src/group.rs +211 -0
  71. data/vendor/wreq/src/header.rs +100 -112
  72. data/vendor/wreq/src/lib.rs +124 -73
  73. data/vendor/wreq/src/proxy.rs +6 -20
  74. data/vendor/wreq/src/redirect.rs +1 -1
  75. data/vendor/wreq/src/rt.rs +208 -0
  76. data/vendor/wreq/src/sync.rs +97 -98
  77. data/vendor/wreq/src/tls/compress.rs +124 -0
  78. data/vendor/wreq/src/tls/conn/ext.rs +54 -45
  79. data/vendor/wreq/src/tls/conn/service.rs +14 -18
  80. data/vendor/wreq/src/tls/conn.rs +169 -241
  81. data/vendor/wreq/src/tls/keylog.rs +68 -5
  82. data/vendor/wreq/src/tls/session.rs +205 -0
  83. data/vendor/wreq/src/tls/{x509 → trust}/identity.rs +4 -21
  84. data/vendor/wreq/src/tls/{x509/parser.rs → trust/parse.rs} +1 -1
  85. data/vendor/wreq/src/tls/{x509 → trust}/store.rs +42 -81
  86. data/vendor/wreq/src/tls/{x509.rs → trust.rs} +8 -2
  87. data/vendor/wreq/src/tls.rs +489 -25
  88. data/vendor/wreq/src/trace.rs +0 -12
  89. data/vendor/wreq/src/util.rs +1 -1
  90. data/vendor/wreq/tests/badssl.rs +10 -10
  91. data/vendor/wreq/tests/client.rs +3 -9
  92. data/vendor/wreq/tests/cookie.rs +6 -8
  93. data/vendor/wreq/tests/{emulation.rs → emulate.rs} +130 -22
  94. data/vendor/wreq/tests/multipart.rs +43 -1
  95. data/vendor/wreq/tests/proxy.rs +1 -1
  96. data/vendor/wreq/tests/support/layer.rs +1 -0
  97. metadata +49 -71
  98. data/patches/0002-add-cancel-connections.patch +0 -181
  99. data/vendor/wreq/src/client/conn/conn.rs +0 -231
  100. data/vendor/wreq/src/client/conn/http.rs +0 -1023
  101. data/vendor/wreq/src/client/conn/tls_info.rs +0 -98
  102. data/vendor/wreq/src/client/core/body/incoming.rs +0 -485
  103. data/vendor/wreq/src/client/core/body/length.rs +0 -118
  104. data/vendor/wreq/src/client/core/body.rs +0 -34
  105. data/vendor/wreq/src/client/core/common/buf.rs +0 -149
  106. data/vendor/wreq/src/client/core/common/rewind.rs +0 -141
  107. data/vendor/wreq/src/client/core/common/watch.rs +0 -76
  108. data/vendor/wreq/src/client/core/common.rs +0 -3
  109. data/vendor/wreq/src/client/core/conn/http1.rs +0 -342
  110. data/vendor/wreq/src/client/core/conn/http2.rs +0 -307
  111. data/vendor/wreq/src/client/core/conn.rs +0 -11
  112. data/vendor/wreq/src/client/core/dispatch.rs +0 -299
  113. data/vendor/wreq/src/client/core/error.rs +0 -435
  114. data/vendor/wreq/src/client/core/ext.rs +0 -201
  115. data/vendor/wreq/src/client/core/http1.rs +0 -178
  116. data/vendor/wreq/src/client/core/http2.rs +0 -483
  117. data/vendor/wreq/src/client/core/proto/h1/conn.rs +0 -988
  118. data/vendor/wreq/src/client/core/proto/h1/decode.rs +0 -1170
  119. data/vendor/wreq/src/client/core/proto/h1/dispatch.rs +0 -684
  120. data/vendor/wreq/src/client/core/proto/h1/encode.rs +0 -580
  121. data/vendor/wreq/src/client/core/proto/h1/io.rs +0 -879
  122. data/vendor/wreq/src/client/core/proto/h1/role.rs +0 -694
  123. data/vendor/wreq/src/client/core/proto/h1.rs +0 -104
  124. data/vendor/wreq/src/client/core/proto/h2/client.rs +0 -650
  125. data/vendor/wreq/src/client/core/proto/h2/ping.rs +0 -539
  126. data/vendor/wreq/src/client/core/proto/h2.rs +0 -379
  127. data/vendor/wreq/src/client/core/proto/headers.rs +0 -138
  128. data/vendor/wreq/src/client/core/proto.rs +0 -58
  129. data/vendor/wreq/src/client/core/rt/bounds.rs +0 -57
  130. data/vendor/wreq/src/client/core/rt/timer.rs +0 -150
  131. data/vendor/wreq/src/client/core/rt/tokio.rs +0 -99
  132. data/vendor/wreq/src/client/core/rt.rs +0 -25
  133. data/vendor/wreq/src/client/core/upgrade.rs +0 -267
  134. data/vendor/wreq/src/client/core.rs +0 -16
  135. data/vendor/wreq/src/client/emulation.rs +0 -161
  136. data/vendor/wreq/src/client/http/client/error.rs +0 -142
  137. data/vendor/wreq/src/client/http/client/exec.rs +0 -29
  138. data/vendor/wreq/src/client/http/client/extra.rs +0 -77
  139. data/vendor/wreq/src/client/http/client/util.rs +0 -104
  140. data/vendor/wreq/src/client/http.rs +0 -1629
  141. data/vendor/wreq/src/client/layer/config/options.rs +0 -156
  142. data/vendor/wreq/src/client/layer/cookie.rs +0 -161
  143. data/vendor/wreq/src/hash.rs +0 -143
  144. data/vendor/wreq/src/tls/conn/cache.rs +0 -123
  145. data/vendor/wreq/src/tls/conn/cert_compression.rs +0 -125
  146. data/vendor/wreq/src/tls/keylog/handle.rs +0 -64
  147. data/vendor/wreq/src/tls/options.rs +0 -464
  148. /data/vendor/wreq/src/client/{http → layer}/client/lazy.rs +0 -0
  149. /data/vendor/wreq/src/{client/conn → conn}/proxy.rs +0 -0
  150. /data/vendor/wreq/src/{client/conn → conn}/verbose.rs +0 -0
@@ -0,0 +1,71 @@
1
+ //! Compio-based TCP connector.
2
+
3
+ #![allow(dead_code)]
4
+
5
+ use std::{future::Future, io, net::SocketAddr, pin::Pin, time::Duration};
6
+
7
+ use compio::net::{TcpSocket, TcpStream};
8
+ use wreq_rt::rt::compio::{future::SendFuture, io::CompioIO};
9
+
10
+ use super::BoxConnecting;
11
+ use crate::conn::{
12
+ Connected, Connection, http::HttpInfo, net::io::CompioConnection, tls_info::TlsInfoFactory,
13
+ };
14
+
15
+ /// A connector that uses `compio` for TCP connections.
16
+ #[derive(Clone, Copy, Debug, Default)]
17
+ pub struct TcpConnector {
18
+ _priv: (),
19
+ }
20
+
21
+ // ===== impl TcpConnector =====
22
+
23
+ impl TcpConnector {
24
+ /// Creates a new [`TcpConnector`].
25
+ #[inline]
26
+ pub fn new() -> Self {
27
+ Self { _priv: () }
28
+ }
29
+ }
30
+
31
+ impl super::TcpConnector for TcpConnector {
32
+ type TcpStream = std::net::TcpStream;
33
+ type Connection = CompioConnection<TcpStream>;
34
+ type Error = io::Error;
35
+ type Future = BoxConnecting<Self::Connection, Self::Error>;
36
+ type Sleep = Pin<Box<dyn Future<Output = ()> + Send + 'static>>;
37
+
38
+ #[inline]
39
+ fn connect(&self, socket: Self::TcpStream, addr: SocketAddr) -> Self::Future {
40
+ Box::pin(SendFuture::new(async move {
41
+ let socket = TcpSocket::from_std_stream(socket)?;
42
+ let tcp_stream = socket.connect(addr).await?;
43
+ Ok(CompioConnection {
44
+ peer_addr: tcp_stream.peer_addr().ok(),
45
+ local_addr: tcp_stream.local_addr().ok(),
46
+ inner: CompioIO::new(tcp_stream),
47
+ })
48
+ }))
49
+ }
50
+
51
+ #[inline]
52
+ fn sleep(&self, duration: Duration) -> Self::Sleep {
53
+ Box::pin(SendFuture::new(compio::time::sleep(duration)))
54
+ }
55
+ }
56
+
57
+ impl Connection for CompioConnection<TcpStream> {
58
+ fn connected(&self) -> Connected {
59
+ let connected = Connected::new();
60
+ if let (Some(remote_addr), Some(local_addr)) = (self.peer_addr, self.local_addr) {
61
+ connected.extra(HttpInfo {
62
+ remote_addr,
63
+ local_addr,
64
+ })
65
+ } else {
66
+ connected
67
+ }
68
+ }
69
+ }
70
+
71
+ impl TlsInfoFactory for CompioConnection<TcpStream> {}
@@ -0,0 +1,57 @@
1
+ //! Tokio-based TCP connector.
2
+
3
+ use std::{io, net::SocketAddr, time::Duration};
4
+
5
+ use tokio::net::{TcpSocket, TcpStream};
6
+
7
+ use super::BoxConnecting;
8
+ use crate::conn::{Connected, Connection, http::HttpInfo, tls_info::TlsInfoFactory};
9
+
10
+ /// A connector that uses `tokio` for TCP connections.
11
+ #[derive(Clone, Copy, Debug, Default)]
12
+ pub struct TcpConnector {
13
+ _priv: (),
14
+ }
15
+
16
+ impl TcpConnector {
17
+ /// Creates a new [`TcpConnector`].
18
+ #[inline]
19
+ pub fn new() -> Self {
20
+ Self { _priv: () }
21
+ }
22
+ }
23
+
24
+ impl super::TcpConnector for TcpConnector {
25
+ type TcpStream = std::net::TcpStream;
26
+ type Connection = TcpStream;
27
+ type Error = io::Error;
28
+ type Future = BoxConnecting<Self::Connection, Self::Error>;
29
+ type Sleep = tokio::time::Sleep;
30
+
31
+ #[inline]
32
+ fn connect(&self, socket: Self::TcpStream, addr: SocketAddr) -> Self::Future {
33
+ let socket = TcpSocket::from_std_stream(socket);
34
+ Box::pin(socket.connect(addr))
35
+ }
36
+
37
+ #[inline]
38
+ fn sleep(&self, duration: Duration) -> Self::Sleep {
39
+ tokio::time::sleep(duration)
40
+ }
41
+ }
42
+
43
+ impl Connection for TcpStream {
44
+ fn connected(&self) -> Connected {
45
+ let connected = Connected::new();
46
+ if let (Ok(remote_addr), Ok(local_addr)) = (self.peer_addr(), self.local_addr()) {
47
+ connected.extra(HttpInfo {
48
+ remote_addr,
49
+ local_addr,
50
+ })
51
+ } else {
52
+ connected
53
+ }
54
+ }
55
+ }
56
+
57
+ impl TlsInfoFactory for TcpStream {}
@@ -0,0 +1,561 @@
1
+ //! TCP connection types and utilities.
2
+
3
+ #[cfg(feature = "tokio-rt")]
4
+ pub mod tokio;
5
+
6
+ #[cfg(feature = "compio-rt")]
7
+ pub mod compio;
8
+
9
+ use std::{
10
+ error::Error as StdError,
11
+ fmt,
12
+ future::Future,
13
+ io,
14
+ net::{Ipv4Addr, Ipv6Addr, SocketAddr},
15
+ pin::{Pin, pin},
16
+ time::Duration,
17
+ };
18
+
19
+ use futures_util::future::Either;
20
+ use socket2::TcpKeepalive;
21
+
22
+ use crate::{
23
+ conn::{Connection, net::SocketBindOptions},
24
+ dns,
25
+ error::BoxError,
26
+ };
27
+
28
+ type BoxConnecting<T, E> = Pin<Box<dyn Future<Output = Result<T, E>> + Send>>;
29
+
30
+ /// A builder for tcp connections.
31
+ pub trait TcpConnector: Clone + Send + Sync + 'static {
32
+ /// The underlying stream type.
33
+ type TcpStream: From<socket2::Socket> + Send + Sync + 'static;
34
+
35
+ /// The type of connection returned by this builder.
36
+ type Connection: ::tokio::io::AsyncRead
37
+ + ::tokio::io::AsyncWrite
38
+ + Connection
39
+ + Send
40
+ + Unpin
41
+ + 'static;
42
+
43
+ /// The type of error returned by this builder.
44
+ type Error: Into<Box<dyn StdError + Send + Sync>>;
45
+
46
+ /// The future type returned by this builder.
47
+ type Future: Future<Output = Result<Self::Connection, Self::Error>> + Send + 'static;
48
+
49
+ /// The future type returned by this builder's sleep.
50
+ type Sleep: Future<Output = ()> + Send + 'static;
51
+
52
+ /// Build a connection from the given socket and connect to the address.
53
+ fn connect(&self, socket: Self::TcpStream, addr: SocketAddr) -> Self::Future;
54
+
55
+ /// Return a future that sleeps for the given duration.
56
+ fn sleep(&self, duration: Duration) -> Self::Sleep;
57
+ }
58
+
59
+ pub(crate) struct ConnectingTcp<S: TcpConnector> {
60
+ preferred: ConnectingTcpRemote<S>,
61
+ fallback: Option<ConnectingTcpFallback<S>>,
62
+ }
63
+
64
+ struct ConnectingTcpFallback<S: TcpConnector> {
65
+ delay: S::Sleep,
66
+ remote: ConnectingTcpRemote<S>,
67
+ }
68
+
69
+ struct ConnectingTcpRemote<S: TcpConnector> {
70
+ addrs: dns::SocketAddrs,
71
+ connect_timeout: Option<Duration>,
72
+ connector: S,
73
+ }
74
+
75
+ impl<S: TcpConnector> ConnectingTcp<S>
76
+ where
77
+ S::TcpStream: From<socket2::Socket>,
78
+ {
79
+ pub(crate) fn new(remote_addrs: dns::SocketAddrs, config: &TcpOptions, connector: S) -> Self {
80
+ if let Some(fallback_timeout) = config.happy_eyeballs_timeout {
81
+ let (preferred_addrs, fallback_addrs) = remote_addrs.split_by_preference(
82
+ config.socket_bind.ipv4_address,
83
+ config.socket_bind.ipv6_address,
84
+ );
85
+ if fallback_addrs.is_empty() {
86
+ return ConnectingTcp {
87
+ preferred: ConnectingTcpRemote::new(
88
+ preferred_addrs,
89
+ config.connect_timeout,
90
+ connector,
91
+ ),
92
+ fallback: None,
93
+ };
94
+ }
95
+
96
+ ConnectingTcp {
97
+ preferred: ConnectingTcpRemote::new(
98
+ preferred_addrs,
99
+ config.connect_timeout,
100
+ connector.clone(),
101
+ ),
102
+ fallback: Some(ConnectingTcpFallback {
103
+ delay: connector.sleep(fallback_timeout),
104
+ remote: ConnectingTcpRemote::new(
105
+ fallback_addrs,
106
+ config.connect_timeout,
107
+ connector,
108
+ ),
109
+ }),
110
+ }
111
+ } else {
112
+ ConnectingTcp {
113
+ preferred: ConnectingTcpRemote::new(
114
+ remote_addrs,
115
+ config.connect_timeout,
116
+ connector,
117
+ ),
118
+ fallback: None,
119
+ }
120
+ }
121
+ }
122
+ }
123
+
124
+ impl<S: TcpConnector> ConnectingTcpRemote<S>
125
+ where
126
+ S::TcpStream: From<socket2::Socket>,
127
+ {
128
+ fn new(addrs: dns::SocketAddrs, connect_timeout: Option<Duration>, connector: S) -> Self {
129
+ let connect_timeout = connect_timeout.and_then(|t| t.checked_div(addrs.len() as u32));
130
+ Self {
131
+ addrs,
132
+ connect_timeout,
133
+ connector,
134
+ }
135
+ }
136
+
137
+ async fn connect(&mut self, config: &TcpOptions) -> Result<S::Connection, ConnectError> {
138
+ let mut err = None;
139
+ for addr in &mut self.addrs {
140
+ debug!("connecting to {}", addr);
141
+ match connect(&addr, config, self.connect_timeout, &self.connector) {
142
+ Ok(fut) => match fut.await {
143
+ Ok(tcp) => {
144
+ debug!("connected to {}", addr);
145
+ return Ok(tcp);
146
+ }
147
+ Err(mut e) => {
148
+ trace!("connect error for {}: {:?}", addr, e);
149
+ e.addr = Some(addr);
150
+ if err.is_none() {
151
+ err = Some(e);
152
+ }
153
+ }
154
+ },
155
+ Err(mut e) => {
156
+ trace!("connect error for {}: {:?}", addr, e);
157
+ e.addr = Some(addr);
158
+ if err.is_none() {
159
+ err = Some(e);
160
+ }
161
+ }
162
+ }
163
+ }
164
+
165
+ match err {
166
+ Some(e) => Err(e),
167
+ None => Err(ConnectError::new(
168
+ "tcp connect error",
169
+ std::io::Error::new(std::io::ErrorKind::NotConnected, "Network unreachable"),
170
+ )),
171
+ }
172
+ }
173
+ }
174
+
175
+ fn bind_local_address(
176
+ socket: &socket2::Socket,
177
+ dst_addr: &SocketAddr,
178
+ local_addr_ipv4: &Option<Ipv4Addr>,
179
+ local_addr_ipv6: &Option<Ipv6Addr>,
180
+ ) -> io::Result<()> {
181
+ match (*dst_addr, local_addr_ipv4, local_addr_ipv6) {
182
+ (SocketAddr::V4(_), Some(addr), _) => {
183
+ socket.bind(&SocketAddr::new((*addr).into(), 0).into())?;
184
+ }
185
+ (SocketAddr::V6(_), _, Some(addr)) => {
186
+ socket.bind(&SocketAddr::new((*addr).into(), 0).into())?;
187
+ }
188
+ _ => {
189
+ if cfg!(windows) {
190
+ // Windows requires a socket be bound before calling connect
191
+ let any: SocketAddr = match *dst_addr {
192
+ SocketAddr::V4(_) => ([0, 0, 0, 0], 0).into(),
193
+ SocketAddr::V6(_) => ([0, 0, 0, 0, 0, 0, 0, 0], 0).into(),
194
+ };
195
+ socket.bind(&any.into())?;
196
+ }
197
+ }
198
+ }
199
+
200
+ Ok(())
201
+ }
202
+
203
+ fn connect<S: TcpConnector>(
204
+ addr: &SocketAddr,
205
+ config: &TcpOptions,
206
+ connect_timeout: Option<Duration>,
207
+ connector: &S,
208
+ ) -> Result<impl Future<Output = Result<S::Connection, ConnectError>>, ConnectError>
209
+ where
210
+ S::TcpStream: From<socket2::Socket>,
211
+ {
212
+ use socket2::{Domain, Protocol, Socket, Type};
213
+
214
+ let domain = Domain::for_address(*addr);
215
+ let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))
216
+ .map_err(ConnectError::m("tcp open error"))?;
217
+
218
+ // When constructing a Tokio `TcpSocket` from a raw fd/socket, the user is
219
+ // responsible for ensuring O_NONBLOCK is set.
220
+ socket
221
+ .set_nonblocking(true)
222
+ .map_err(ConnectError::m("tcp set_nonblocking error"))?;
223
+
224
+ if let Some(tcp_keepalive) = &config.tcp_keepalive.into_tcpkeepalive() {
225
+ if let Err(_e) = socket.set_tcp_keepalive(tcp_keepalive) {
226
+ warn!("tcp set_keepalive error: {_e}");
227
+ }
228
+ }
229
+
230
+ // That this only works for some socket types, particularly AF_INET sockets.
231
+ #[cfg(any(
232
+ target_os = "android",
233
+ target_os = "fuchsia",
234
+ target_os = "illumos",
235
+ target_os = "ios",
236
+ target_os = "linux",
237
+ target_os = "macos",
238
+ target_os = "solaris",
239
+ target_os = "tvos",
240
+ target_os = "visionos",
241
+ target_os = "watchos",
242
+ ))]
243
+ if let Some(interface) = &config.socket_bind.interface {
244
+ // On Linux-like systems, set the interface to bind using
245
+ // `SO_BINDTODEVICE`.
246
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
247
+ socket
248
+ .bind_device(Some(interface.as_bytes()))
249
+ .map_err(ConnectError::m("tcp bind interface error"))?;
250
+
251
+ // On macOS-like and Solaris-like systems, we instead use `IP_BOUND_IF`.
252
+ // This socket option desires an integer index for the interface, so we
253
+ // must first determine the index of the requested interface name using
254
+ // `if_nametoindex`.
255
+ #[cfg(any(
256
+ target_os = "illumos",
257
+ target_os = "ios",
258
+ target_os = "macos",
259
+ target_os = "solaris",
260
+ target_os = "tvos",
261
+ target_os = "visionos",
262
+ target_os = "watchos",
263
+ ))]
264
+ if let Ok(interface) = std::ffi::CString::new(interface.as_bytes()) {
265
+ #[allow(unsafe_code)]
266
+ let idx = unsafe { libc::if_nametoindex(interface.as_ptr()) };
267
+ let idx = std::num::NonZeroU32::new(idx).ok_or_else(|| {
268
+ // If the index is 0, check errno and return an I/O error.
269
+ ConnectError::new(
270
+ "error converting interface name to index",
271
+ io::Error::last_os_error(),
272
+ )
273
+ })?;
274
+
275
+ // Different setsockopt calls are necessary depending on whether the
276
+ // address is IPv4 or IPv6.
277
+ match addr {
278
+ SocketAddr::V4(_) => socket.bind_device_by_index_v4(Some(idx)),
279
+ SocketAddr::V6(_) => socket.bind_device_by_index_v6(Some(idx)),
280
+ }
281
+ .map_err(ConnectError::m("tcp bind interface error"))?;
282
+ }
283
+ }
284
+
285
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
286
+ if let Some(tcp_user_timeout) = &config.tcp_user_timeout {
287
+ if let Err(_e) = socket.set_tcp_user_timeout(Some(*tcp_user_timeout)) {
288
+ warn!("tcp set_tcp_user_timeout error: {_e}");
289
+ }
290
+ }
291
+
292
+ bind_local_address(
293
+ &socket,
294
+ addr,
295
+ &config.socket_bind.ipv4_address,
296
+ &config.socket_bind.ipv6_address,
297
+ )
298
+ .map_err(ConnectError::m("tcp bind local error"))?;
299
+
300
+ if config.reuse_address {
301
+ if let Err(_e) = socket.set_reuse_address(true) {
302
+ warn!("tcp set_reuse_address error: {_e}");
303
+ }
304
+ }
305
+
306
+ if let Some(size) = config.send_buffer_size {
307
+ if let Err(_e) = socket.set_send_buffer_size(size) {
308
+ warn!("tcp set_buffer_size error: {_e}");
309
+ }
310
+ }
311
+
312
+ if let Some(size) = config.recv_buffer_size {
313
+ if let Err(_e) = socket.set_recv_buffer_size(size) {
314
+ warn!("tcp set_recv_buffer_size error: {_e}");
315
+ }
316
+ }
317
+
318
+ if let Err(_e) = socket.set_tcp_nodelay(config.nodelay) {
319
+ warn!("tcp set_tcp_nodelay error: {_e}");
320
+ }
321
+
322
+ let connect = connector.connect(socket.into(), *addr);
323
+ let sleep = connect_timeout.map(|dur| connector.sleep(dur));
324
+
325
+ Ok(async move {
326
+ match sleep {
327
+ Some(sleep) => match futures_util::future::select(pin!(sleep), pin!(connect)).await {
328
+ Either::Left(((), _)) => {
329
+ Err(io::Error::new(io::ErrorKind::TimedOut, "connect timeout").into())
330
+ }
331
+ Either::Right((Ok(s), _)) => Ok(s),
332
+ Either::Right((Err(e), _)) => Err(e.into()),
333
+ },
334
+ None => connect.await.map_err(Into::into),
335
+ }
336
+ .map_err(ConnectError::m("tcp connect error"))
337
+ })
338
+ }
339
+
340
+ impl<S: TcpConnector> ConnectingTcp<S>
341
+ where
342
+ S::TcpStream: From<socket2::Socket>,
343
+ {
344
+ pub(crate) async fn connect(
345
+ mut self,
346
+ config: &TcpOptions,
347
+ ) -> Result<S::Connection, ConnectError> {
348
+ match self.fallback {
349
+ None => self.preferred.connect(config).await,
350
+ Some(mut fallback) => {
351
+ let preferred_fut = pin!(self.preferred.connect(config));
352
+ let fallback_fut = pin!(fallback.remote.connect(config));
353
+ let fallback_delay = pin!(fallback.delay);
354
+
355
+ let (result, future) =
356
+ match futures_util::future::select(preferred_fut, fallback_delay).await {
357
+ Either::Left((result, _fallback_delay)) => {
358
+ (result, Either::Right(fallback_fut))
359
+ }
360
+ Either::Right(((), preferred_fut)) => {
361
+ // Delay is done, start polling both the preferred and the fallback
362
+ futures_util::future::select(preferred_fut, fallback_fut)
363
+ .await
364
+ .factor_first()
365
+ }
366
+ };
367
+
368
+ if result.is_err() {
369
+ // Fallback to the remaining future (could be preferred or fallback)
370
+ // if we get an error
371
+ future.await
372
+ } else {
373
+ result
374
+ }
375
+ }
376
+ }
377
+ }
378
+ }
379
+
380
+ // Not publicly exported (so missing_docs doesn't trigger).
381
+ pub struct ConnectError {
382
+ pub(crate) msg: &'static str,
383
+ pub(crate) addr: Option<SocketAddr>,
384
+ pub(crate) cause: Option<BoxError>,
385
+ }
386
+
387
+ impl ConnectError {
388
+ pub(crate) fn new<E>(msg: &'static str, cause: E) -> ConnectError
389
+ where
390
+ E: Into<BoxError>,
391
+ {
392
+ ConnectError {
393
+ msg,
394
+ addr: None,
395
+ cause: Some(cause.into()),
396
+ }
397
+ }
398
+
399
+ pub(crate) fn dns<E>(cause: E) -> ConnectError
400
+ where
401
+ E: Into<BoxError>,
402
+ {
403
+ ConnectError::new("dns error", cause)
404
+ }
405
+
406
+ pub(crate) fn m<E>(msg: &'static str) -> impl FnOnce(E) -> ConnectError
407
+ where
408
+ E: Into<BoxError>,
409
+ {
410
+ move |cause| ConnectError::new(msg, cause)
411
+ }
412
+ }
413
+
414
+ impl fmt::Debug for ConnectError {
415
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416
+ let mut b = f.debug_tuple("ConnectError");
417
+ b.field(&self.msg);
418
+ if let Some(ref addr) = self.addr {
419
+ b.field(addr);
420
+ }
421
+ if let Some(ref cause) = self.cause {
422
+ b.field(cause);
423
+ }
424
+ b.finish()
425
+ }
426
+ }
427
+
428
+ impl fmt::Display for ConnectError {
429
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
430
+ f.write_str(self.msg)
431
+ }
432
+ }
433
+
434
+ impl StdError for ConnectError {
435
+ fn source(&self) -> Option<&(dyn StdError + 'static)> {
436
+ self.cause.as_ref().map(|e| &**e as _)
437
+ }
438
+ }
439
+
440
+ #[derive(Clone)]
441
+ pub(crate) struct TcpOptions {
442
+ pub enforce_http: bool,
443
+ pub connect_timeout: Option<Duration>,
444
+ pub happy_eyeballs_timeout: Option<Duration>,
445
+ pub nodelay: bool,
446
+ pub reuse_address: bool,
447
+ pub send_buffer_size: Option<usize>,
448
+ pub recv_buffer_size: Option<usize>,
449
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
450
+ pub tcp_user_timeout: Option<Duration>,
451
+ pub tcp_keepalive: TcpKeepaliveOptions,
452
+ pub socket_bind: SocketBindOptions,
453
+ }
454
+
455
+ #[derive(Default, Debug, Clone, Copy)]
456
+ pub(crate) struct TcpKeepaliveOptions {
457
+ pub time: Option<Duration>,
458
+ #[cfg(any(
459
+ target_os = "android",
460
+ target_os = "dragonfly",
461
+ target_os = "freebsd",
462
+ target_os = "fuchsia",
463
+ target_os = "illumos",
464
+ target_os = "ios",
465
+ target_os = "visionos",
466
+ target_os = "linux",
467
+ target_os = "macos",
468
+ target_os = "netbsd",
469
+ target_os = "tvos",
470
+ target_os = "watchos",
471
+ target_os = "windows",
472
+ target_os = "cygwin",
473
+ ))]
474
+ pub interval: Option<Duration>,
475
+ #[cfg(any(
476
+ target_os = "android",
477
+ target_os = "dragonfly",
478
+ target_os = "freebsd",
479
+ target_os = "fuchsia",
480
+ target_os = "illumos",
481
+ target_os = "ios",
482
+ target_os = "visionos",
483
+ target_os = "linux",
484
+ target_os = "macos",
485
+ target_os = "netbsd",
486
+ target_os = "tvos",
487
+ target_os = "watchos",
488
+ target_os = "cygwin",
489
+ target_os = "windows",
490
+ ))]
491
+ pub retries: Option<u32>,
492
+ }
493
+
494
+ impl TcpKeepaliveOptions {
495
+ /// Converts into a `socket2::TcpKeealive` if there is any keep alive configuration.
496
+ pub(crate) fn into_tcpkeepalive(self) -> Option<TcpKeepalive> {
497
+ let mut dirty = false;
498
+ let mut ka = TcpKeepalive::new();
499
+ if let Some(time) = self.time {
500
+ ka = ka.with_time(time);
501
+ dirty = true
502
+ }
503
+
504
+ // Set the value of the `TCP_KEEPINTVL` option. On Windows, this sets the
505
+ // value of the `tcp_keepalive` struct's `keepaliveinterval` field.
506
+ //
507
+ // Sets the time interval between TCP keepalive probes.
508
+ //
509
+ // Some platforms specify this value in seconds, so sub-second
510
+ // specifications may be omitted.
511
+ #[cfg(any(
512
+ target_os = "android",
513
+ target_os = "dragonfly",
514
+ target_os = "freebsd",
515
+ target_os = "fuchsia",
516
+ target_os = "illumos",
517
+ target_os = "ios",
518
+ target_os = "visionos",
519
+ target_os = "linux",
520
+ target_os = "macos",
521
+ target_os = "netbsd",
522
+ target_os = "tvos",
523
+ target_os = "watchos",
524
+ target_os = "windows",
525
+ target_os = "cygwin",
526
+ ))]
527
+ {
528
+ if let Some(interval) = self.interval {
529
+ dirty = true;
530
+ ka = ka.with_interval(interval)
531
+ };
532
+ }
533
+
534
+ // Set the value of the `TCP_KEEPCNT` option.
535
+ //
536
+ // Set the maximum number of TCP keepalive probes that will be sent before
537
+ // dropping a connection, if TCP keepalive is enabled on this socket.
538
+ #[cfg(any(
539
+ target_os = "android",
540
+ target_os = "dragonfly",
541
+ target_os = "freebsd",
542
+ target_os = "fuchsia",
543
+ target_os = "illumos",
544
+ target_os = "ios",
545
+ target_os = "visionos",
546
+ target_os = "linux",
547
+ target_os = "macos",
548
+ target_os = "netbsd",
549
+ target_os = "tvos",
550
+ target_os = "watchos",
551
+ target_os = "cygwin",
552
+ target_os = "windows",
553
+ ))]
554
+ if let Some(retries) = self.retries {
555
+ dirty = true;
556
+ ka = ka.with_retries(retries)
557
+ };
558
+
559
+ if dirty { Some(ka) } else { None }
560
+ }
561
+ }