wreq-rb 0.6.0 → 0.6.2

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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +257 -102
  3. data/ext/wreq_rb/Cargo.toml +7 -4
  4. data/ext/wreq_rb/src/client.rs +105 -15
  5. data/ext/wreq_rb/src/response.rs +28 -3
  6. data/lib/wreq-rb/version.rb +1 -1
  7. data/patches/0001-add-transfer-size-tracking.patch +11 -15
  8. data/vendor/wreq/Cargo.toml +9 -8
  9. data/vendor/wreq/README.md +5 -5
  10. data/vendor/wreq/bench/support/bench.rs +6 -2
  11. data/vendor/wreq/bench/support/client.rs +88 -1
  12. data/vendor/wreq/bench/support/exec.rs +0 -0
  13. data/vendor/wreq/bench/support/rt.rs +34 -0
  14. data/vendor/wreq/bench/support/server.rs +1 -1
  15. data/vendor/wreq/bench/support.rs +1 -15
  16. data/vendor/wreq/examples/cert_store.rs +13 -13
  17. data/vendor/wreq/examples/request_with_emulate.rs +1 -1
  18. data/vendor/wreq/examples/tcp_linger.rs +22 -0
  19. data/vendor/wreq/src/client/layer/client/pool.rs +17 -17
  20. data/vendor/wreq/src/client/layer/client.rs +2 -0
  21. data/vendor/wreq/src/client/layer/decoder.rs +71 -17
  22. data/vendor/wreq/src/client/layer/redirect/future.rs +49 -63
  23. data/vendor/wreq/src/client/layer/redirect/policy.rs +2 -26
  24. data/vendor/wreq/src/client/layer/redirect.rs +48 -60
  25. data/vendor/wreq/src/client/layer/retry.rs +12 -15
  26. data/vendor/wreq/src/client/layer/timeout/body.rs +27 -21
  27. data/vendor/wreq/src/client/layer/timeout/future.rs +33 -58
  28. data/vendor/wreq/src/client/layer/timeout.rs +8 -14
  29. data/vendor/wreq/src/client/request.rs +4 -0
  30. data/vendor/wreq/src/client.rs +53 -31
  31. data/vendor/wreq/src/conn/connector.rs +99 -129
  32. data/vendor/wreq/src/conn/http.rs +25 -18
  33. data/vendor/wreq/src/conn/net/tcp.rs +601 -107
  34. data/vendor/wreq/src/conn/proxy/socks.rs +6 -6
  35. data/vendor/wreq/src/conn/timeout.rs +166 -0
  36. data/vendor/wreq/src/conn.rs +5 -4
  37. data/vendor/wreq/src/cookie/jar.rs +1225 -0
  38. data/vendor/wreq/src/cookie/store.rs +321 -0
  39. data/vendor/wreq/src/cookie.rs +108 -612
  40. data/vendor/wreq/src/dns/resolve.rs +8 -2
  41. data/vendor/wreq/src/dns.rs +4 -4
  42. data/vendor/wreq/src/error.rs +53 -20
  43. data/vendor/wreq/src/lib.rs +1 -0
  44. data/vendor/wreq/src/proxy/matcher.rs +26 -12
  45. data/vendor/wreq/src/proxy/win.rs +39 -9
  46. data/vendor/wreq/src/redirect.rs +515 -100
  47. data/vendor/wreq/src/tls/conn.rs +3 -11
  48. data/vendor/wreq/src/tls/session.rs +7 -8
  49. data/vendor/wreq/src/tls/trust/store.rs +4 -4
  50. data/vendor/wreq/src/util.rs +23 -0
  51. data/vendor/wreq/tests/badssl.rs +72 -7
  52. data/vendor/wreq/tests/brotli.rs +1 -1
  53. data/vendor/wreq/tests/client.rs +24 -0
  54. data/vendor/wreq/tests/connector_layers.rs +8 -4
  55. data/vendor/wreq/tests/cookie.rs +59 -0
  56. data/vendor/wreq/tests/deflate.rs +1 -1
  57. data/vendor/wreq/tests/gzip.rs +53 -1
  58. data/vendor/wreq/tests/layers.rs +8 -4
  59. data/vendor/wreq/tests/redirect.rs +180 -97
  60. data/vendor/wreq/tests/timeouts.rs +47 -12
  61. data/vendor/wreq/tests/zstd.rs +1 -1
  62. metadata +8 -2
@@ -60,8 +60,10 @@ use crate::dns::hickory::HickoryDnsResolver;
60
60
  use crate::{
61
61
  IntoUri, Method, Proxy,
62
62
  conn::{
63
- BoxedConnectorLayer, BoxedConnectorService, Conn, Unnameable, connector::Connector,
64
- http::HttpConnect, net::SocketBindOptions,
63
+ BoxedConnectorLayer, BoxedTransportConnector, Conn, Unnameable,
64
+ connector::{Connector, ConnectorBuilder},
65
+ http::HttpConnect,
66
+ net::SocketBindOptions,
65
67
  },
66
68
  dns::{DnsResolverWithOverrides, DynResolver, GaiResolver, IntoResolve, Resolve},
67
69
  error::{self, Error},
@@ -114,9 +116,7 @@ type MaybeDecompressionBody<T> = tower_http::decompression::DecompressionBody<T>
114
116
 
115
117
  type ClientService = Timeout<
116
118
  ConfigService<
117
- MaybeDecompression<
118
- Retry<RetryPolicy, FollowRedirect<HttpClient<Connector, Body>, FollowRedirectPolicy>>,
119
- >,
119
+ MaybeDecompression<Retry<RetryPolicy, FollowRedirect<HttpClient<Connector, Body>>>>,
120
120
  >,
121
121
  >;
122
122
 
@@ -185,6 +185,7 @@ struct Config {
185
185
  pool_max_size: Option<NonZeroUsize>,
186
186
  tcp_nodelay: bool,
187
187
  tcp_reuse_address: bool,
188
+ tcp_linger: Option<Duration>,
188
189
  tcp_keepalive: Option<Duration>,
189
190
  tcp_keepalive_interval: Option<Duration>,
190
191
  tcp_keepalive_retries: Option<u32>,
@@ -276,6 +277,7 @@ impl Client {
276
277
  tcp_user_timeout: Some(Duration::from_secs(30)),
277
278
  tcp_nodelay: true,
278
279
  tcp_reuse_address: false,
280
+ tcp_linger: None,
279
281
  tcp_send_buffer_size: None,
280
282
  tcp_recv_buffer_size: None,
281
283
  tcp_happy_eyeballs_timeout: Some(Duration::from_millis(300)),
@@ -502,7 +504,8 @@ impl ClientBuilder {
502
504
  DynResolver::new(resolver)
503
505
  };
504
506
 
505
- let connector = Connector::builder(config.proxies, resolver)
507
+ let connector = ConnectorBuilder::new(config.proxies, resolver)
508
+ .timer(config.timer.clone())
506
509
  .timeout(config.connect_timeout)
507
510
  .tls_info(config.tls_info)
508
511
  .tcp_nodelay(config.tcp_nodelay)
@@ -529,6 +532,7 @@ impl ClientBuilder {
529
532
  http.set_keepalive_interval(config.tcp_keepalive_interval);
530
533
  http.set_keepalive_retries(config.tcp_keepalive_retries);
531
534
  http.set_reuse_address(config.tcp_reuse_address);
535
+ http.set_linger(config.tcp_linger);
532
536
  http.set_connect_timeout(config.connect_timeout);
533
537
  http.set_nodelay(config.tcp_nodelay);
534
538
  http.set_send_buffer_size(config.tcp_send_buffer_size);
@@ -788,9 +792,8 @@ impl ClientBuilder {
788
792
  ///
789
793
  /// If auto gzip decompression is turned on:
790
794
  ///
791
- /// - When sending a request and if the request's headers do not already contain an
792
- /// `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to `gzip`.
793
- /// The request body is **not** automatically compressed.
795
+ /// - Requests without `Accept-Encoding` advertise `gzip`. Range requests use `Accept-Encoding:
796
+ /// identity` instead. The request body is **not** automatically compressed.
794
797
  /// - When receiving a response, if its headers contain a `Content-Encoding` value of `gzip`,
795
798
  /// both `Content-Encoding` and `Content-Length` are removed from the headers' set. The
796
799
  /// response body is automatically decompressed.
@@ -812,9 +815,8 @@ impl ClientBuilder {
812
815
  ///
813
816
  /// If auto brotli decompression is turned on:
814
817
  ///
815
- /// - When sending a request and if the request's headers do not already contain an
816
- /// `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to `br`. The
817
- /// request body is **not** automatically compressed.
818
+ /// - Requests without `Accept-Encoding` advertise `br`. Range requests use `Accept-Encoding:
819
+ /// identity` instead. The request body is **not** automatically compressed.
818
820
  /// - When receiving a response, if its headers contain a `Content-Encoding` value of `br`, both
819
821
  /// `Content-Encoding` and `Content-Length` are removed from the headers' set. The response
820
822
  /// body is automatically decompressed.
@@ -836,9 +838,8 @@ impl ClientBuilder {
836
838
  ///
837
839
  /// If auto zstd decompression is turned on:
838
840
  ///
839
- /// - When sending a request and if the request's headers do not already contain an
840
- /// `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to `zstd`.
841
- /// The request body is **not** automatically compressed.
841
+ /// - Requests without `Accept-Encoding` advertise `zstd`. Range requests use `Accept-Encoding:
842
+ /// identity` instead. The request body is **not** automatically compressed.
842
843
  /// - When receiving a response, if its headers contain a `Content-Encoding` value of `zstd`,
843
844
  /// both `Content-Encoding` and `Content-Length` are removed from the headers' set. The
844
845
  /// response body is automatically decompressed.
@@ -860,9 +861,8 @@ impl ClientBuilder {
860
861
  ///
861
862
  /// If auto deflate decompression is turned on:
862
863
  ///
863
- /// - When sending a request and if the request's headers do not already contain an
864
- /// `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to
865
- /// `deflate`. The request body is **not** automatically compressed.
864
+ /// - Requests without `Accept-Encoding` advertise `deflate`. Range requests use
865
+ /// `Accept-Encoding: identity` instead. The request body is **not** automatically compressed.
866
866
  /// - When receiving a response, if it's headers contain a `Content-Encoding` value that equals
867
867
  /// to `deflate`, both values `Content-Encoding` and `Content-Length` are removed from the
868
868
  /// headers' set. The response body is automatically decompressed.
@@ -963,7 +963,11 @@ impl ClientBuilder {
963
963
  self
964
964
  }
965
965
 
966
- /// Enable or disable automatic setting of the `Referer` header.
966
+ /// Enable or disable automatic `Referer` management during redirects.
967
+ ///
968
+ /// When enabled, redirect responses can control how an existing `Referer`
969
+ /// is forwarded through `Referrer-Policy`. No header is added when the
970
+ /// request has none.
967
971
  ///
968
972
  /// Default is `true`.
969
973
  #[inline]
@@ -1041,12 +1045,10 @@ impl ClientBuilder {
1041
1045
 
1042
1046
  /// Set a timeout for only the connect phase of a `Client`.
1043
1047
  ///
1044
- /// Default is `None`.
1045
- ///
1046
- /// # Note
1048
+ /// The timeout covers DNS resolution, the complete TCP address race,
1049
+ /// proxy tunneling, and the TLS handshake.
1047
1050
  ///
1048
- /// This **requires** the futures be executed in a tokio runtime with
1049
- /// a tokio timer enabled.
1051
+ /// Default is `None`.
1050
1052
  #[inline]
1051
1053
  pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
1052
1054
  self.config.connect_timeout = Some(timeout);
@@ -1220,6 +1222,27 @@ impl ClientBuilder {
1220
1222
  self
1221
1223
  }
1222
1224
 
1225
+ /// Set that all sockets have `SO_LINGER` set with the supplied duration.
1226
+ ///
1227
+ /// This option controls how long the socket lingers on close while data
1228
+ /// remains unsent. A duration of zero aborts the connection on close: unsent
1229
+ /// data is discarded, the connection is reset (`RST`) rather than going
1230
+ /// through the normal `FIN` termination sequence, and the socket does not
1231
+ /// enter `TIME_WAIT`.
1232
+ ///
1233
+ /// If `None`, the option will not be set and the operating system default
1234
+ /// applies.
1235
+ ///
1236
+ /// Default is `None`.
1237
+ #[inline]
1238
+ pub fn tcp_linger<D>(mut self, val: D) -> ClientBuilder
1239
+ where
1240
+ D: Into<Option<Duration>>,
1241
+ {
1242
+ self.config.tcp_linger = val.into();
1243
+ self
1244
+ }
1245
+
1223
1246
  /// Sets the size of the TCP send buffer on this client socket.
1224
1247
  ///
1225
1248
  /// On most operating systems, this sets the `SO_SNDBUF` socket option.
@@ -1244,18 +1267,17 @@ impl ClientBuilder {
1244
1267
  self
1245
1268
  }
1246
1269
 
1247
- /// Set timeout for [RFC 6555 (Happy Eyeballs)][RFC 6555] algorithm.
1270
+ /// Set the delay between connection attempts for
1271
+ /// [RFC 8305 (Happy Eyeballs v2)][RFC 8305].
1248
1272
  ///
1249
- /// If hostname resolves to both IPv4 and IPv6 addresses and connection
1250
- /// cannot be established using preferred address family before timeout
1251
- /// elapses, then connector will in parallel attempt connection using other
1252
- /// address family.
1273
+ /// The first address is attempted immediately. Later addresses are started
1274
+ /// after this delay without waiting for the previous attempt to finish.
1253
1275
  ///
1254
1276
  /// If `None`, parallel connection attempts are disabled.
1255
1277
  ///
1256
1278
  /// Default is 300 milliseconds.
1257
1279
  ///
1258
- /// [RFC 6555]: https://tools.ietf.org/html/rfc6555
1280
+ /// [RFC 8305]: https://www.rfc-editor.org/rfc/rfc8305.html#section-5
1259
1281
  #[inline]
1260
1282
  pub fn tcp_happy_eyeballs_timeout<D>(mut self, val: D) -> ClientBuilder
1261
1283
  where
@@ -1631,7 +1653,7 @@ impl ClientBuilder {
1631
1653
  #[inline]
1632
1654
  pub fn connector_layer<L>(mut self, layer: L) -> ClientBuilder
1633
1655
  where
1634
- L: Layer<BoxedConnectorService> + Clone + Send + Sync + 'static,
1656
+ L: Layer<BoxedTransportConnector> + Clone + Send + Sync + 'static,
1635
1657
  L::Service:
1636
1658
  Service<Unnameable, Response = Conn, Error = BoxError> + Clone + Send + Sync + 'static,
1637
1659
  <L::Service as Service<Unnameable>>::Future: Send + 'static,
@@ -1,31 +1,35 @@
1
1
  use std::{
2
2
  borrow::Cow,
3
- future::Future,
4
- pin::Pin,
5
3
  sync::Arc,
6
4
  task::{Context, Poll},
7
5
  time::Duration,
8
6
  };
9
7
 
8
+ use futures_util::future::BoxFuture;
10
9
  use tokio_btls::SslStream;
11
10
  use tower::{
12
- BoxError, Service, ServiceBuilder, ServiceExt,
13
- timeout::TimeoutLayer,
14
- util::{BoxCloneSyncService, MapRequestLayer},
11
+ BoxError, Layer, Service, ServiceBuilder, ServiceExt,
12
+ util::{BoxCloneSyncService, Either, MapRequest, MapRequestLayer},
15
13
  };
16
14
 
17
15
  #[cfg(unix)]
18
16
  use super::net::UnixConnector;
19
17
  use super::{
20
- AsyncConnWithInfo, BoxedConnectorLayer, BoxedConnectorService, Conn, Connection, HttpConnector,
21
- TlsConn, TlsInfoFactory, Unnameable, descriptor::ConnectionDescriptor, http::HttpConnect,
22
- net::TcpConnector, proxy, verbose::Verbose,
18
+ AsyncConnWithInfo, BoxedConnectorLayer, BoxedTransportConnector, Conn, Connection,
19
+ HttpConnector, TlsConn, TlsInfoFactory, Unnameable,
20
+ descriptor::ConnectionDescriptor,
21
+ http::HttpConnect,
22
+ net::TcpConnector,
23
+ proxy,
24
+ timeout::{Timeout, TimeoutLayer},
25
+ verbose::Verbose,
23
26
  };
24
27
  use crate::{
25
28
  dns::DynResolver,
26
- error::{ProxyConnect, TimedOut, map_timeout_to_connector_error},
29
+ error::{ProxyConnect, map_timeout_to_connector_error},
27
30
  ext::UriExt,
28
31
  proxy::{Intercepted, Matcher as ProxyMatcher, matcher::Intercept},
32
+ rt::Timer,
29
33
  tls::{
30
34
  TlsOptions,
31
35
  conn::{
@@ -34,41 +38,51 @@ use crate::{
34
38
  },
35
39
  };
36
40
 
37
- type Connecting = Pin<Box<dyn Future<Output = Result<Conn, BoxError>> + Send>>;
38
-
39
- /// Configuration for the connector service.
41
+ /// Client-wide connection settings retained by each transport connector.
42
+ ///
43
+ /// These defaults control proxy selection, socket behavior, stream instrumentation, and TLS
44
+ /// metadata. Settings that can vary per request remain in [`ConnectionDescriptor`] and are
45
+ /// applied when a connection starts.
40
46
  #[derive(Clone)]
41
47
  struct Config {
42
48
  proxies: Arc<Vec<ProxyMatcher>>,
43
49
  verbose: Verbose,
44
50
  nodelay: bool,
45
51
  tls_info: bool,
46
- /// When there is a single timeout layer and no other layers,
47
- /// we embed it directly inside our base Service::call().
48
- /// This lets us avoid an extra `Box::pin` indirection layer
49
- /// since `tokio::time::Timeout` is `Unpin`
50
- timeout: Option<Duration>,
51
52
  }
52
53
 
53
- /// Builder for `Connector`.
54
+ /// Assembles the transport service graph used by a client.
55
+ ///
56
+ /// The builder owns DNS, TCP, TLS, and timeout configuration until [`ConnectorBuilder::build`]
57
+ /// places the timeout around the concrete connector and any user-provided layers.
54
58
  pub struct ConnectorBuilder {
55
59
  config: Config,
60
+ timer: Timer,
61
+ timeout: Option<Duration>,
56
62
  #[cfg(feature = "socks")]
57
63
  resolver: DynResolver,
58
64
  http: HttpConnector,
59
65
  builder: TlsConnectorBuilder,
60
66
  }
61
67
 
62
- /// Connector service that establishes connections.
63
- #[derive(Clone)]
64
- pub enum Connector {
65
- Simple(ConnectorService),
66
- WithLayers(BoxedConnectorService),
67
- }
68
-
69
- /// Service that establishes connections to HTTP servers.
68
+ /// Client-owned transport service selected when the connector graph is assembled.
69
+ ///
70
+ /// The left branch keeps the graph concrete when no custom layers are present. The right branch
71
+ /// wraps the type-erased custom-layer graph so it also accepts [`ConnectionDescriptor`]. Both
72
+ /// branches remain with the client and create a separately timed future for each connection
73
+ /// attempt.
74
+ pub type Connector = Either<
75
+ Timeout<TransportConnector>,
76
+ MapRequest<BoxedTransportConnector, fn(ConnectionDescriptor) -> Unnameable>,
77
+ >;
78
+
79
+ /// Establishes the transport consumed by the HTTP protocol layer.
80
+ ///
81
+ /// Each call selects the proxy path, applies request-specific socket and TLS settings, and returns
82
+ /// an established [`Conn`]. The service is cloned into each in-flight connection future, while its
83
+ /// immutable TLS builder is shared across those attempts.
70
84
  #[derive(Clone)]
71
- pub struct ConnectorService {
85
+ pub struct TransportConnector {
72
86
  config: Config,
73
87
  #[cfg(feature = "socks")]
74
88
  resolver: DynResolver,
@@ -80,6 +94,24 @@ pub struct ConnectorService {
80
94
  // ===== impl ConnectorBuilder =====
81
95
 
82
96
  impl ConnectorBuilder {
97
+ /// Creates a builder with the client's proxy and DNS configuration.
98
+ pub(crate) fn new(proxies: Vec<ProxyMatcher>, resolver: DynResolver) -> Self {
99
+ Self {
100
+ config: Config {
101
+ proxies: Arc::new(proxies),
102
+ verbose: Verbose::OFF,
103
+ nodelay: true,
104
+ tls_info: false,
105
+ },
106
+ timer: Timer::default(),
107
+ timeout: None,
108
+ #[cfg(feature = "socks")]
109
+ resolver: resolver.clone(),
110
+ http: HttpConnector::new(resolver, TcpConnector::new()),
111
+ builder: TlsConnector::builder(),
112
+ }
113
+ }
114
+
83
115
  /// Set the HTTP connector to use.
84
116
  #[inline]
85
117
  pub fn with_http<F>(mut self, call: F) -> ConnectorBuilder
@@ -101,12 +133,16 @@ impl ConnectorBuilder {
101
133
  }
102
134
 
103
135
  /// Set the connect timeout.
104
- ///
105
- /// If a domain resolves to multiple IP addresses, the timeout will be
106
- /// evenly divided across them.
107
136
  #[inline]
108
137
  pub fn timeout(mut self, timeout: Option<Duration>) -> ConnectorBuilder {
109
- self.config.timeout = timeout;
138
+ self.timeout = timeout;
139
+ self
140
+ }
141
+
142
+ /// Set the timer used to drive the connect timeout.
143
+ #[inline]
144
+ pub fn timer(mut self, timer: Timer) -> ConnectorBuilder {
145
+ self.timer = timer;
110
146
  self
111
147
  }
112
148
 
@@ -137,7 +173,8 @@ impl ConnectorBuilder {
137
173
  tls_options: Option<TlsOptions>,
138
174
  layers: Vec<BoxedConnectorLayer>,
139
175
  ) -> crate::Result<Connector> {
140
- let mut service = ConnectorService {
176
+ let timeout = TimeoutLayer::new(self.timer, self.timeout);
177
+ let service = TransportConnector {
141
178
  config: self.config,
142
179
  #[cfg(feature = "socks")]
143
180
  resolver: self.resolver.clone(),
@@ -150,12 +187,9 @@ impl ConnectorBuilder {
150
187
 
151
188
  // we have no user-provided layers, only use concrete types
152
189
  if layers.is_empty() {
153
- return Ok(Connector::Simple(service));
190
+ return Ok(Either::Left(timeout.layer(service)));
154
191
  }
155
192
 
156
- // user-provided layers exist, the timeout will be applied as an additional layer.
157
- let timeout = service.config.timeout.take();
158
-
159
193
  // otherwise we have user provided layers
160
194
  // so we need type erasure all the way through
161
195
  // as well as mapping the unnameable type of the layers back to ConnectionDescriptor for the
@@ -169,78 +203,25 @@ impl ConnectorBuilder {
169
203
  |service, layer| ServiceBuilder::new().layer(layer).service(service),
170
204
  );
171
205
 
172
- // now we handle the concrete stuff - any `connect_timeout`,
173
- // plus a final map_err layer we can use to cast default tower layer
174
- // errors to internal errors
175
- match timeout {
176
- Some(timeout) => {
177
- let service = ServiceBuilder::new()
178
- .layer(TimeoutLayer::new(timeout))
179
- .service(service)
180
- .map_err(map_timeout_to_connector_error);
181
-
182
- Ok(Connector::WithLayers(BoxCloneSyncService::new(service)))
183
- }
184
- None => {
185
- // no timeout, but still map err
186
- // no named timeout layer but we still map errors since
187
- // we might have user-provided timeout layer
188
- let service = ServiceBuilder::new()
189
- .service(service)
190
- .map_err(map_timeout_to_connector_error);
191
-
192
- Ok(Connector::WithLayers(BoxCloneSyncService::new(service)))
193
- }
194
- }
195
- }
196
- }
197
-
198
- // ===== impl Connector =====
199
-
200
- impl Connector {
201
- /// Creates a new [`Connector`] with the provided configuration and optional layers.
202
- pub(crate) fn builder(proxies: Vec<ProxyMatcher>, resolver: DynResolver) -> ConnectorBuilder {
203
- ConnectorBuilder {
204
- config: Config {
205
- proxies: Arc::new(proxies),
206
- verbose: Verbose::OFF,
207
- nodelay: true,
208
- tls_info: false,
209
- timeout: None,
210
- },
211
- #[cfg(feature = "socks")]
212
- resolver: resolver.clone(),
213
- http: HttpConnector::new(resolver, TcpConnector::new()),
214
- builder: TlsConnector::builder(),
215
- }
216
- }
217
- }
206
+ // Keep the built-in timeout outside user layers so it covers their work too.
207
+ // The final mapping also handles a tower timeout supplied by the caller.
208
+ let service = ServiceBuilder::new()
209
+ .layer(timeout)
210
+ .service(service)
211
+ .map_err(map_timeout_to_connector_error);
218
212
 
219
- impl Service<ConnectionDescriptor> for Connector {
220
- type Response = Conn;
221
- type Error = BoxError;
222
- type Future = Connecting;
223
-
224
- #[inline]
225
- fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
226
- match self {
227
- Connector::Simple(service) => service.poll_ready(cx),
228
- Connector::WithLayers(service) => service.poll_ready(cx),
229
- }
230
- }
213
+ let service = MapRequest::new(
214
+ BoxCloneSyncService::new(service),
215
+ Unnameable as fn(ConnectionDescriptor) -> Unnameable,
216
+ );
231
217
 
232
- #[inline]
233
- fn call(&mut self, descriptor: ConnectionDescriptor) -> Self::Future {
234
- match self {
235
- Connector::Simple(service) => service.call(descriptor),
236
- Connector::WithLayers(service) => service.call(Unnameable(descriptor)),
237
- }
218
+ Ok(Either::Right(service))
238
219
  }
239
220
  }
240
221
 
241
- // ===== impl ConnectorService =====
222
+ // ===== impl TransportConnector =====
242
223
 
243
- impl ConnectorService {
224
+ impl TransportConnector {
244
225
  fn build_https_connector(
245
226
  &self,
246
227
  https: bool,
@@ -503,41 +484,30 @@ impl ConnectorService {
503
484
  async fn connect_auto(self, req: ConnectionDescriptor) -> Result<Conn, BoxError> {
504
485
  debug!("starting new connection: {:?}", req.uri());
505
486
 
506
- let timeout = self.config.timeout;
507
-
508
487
  // Determine if a proxy should be used for this request.
509
- let fut = async {
510
- let intercepted = req
511
- .proxy()
512
- .and_then(|prox| prox.intercept(req.uri()))
513
- .or_else(|| {
514
- self.config
515
- .proxies
516
- .iter()
517
- .find_map(|prox| prox.intercept(req.uri()))
518
- });
519
-
520
- // If a proxy is matched, connect via proxy; otherwise, connect directly.
521
- if let Some(intercepted) = intercepted {
522
- self.connect_via_proxy(req, intercepted).await
523
- } else {
524
- self.connect_auto_proxy(req, None).await
525
- }
526
- };
527
-
528
- // Apply timeout if configured.
529
- if let Some(to) = timeout {
530
- tokio::time::timeout(to, fut).await.map_err(|_| TimedOut)?
488
+ let intercepted = req
489
+ .proxy()
490
+ .and_then(|prox| prox.intercept(req.uri()))
491
+ .or_else(|| {
492
+ self.config
493
+ .proxies
494
+ .iter()
495
+ .find_map(|prox| prox.intercept(req.uri()))
496
+ });
497
+
498
+ // If a proxy is matched, connect via proxy; otherwise, connect directly.
499
+ if let Some(intercepted) = intercepted {
500
+ self.connect_via_proxy(req, intercepted).await
531
501
  } else {
532
- fut.await
502
+ self.connect_auto_proxy(req, None).await
533
503
  }
534
504
  }
535
505
  }
536
506
 
537
- impl Service<ConnectionDescriptor> for ConnectorService {
507
+ impl Service<ConnectionDescriptor> for TransportConnector {
538
508
  type Response = Conn;
539
509
  type Error = BoxError;
540
- type Future = Connecting;
510
+ type Future = BoxFuture<'static, Result<Conn, BoxError>>;
541
511
 
542
512
  #[inline]
543
513
  fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
@@ -20,7 +20,7 @@ use super::{
20
20
  tcp::{ConnectError, ConnectingTcp, TcpConnector, TcpKeepaliveOptions, TcpOptions},
21
21
  },
22
22
  };
23
- use crate::dns::{self, InternalResolve};
23
+ use crate::dns::{self, DnsResolver};
24
24
 
25
25
  static INVALID_NOT_HTTP: &str = "invalid URI, scheme is not http";
26
26
  static INVALID_MISSING_SCHEME: &str = "invalid URI, scheme is missing";
@@ -56,16 +56,19 @@ where
56
56
  /// Set that all socket have `SO_REUSEADDR` set to the supplied value `reuse_address`.
57
57
  fn set_reuse_address(&mut self, reuse: bool);
58
58
 
59
+ /// Sets the value of the `SO_LINGER` option on the socket.
60
+ fn set_linger(&mut self, linger: Option<Duration>);
61
+
59
62
  /// Sets the value of the `TCP_USER_TIMEOUT` option on the socket.
60
63
  #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
61
64
  fn set_tcp_user_timeout(&mut self, time: Option<Duration>);
62
65
 
63
- /// Set the connect timeout.
66
+ /// Set the timeout for the complete TCP connection race.
64
67
  fn set_connect_timeout(&mut self, dur: Option<Duration>);
65
68
 
66
- /// Set timeout for [RFC 6555 (Happy Eyeballs)][RFC 6555] algorithm.
69
+ /// Set the delay between connection attempts for [RFC 8305 (Happy Eyeballs v2)][RFC 8305].
67
70
  ///
68
- /// [RFC 6555]: https://tools.ietf.org/html/rfc6555
71
+ /// [RFC 8305]: https://www.rfc-editor.org/rfc/rfc8305.html#section-5
69
72
  fn set_happy_eyeballs_timeout(&mut self, dur: Option<Duration>);
70
73
 
71
74
  /// Set that all sockets have `SO_KEEPALIVE` set with the supplied duration
@@ -156,6 +159,7 @@ impl<R, S> HttpConnector<R, S> {
156
159
  happy_eyeballs_timeout: Some(Duration::from_millis(300)),
157
160
  nodelay: false,
158
161
  reuse_address: false,
162
+ linger: None,
159
163
  send_buffer_size: None,
160
164
  recv_buffer_size: None,
161
165
  #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
@@ -178,7 +182,7 @@ impl<R, S> HttpConnector<R, S> {
178
182
 
179
183
  impl<R, S> HttpConnect for HttpConnector<R, S>
180
184
  where
181
- R: InternalResolve + Clone + Send + Sync + 'static,
185
+ R: DnsResolver + Clone + Send + Sync + 'static,
182
186
  R::Future: Send,
183
187
  S: TcpConnector,
184
188
  {
@@ -218,6 +222,14 @@ where
218
222
  self.config_mut().reuse_address = reuse_address;
219
223
  }
220
224
 
225
+ /// Sets the value of the SO_LINGER option on the socket.
226
+ ///
227
+ /// Default is `None`, which leaves the option untouched.
228
+ #[inline]
229
+ fn set_linger(&mut self, linger: Option<Duration>) {
230
+ self.config_mut().linger = linger;
231
+ }
232
+
221
233
  /// Sets the value of the TCP_USER_TIMEOUT option on the socket.
222
234
  #[inline]
223
235
  #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
@@ -225,10 +237,7 @@ where
225
237
  self.config_mut().tcp_user_timeout = time;
226
238
  }
227
239
 
228
- /// Set the connect timeout.
229
- ///
230
- /// If a domain resolves to multiple IP addresses, the timeout will be
231
- /// evenly divided across them.
240
+ /// Set the timeout for the complete TCP connection race.
232
241
  ///
233
242
  /// Default is `None`.
234
243
  #[inline]
@@ -236,18 +245,16 @@ where
236
245
  self.config_mut().connect_timeout = dur;
237
246
  }
238
247
 
239
- /// Set timeout for [RFC 6555 (Happy Eyeballs)][RFC 6555] algorithm.
248
+ /// Set the delay between connection attempts for [RFC 8305 (Happy Eyeballs v2)][RFC 8305].
240
249
  ///
241
- /// If hostname resolves to both IPv4 and IPv6 addresses and connection
242
- /// cannot be established using preferred address family before timeout
243
- /// elapses, then connector will in parallel attempt connection using other
244
- /// address family.
250
+ /// The first address is attempted immediately. Later addresses are started
251
+ /// after this delay without waiting for the previous attempt to finish.
245
252
  ///
246
253
  /// If `None`, parallel connection attempts are disabled.
247
254
  ///
248
255
  /// Default is 300 milliseconds.
249
256
  ///
250
- /// [RFC 6555]: https://tools.ietf.org/html/rfc6555
257
+ /// [RFC 8305]: https://www.rfc-editor.org/rfc/rfc8305.html#section-5
251
258
  #[inline]
252
259
  fn set_happy_eyeballs_timeout(&mut self, dur: Option<Duration>) {
253
260
  self.config_mut().happy_eyeballs_timeout = dur;
@@ -337,7 +344,7 @@ where
337
344
 
338
345
  impl<R, S> Service<Uri> for HttpConnector<R, S>
339
346
  where
340
- R: InternalResolve + Clone + Send + Sync + 'static,
347
+ R: DnsResolver + Clone + Send + Sync + 'static,
341
348
  R::Future: Send,
342
349
  S: TcpConnector,
343
350
  S::TcpStream: From<socket2::Socket>,
@@ -358,7 +365,7 @@ where
358
365
  let options = &this.options;
359
366
 
360
367
  let (host, port) = get_host_port(options, &dst)?;
361
- let host = host.trim_start_matches('[').trim_end_matches(']');
368
+ let host = crate::util::strip_ipv6_brackets(host);
362
369
 
363
370
  let addrs = if let Some(addrs) = dns::SocketAddrs::try_parse(host, port) {
364
371
  addrs
@@ -472,7 +479,7 @@ pin_project! {
472
479
 
473
480
  impl<R, S> Future for HttpConnecting<R, S>
474
481
  where
475
- R: InternalResolve,
482
+ R: DnsResolver,
476
483
  S: TcpConnector,
477
484
  {
478
485
  type Output = ConnectResult<S>;