wreq-rb 0.4.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 +47 -16
  5. data/exe/wreq +211 -0
  6. data/ext/wreq_rb/Cargo.toml +4 -6
  7. data/ext/wreq_rb/src/client.rs +145 -41
  8. data/lib/wreq-rb/version.rb +1 -1
  9. data/patches/0001-add-transfer-size-tracking.patch +76 -67
  10. data/vendor/wreq/Cargo.toml +119 -71
  11. data/vendor/wreq/README.md +25 -20
  12. data/vendor/wreq/bench/http1.rs +25 -0
  13. data/vendor/wreq/bench/http1_over_tls.rs +25 -0
  14. data/vendor/wreq/bench/http2.rs +25 -0
  15. data/vendor/wreq/bench/http2_over_tls.rs +25 -0
  16. data/vendor/wreq/bench/support/bench.rs +91 -0
  17. data/vendor/wreq/bench/support/client.rs +217 -0
  18. data/vendor/wreq/bench/support/server.rs +188 -0
  19. data/vendor/wreq/bench/support.rs +56 -0
  20. data/vendor/wreq/examples/cert_store.rs +4 -4
  21. data/vendor/wreq/examples/{emulation.rs → emulate.rs} +2 -2
  22. data/vendor/wreq/examples/http2_websocket.rs +2 -2
  23. data/vendor/wreq/examples/keylog.rs +3 -3
  24. data/vendor/wreq/examples/{request_with_emulation.rs → request_with_emulate.rs} +2 -2
  25. data/vendor/wreq/examples/rt.rs +23 -0
  26. data/vendor/wreq/src/client/body.rs +23 -61
  27. data/vendor/wreq/src/client/emulate.rs +119 -0
  28. data/vendor/wreq/src/client/{http/future.rs → future.rs} +11 -32
  29. data/vendor/wreq/src/client/{http → layer}/client/pool.rs +66 -61
  30. data/vendor/wreq/src/client/{http → layer}/client.rs +416 -270
  31. data/vendor/wreq/src/client/layer/config.rs +27 -6
  32. data/vendor/wreq/src/client/layer/decoder.rs +9 -4
  33. data/vendor/wreq/src/client/layer/redirect/future.rs +6 -3
  34. data/vendor/wreq/src/client/layer/redirect.rs +4 -5
  35. data/vendor/wreq/src/client/layer/retry.rs +8 -5
  36. data/vendor/wreq/src/client/layer/timeout/body.rs +15 -6
  37. data/vendor/wreq/src/client/layer/timeout/future.rs +23 -18
  38. data/vendor/wreq/src/client/layer/timeout.rs +24 -74
  39. data/vendor/wreq/src/client/layer.rs +1 -2
  40. data/vendor/wreq/src/client/multipart.rs +137 -154
  41. data/vendor/wreq/src/client/request.rs +202 -118
  42. data/vendor/wreq/src/client/response.rs +46 -45
  43. data/vendor/wreq/src/client/upgrade.rs +15 -0
  44. data/vendor/wreq/src/client/ws.rs +73 -25
  45. data/vendor/wreq/src/client.rs +1655 -17
  46. data/vendor/wreq/src/config.rs +11 -11
  47. data/vendor/wreq/src/{client/conn → conn}/connector.rs +139 -137
  48. data/vendor/wreq/src/conn/descriptor.rs +143 -0
  49. data/vendor/wreq/src/conn/http.rs +484 -0
  50. data/vendor/wreq/src/conn/net/io.rs +75 -0
  51. data/vendor/wreq/src/conn/net/tcp/compio.rs +71 -0
  52. data/vendor/wreq/src/conn/net/tcp/tokio.rs +57 -0
  53. data/vendor/wreq/src/conn/net/tcp.rs +561 -0
  54. data/vendor/wreq/src/conn/net/uds/compio.rs +60 -0
  55. data/vendor/wreq/src/{client/conn/uds.rs → conn/net/uds/tokio.rs} +18 -12
  56. data/vendor/wreq/src/conn/net/uds.rs +11 -0
  57. data/vendor/wreq/src/conn/net.rs +130 -0
  58. data/vendor/wreq/src/{client/conn → conn}/proxy/socks.rs +2 -9
  59. data/vendor/wreq/src/{client/conn → conn}/proxy/tunnel.rs +21 -56
  60. data/vendor/wreq/src/conn/tls_info.rs +47 -0
  61. data/vendor/wreq/src/{client/conn.rs → conn.rs} +202 -54
  62. data/vendor/wreq/src/cookie.rs +302 -142
  63. data/vendor/wreq/src/dns/gai/compio.rs +77 -0
  64. data/vendor/wreq/src/dns/gai/tokio.rs +90 -0
  65. data/vendor/wreq/src/dns/gai.rs +14 -164
  66. data/vendor/wreq/src/dns/hickory.rs +16 -23
  67. data/vendor/wreq/src/dns/resolve.rs +7 -41
  68. data/vendor/wreq/src/dns.rs +90 -7
  69. data/vendor/wreq/src/error.rs +57 -31
  70. data/vendor/wreq/src/ext.rs +25 -0
  71. data/vendor/wreq/src/group.rs +211 -0
  72. data/vendor/wreq/src/header.rs +100 -112
  73. data/vendor/wreq/src/lib.rs +124 -73
  74. data/vendor/wreq/src/proxy.rs +6 -20
  75. data/vendor/wreq/src/redirect.rs +1 -1
  76. data/vendor/wreq/src/rt.rs +208 -0
  77. data/vendor/wreq/src/sync.rs +97 -98
  78. data/vendor/wreq/src/tls/compress.rs +124 -0
  79. data/vendor/wreq/src/tls/conn/ext.rs +54 -45
  80. data/vendor/wreq/src/tls/conn/service.rs +14 -18
  81. data/vendor/wreq/src/tls/conn.rs +169 -241
  82. data/vendor/wreq/src/tls/keylog.rs +68 -5
  83. data/vendor/wreq/src/tls/session.rs +205 -0
  84. data/vendor/wreq/src/tls/{x509 → trust}/identity.rs +4 -21
  85. data/vendor/wreq/src/tls/{x509/parser.rs → trust/parse.rs} +1 -1
  86. data/vendor/wreq/src/tls/{x509 → trust}/store.rs +42 -81
  87. data/vendor/wreq/src/tls/{x509.rs → trust.rs} +8 -2
  88. data/vendor/wreq/src/tls.rs +489 -25
  89. data/vendor/wreq/src/trace.rs +0 -12
  90. data/vendor/wreq/src/util.rs +1 -1
  91. data/vendor/wreq/tests/badssl.rs +10 -10
  92. data/vendor/wreq/tests/client.rs +3 -9
  93. data/vendor/wreq/tests/cookie.rs +6 -8
  94. data/vendor/wreq/tests/{emulation.rs → emulate.rs} +130 -22
  95. data/vendor/wreq/tests/multipart.rs +43 -1
  96. data/vendor/wreq/tests/proxy.rs +1 -1
  97. data/vendor/wreq/tests/support/layer.rs +1 -0
  98. metadata +53 -72
  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
@@ -5,16 +5,14 @@ use std::{
5
5
 
6
6
  use bytes::Bytes;
7
7
  use http_body::{Body as HttpBody, SizeHint};
8
- use http_body_util::{BodyExt, Either, combinators::BoxBody};
8
+ use http_body_util::{BodyExt, Either, Full, combinators::BoxBody};
9
9
  use pin_project_lite::pin_project;
10
- #[cfg(feature = "stream")]
11
- use {tokio::fs::File, tokio_util::io::ReaderStream};
12
10
 
13
11
  use crate::error::{BoxError, Error};
14
12
 
15
13
  /// An request body.
16
14
  #[derive(Debug)]
17
- pub struct Body(Either<Bytes, BoxBody<Bytes, BoxError>>);
15
+ pub struct Body(Either<Full<Bytes>, BoxBody<Bytes, BoxError>>);
18
16
 
19
17
  pin_project! {
20
18
  /// We can't use `map_frame()` because that loses the hint data (for good reason).
@@ -28,16 +26,6 @@ pin_project! {
28
26
  // ===== impl Body =====
29
27
 
30
28
  impl Body {
31
- /// Returns a reference to the internal data of the `Body`.
32
- ///
33
- /// `None` is returned, if the underlying data is a stream.
34
- pub fn as_bytes(&self) -> Option<&[u8]> {
35
- match &self.0 {
36
- Either::Left(bytes) => Some(bytes.as_ref()),
37
- Either::Right(..) => None,
38
- }
39
- }
40
-
41
29
  /// Wrap a [`HttpBody`] in a box inside `Body`.
42
30
  ///
43
31
  /// # Example
@@ -120,20 +108,19 @@ impl Body {
120
108
 
121
109
  #[inline]
122
110
  pub(crate) fn reusable(chunk: Bytes) -> Body {
123
- Body(Either::Left(chunk))
111
+ Body(Either::Left(Full::new(chunk)))
124
112
  }
125
113
 
114
+ #[inline]
126
115
  #[cfg(feature = "multipart")]
127
116
  pub(crate) fn content_length(&self) -> Option<u64> {
128
- match self.0 {
129
- Either::Left(ref bytes) => Some(bytes.len() as u64),
130
- Either::Right(ref body) => body.size_hint().exact(),
131
- }
117
+ self.0.size_hint().exact()
132
118
  }
133
119
 
120
+ #[inline]
134
121
  pub(crate) fn try_clone(&self) -> Option<Body> {
135
122
  match self.0 {
136
- Either::Left(ref chunk) => Some(Body::reusable(chunk.clone())),
123
+ Either::Left(ref chunk) => Some(Body(Either::Left(chunk.clone()))),
137
124
  Either::Right { .. } => None,
138
125
  }
139
126
  }
@@ -188,11 +175,11 @@ impl From<&'static str> for Body {
188
175
  }
189
176
  }
190
177
 
191
- #[cfg(feature = "stream")]
192
- impl From<File> for Body {
178
+ #[cfg(all(feature = "tokio-rt", feature = "stream"))]
179
+ impl From<tokio::fs::File> for Body {
193
180
  #[inline]
194
- fn from(file: File) -> Body {
195
- Body::wrap_stream(ReaderStream::new(file))
181
+ fn from(file: tokio::fs::File) -> Body {
182
+ Body::wrap_stream(tokio_util::io::ReaderStream::new(file))
196
183
  }
197
184
  }
198
185
 
@@ -200,44 +187,25 @@ impl HttpBody for Body {
200
187
  type Data = Bytes;
201
188
  type Error = Error;
202
189
 
190
+ #[inline(always)]
203
191
  fn poll_frame(
204
192
  mut self: Pin<&mut Self>,
205
193
  cx: &mut Context,
206
194
  ) -> Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
207
- match self.0 {
208
- Either::Left(ref mut bytes) => {
209
- let out = bytes.split_off(0);
210
- if out.is_empty() {
211
- Poll::Ready(None)
212
- } else {
213
- Poll::Ready(Some(Ok(http_body::Frame::data(out))))
214
- }
215
- }
216
- Either::Right(ref mut body) => {
217
- Poll::Ready(ready!(Pin::new(body).poll_frame(cx)).map(|opt_chunk| {
218
- opt_chunk.map_err(|err| match err.downcast::<Error>() {
219
- Ok(err) => *err,
220
- Err(err) => Error::body(err),
221
- })
222
- }))
223
- }
224
- }
195
+ Pin::new(&mut self.0).poll_frame(cx).map_err(|err| {
196
+ err.downcast::<Error>()
197
+ .map_or_else(Error::request, |err| *err)
198
+ })
225
199
  }
226
200
 
227
- #[inline]
201
+ #[inline(always)]
228
202
  fn size_hint(&self) -> SizeHint {
229
- match self.0 {
230
- Either::Left(ref bytes) => SizeHint::with_exact(bytes.len() as u64),
231
- Either::Right(ref body) => body.size_hint(),
232
- }
203
+ self.0.size_hint()
233
204
  }
234
205
 
235
- #[inline]
206
+ #[inline(always)]
236
207
  fn is_end_stream(&self) -> bool {
237
- match self.0 {
238
- Either::Left(ref bytes) => bytes.is_empty(),
239
- Either::Right(ref body) => body.is_end_stream(),
240
- }
208
+ self.0.is_end_stream()
241
209
  }
242
210
  }
243
211
 
@@ -251,6 +219,7 @@ where
251
219
  type Data = Bytes;
252
220
  type Error = B::Error;
253
221
 
222
+ #[inline(always)]
254
223
  fn poll_frame(
255
224
  self: Pin<&mut Self>,
256
225
  cx: &mut Context,
@@ -262,12 +231,12 @@ where
262
231
  }
263
232
  }
264
233
 
265
- #[inline]
234
+ #[inline(always)]
266
235
  fn size_hint(&self) -> SizeHint {
267
236
  self.inner.size_hint()
268
237
  }
269
238
 
270
- #[inline]
239
+ #[inline(always)]
271
240
  fn is_end_stream(&self) -> bool {
272
241
  self.inner.is_end_stream()
273
242
  }
@@ -279,13 +248,6 @@ mod tests {
279
248
 
280
249
  use super::Body;
281
250
 
282
- #[test]
283
- fn test_as_bytes() {
284
- let test_data = b"Test body";
285
- let body = Body::from(&test_data[..]);
286
- assert_eq!(body.as_bytes(), Some(&test_data[..]));
287
- }
288
-
289
251
  #[test]
290
252
  fn body_exact_length() {
291
253
  let empty_body = Body::empty();
@@ -0,0 +1,119 @@
1
+ use http::HeaderMap;
2
+ use wreq_proto::{http1::Http1Options, http2::Http2Options};
3
+
4
+ use crate::{group::Group, header::OrigHeaderMap, tls::TlsOptions};
5
+
6
+ /// Converts a value into an [`Emulation`] configuration.
7
+ ///
8
+ /// This trait lets multiple input types provide a unified way to produce
9
+ /// an emulation profile. Typical inputs include:
10
+ /// - Predefined browser profiles
11
+ /// - Transport option sets (e.g. HTTP/1, HTTP/2, TLS)
12
+ /// - User-defined strategy types
13
+ pub trait IntoEmulation {
14
+ /// Converts `self` into an [`Emulation`] configuration.
15
+ fn into_emulation(self) -> Emulation;
16
+ }
17
+
18
+ /// Builder for creating an [`Emulation`] configuration.
19
+ #[must_use]
20
+ #[derive(Debug)]
21
+ pub struct EmulationBuilder {
22
+ emulation: Emulation,
23
+ }
24
+
25
+ /// HTTP emulation settings for a client profile.
26
+ ///
27
+ /// Combines protocol options (HTTP/1, HTTP/2, TLS) and default headers.
28
+ #[non_exhaustive]
29
+ #[derive(Debug, Clone)]
30
+ pub struct Emulation {
31
+ pub(crate) group: Group,
32
+
33
+ /// Default headers applied to outgoing requests.
34
+ pub headers: HeaderMap,
35
+
36
+ /// Original headers with preserved case and duplicates.
37
+ pub orig_headers: OrigHeaderMap,
38
+
39
+ /// TLS configuration.
40
+ pub tls_options: Option<TlsOptions>,
41
+
42
+ /// HTTP/1 configuration.
43
+ pub http1_options: Option<Http1Options>,
44
+
45
+ /// HTTP/2 configuration.
46
+ pub http2_options: Option<Http2Options>,
47
+ }
48
+
49
+ // ==== impl EmulationBuilder ====
50
+
51
+ impl EmulationBuilder {
52
+ /// Sets the HTTP/1 options configuration.
53
+ #[inline]
54
+ pub fn http1_options(mut self, opts: Http1Options) -> Self {
55
+ self.emulation.http1_options = Some(opts);
56
+ self
57
+ }
58
+
59
+ /// Sets the HTTP/2 options configuration.
60
+ #[inline]
61
+ pub fn http2_options(mut self, opts: Http2Options) -> Self {
62
+ self.emulation.http2_options = Some(opts);
63
+ self
64
+ }
65
+
66
+ /// Sets the TLS options configuration.
67
+ #[inline]
68
+ pub fn tls_options(mut self, opts: TlsOptions) -> Self {
69
+ self.emulation.tls_options = Some(opts);
70
+ self
71
+ }
72
+
73
+ /// Sets the default headers.
74
+ #[inline]
75
+ pub fn headers(mut self, src: HeaderMap) -> Self {
76
+ crate::util::replace_headers(&mut self.emulation.headers, src);
77
+ self
78
+ }
79
+
80
+ /// Sets the original headers.
81
+ #[inline]
82
+ pub fn orig_headers(mut self, src: OrigHeaderMap) -> Self {
83
+ self.emulation.orig_headers.extend(src);
84
+ self
85
+ }
86
+
87
+ /// Builds the [`Emulation`] instance.
88
+ #[inline]
89
+ pub fn build(mut self, group: Group) -> Emulation {
90
+ self.emulation.group.emulate(group);
91
+ self.emulation
92
+ }
93
+ }
94
+
95
+ // ==== impl Emulation ====
96
+
97
+ impl Emulation {
98
+ /// Creates a new [`EmulationBuilder`].
99
+ #[inline]
100
+ pub fn builder() -> EmulationBuilder {
101
+ EmulationBuilder {
102
+ emulation: Emulation {
103
+ group: Group::default(),
104
+ headers: HeaderMap::new(),
105
+ orig_headers: OrigHeaderMap::new(),
106
+ tls_options: None,
107
+ http1_options: None,
108
+ http2_options: None,
109
+ },
110
+ }
111
+ }
112
+ }
113
+
114
+ impl<T: Into<Emulation>> IntoEmulation for T {
115
+ #[inline]
116
+ fn into_emulation(self) -> Emulation {
117
+ self.into()
118
+ }
119
+ }
@@ -5,12 +5,9 @@ use std::{
5
5
 
6
6
  use http::{Request, Uri};
7
7
  use pin_project_lite::pin_project;
8
- use tower::util::Oneshot;
8
+ use tower::util::{Either, Oneshot};
9
9
 
10
- use super::{Body, ClientRef, Response};
11
- use crate::{Error, ext::RequestUri};
12
-
13
- type ResponseFuture = Oneshot<ClientRef, Request<Body>>;
10
+ use super::{Body, BoxedClientService, ClientService, Error, Response};
14
11
 
15
12
  pin_project! {
16
13
  /// [`Pending`] is a future representing the state of an HTTP request, which may be either
@@ -19,8 +16,8 @@ pin_project! {
19
16
  #[project = PendingProj]
20
17
  pub enum Pending {
21
18
  Request {
22
- uri: Uri,
23
- fut: Pin<Box<ResponseFuture>>,
19
+ uri: Option<Uri>,
20
+ fut: Pin<Box<Oneshot<Either<ClientService, BoxedClientService>, Request<Body>>>>,
24
21
  },
25
22
  Error {
26
23
  error: Option<Error>,
@@ -28,23 +25,6 @@ pin_project! {
28
25
  }
29
26
  }
30
27
 
31
- impl Pending {
32
- /// Creates a new [`Pending`] with a request future and its associated URI.
33
- #[inline]
34
- pub(crate) fn request(uri: Uri, fut: ResponseFuture) -> Self {
35
- Pending::Request {
36
- uri,
37
- fut: Box::pin(fut),
38
- }
39
- }
40
-
41
- /// Creates a new [`Pending`] with an error.
42
- #[inline]
43
- pub(crate) fn error(error: Error) -> Self {
44
- Pending::Error { error: Some(error) }
45
- }
46
- }
47
-
48
28
  impl Future for Pending {
49
29
  type Output = Result<Response, Error>;
50
30
 
@@ -59,19 +39,18 @@ impl Future for Pending {
59
39
  }
60
40
  };
61
41
 
62
- let res = match ready!(res) {
63
- Ok(mut res) => {
64
- if let Some(redirect_uri) = res.extensions_mut().remove::<RequestUri>() {
65
- *uri = redirect_uri.0;
66
- }
67
- Ok(Response::new(res, uri.clone()))
68
- }
42
+ let res = ready!(res);
43
+ let uri = uri
44
+ .take()
45
+ .expect("Pending::Request polled after completion");
46
+ let res = match res {
47
+ Ok(res) => Ok(Response::new(res, uri)),
69
48
  Err(err) => {
70
49
  let mut err = err
71
50
  .downcast::<Error>()
72
51
  .map_or_else(Error::request, |err| *err);
73
52
  if err.uri().is_none() {
74
- err = err.with_uri(uri.clone());
53
+ err = err.with_uri(uri);
75
54
  }
76
55
  Err(err)
77
56
  }
@@ -1,11 +1,11 @@
1
1
  use std::{
2
- collections::VecDeque,
2
+ collections::{HashMap, HashSet, VecDeque},
3
3
  convert::Infallible,
4
4
  error::Error as StdError,
5
5
  fmt::{self, Debug},
6
6
  future::Future,
7
7
  hash::Hash,
8
- num::NonZero,
8
+ num::NonZeroUsize,
9
9
  ops::{Deref, DerefMut},
10
10
  pin::Pin,
11
11
  sync::{Arc, Weak},
@@ -13,13 +13,12 @@ use std::{
13
13
  time::{Duration, Instant},
14
14
  };
15
15
 
16
- use schnellru::ByLength;
16
+ use lru::LruCache;
17
17
  use tokio::sync::oneshot;
18
+ use wreq_proto::rt::{Executor as _, Timer as _};
18
19
 
19
- use super::exec::{self, Exec};
20
20
  use crate::{
21
- client::core::rt::{ArcTimer, Executor, Timer},
22
- hash::{HASHER, HashMap, HashSet, LruMap},
21
+ rt::{Executor, Timer},
23
22
  sync::Mutex,
24
23
  };
25
24
 
@@ -79,7 +78,7 @@ struct PoolInner<T, K: Eq + Hash> {
79
78
  connecting: HashSet<K>,
80
79
  // These are internal Conns sitting in the event loop in the KeepAlive
81
80
  // state, waiting to receive a new Request to send on the socket.
82
- idle: LruMap<K, Vec<Idle<T>>>,
81
+ idle: LruCache<K, Vec<Idle<T>>>,
83
82
  max_idle_per_host: usize,
84
83
  // These are outstanding Checkouts that are waiting for a socket to be
85
84
  // able to send a Request one. This is used when "racing" for a new
@@ -94,8 +93,8 @@ struct PoolInner<T, K: Eq + Hash> {
94
93
  // A oneshot channel is used to allow the interval to be notified when
95
94
  // the Pool completely drops. That way, the interval can cancel immediately.
96
95
  idle_interval_ref: Option<oneshot::Sender<Infallible>>,
97
- exec: Exec,
98
- timer: Option<ArcTimer>,
96
+ exec: Executor,
97
+ timer: Timer,
99
98
  timeout: Option<Duration>,
100
99
  }
101
100
 
@@ -107,7 +106,7 @@ struct WeakOpt<T>(Option<Weak<T>>);
107
106
  pub struct Config {
108
107
  pub idle_timeout: Option<Duration>,
109
108
  pub max_idle_per_host: usize,
110
- pub max_pool_size: Option<NonZero<u32>>,
109
+ pub max_pool_size: Option<NonZeroUsize>,
111
110
  }
112
111
 
113
112
  impl Config {
@@ -117,23 +116,18 @@ impl Config {
117
116
  }
118
117
 
119
118
  impl<T, K: Key> Pool<T, K> {
120
- pub fn new<E, M>(config: Config, executor: E, timer: Option<M>) -> Pool<T, K>
121
- where
122
- E: Executor<exec::BoxSendFuture> + Send + Sync + Clone + 'static,
123
- M: Timer + Send + Sync + Clone + 'static,
124
- {
119
+ pub fn new(config: Config, exec: Executor, timer: Timer) -> Pool<T, K> {
125
120
  let inner = if config.is_enabled() {
126
121
  Some(Arc::new(Mutex::new(PoolInner {
127
- connecting: HashSet::with_hasher(HASHER),
128
- idle: LruMap::with_hasher(
129
- ByLength::new(config.max_pool_size.map_or(u32::MAX, NonZero::get)),
130
- HASHER,
131
- ),
122
+ connecting: HashSet::default(),
123
+ idle: config
124
+ .max_pool_size
125
+ .map_or_else(LruCache::unbounded, LruCache::new),
132
126
  idle_interval_ref: None,
133
127
  max_idle_per_host: config.max_idle_per_host,
134
- waiters: HashMap::with_hasher(HASHER),
135
- exec: Exec::new(executor),
136
- timer: timer.map(ArcTimer::new),
128
+ waiters: HashMap::default(),
129
+ exec,
130
+ timer,
137
131
  timeout: config.idle_timeout,
138
132
  })))
139
133
  } else {
@@ -262,7 +256,7 @@ struct IdlePopper<'a, T, K> {
262
256
  }
263
257
 
264
258
  impl<'a, T: Poolable + 'a, K: Debug> IdlePopper<'a, T, K> {
265
- fn pop(self, expiration: &Expiration) -> Option<Idle<T>> {
259
+ fn pop(self, expiration: &Expiration, now: Instant) -> Option<Idle<T>> {
266
260
  while let Some(entry) = self.list.pop() {
267
261
  // If the connection has been closed, or is older than our idle
268
262
  // timeout, simply drop it and keep looking...
@@ -276,7 +270,7 @@ impl<'a, T: Poolable + 'a, K: Debug> IdlePopper<'a, T, K> {
276
270
  //
277
271
  // In that case, we could just break out of the loop and drop the
278
272
  // whole list...
279
- if expiration.expires(entry.idle_at) {
273
+ if expiration.expires(entry.idle_at, now) {
280
274
  trace!("removing expired connection for {:?}", self.key);
281
275
  continue;
282
276
  }
@@ -284,7 +278,7 @@ impl<'a, T: Poolable + 'a, K: Debug> IdlePopper<'a, T, K> {
284
278
  let value = match entry.value.reserve() {
285
279
  Reservation::Shared(to_reinsert, to_checkout) => {
286
280
  self.list.push(Idle {
287
- idle_at: Instant::now(),
281
+ idle_at: now,
288
282
  value: to_reinsert,
289
283
  });
290
284
  to_checkout
@@ -303,6 +297,10 @@ impl<'a, T: Poolable + 'a, K: Debug> IdlePopper<'a, T, K> {
303
297
  }
304
298
 
305
299
  impl<T: Poolable, K: Key> PoolInner<T, K> {
300
+ fn now(&self) -> Instant {
301
+ self.timer.now()
302
+ }
303
+
306
304
  fn put(&mut self, key: &K, value: T, __pool_ref: &Arc<Mutex<PoolInner<T, K>>>) {
307
305
  if value.can_share() && self.idle.peek(key).is_some() {
308
306
  trace!("put; existing idle HTTP/2 connection for {:?}", key);
@@ -348,22 +346,21 @@ impl<T: Poolable, K: Key> PoolInner<T, K> {
348
346
  if let Some(value) = value {
349
347
  // borrow-check scope...
350
348
  {
349
+ let now = self.now();
351
350
  let idle_list = self
352
351
  .idle
353
- .get_or_insert(key.clone(), Vec::<Idle<T>>::default);
352
+ .get_or_insert_mut(key.clone(), Vec::<Idle<T>>::default);
354
353
 
355
- if let Some(idle_list) = idle_list {
356
- if self.max_idle_per_host <= idle_list.len() {
357
- trace!("max idle per host for {:?}, dropping connection", key);
358
- return;
359
- }
360
-
361
- debug!("pooling idle connection for {:?}", key);
362
- idle_list.push(Idle {
363
- value,
364
- idle_at: Instant::now(),
365
- });
354
+ if self.max_idle_per_host <= idle_list.len() {
355
+ trace!("max idle per host for {:?}, dropping connection", key);
356
+ return;
366
357
  }
358
+
359
+ debug!("pooling idle connection for {:?}", key);
360
+ idle_list.push(Idle {
361
+ value,
362
+ idle_at: now,
363
+ });
367
364
  }
368
365
 
369
366
  self.spawn_idle_interval(__pool_ref);
@@ -398,11 +395,9 @@ impl<T: Poolable, K: Key> PoolInner<T, K> {
398
395
  return;
399
396
  }
400
397
 
401
- let timer = if let Some(timer) = self.timer.clone() {
402
- timer
403
- } else {
398
+ if self.timer.is_empty() {
404
399
  return;
405
- };
400
+ }
406
401
 
407
402
  // While someone might want a shorter duration, and it will be respected
408
403
  // at checkout time, there's no need to wake up and proactively evict
@@ -421,7 +416,7 @@ impl<T: Poolable, K: Key> PoolInner<T, K> {
421
416
  self.idle_interval_ref = Some(tx);
422
417
 
423
418
  let interval = IdleTask {
424
- timer: timer.clone(),
419
+ timer: self.timer.clone(),
425
420
  duration: dur,
426
421
  pool: WeakOpt::downgrade(pool_ref),
427
422
  pool_drop_notifier: rx,
@@ -452,7 +447,7 @@ impl<T: Poolable, K: Key> PoolInner<T, K> {
452
447
  /// This should *only* be called by the IdleTask
453
448
  fn clear_expired(&mut self) {
454
449
  let dur = self.timeout.expect("interval assumes timeout");
455
- let now = Instant::now();
450
+ let now = self.now();
456
451
 
457
452
  let mut keys_to_remove = Vec::new();
458
453
  for (key, values) in self.idle.iter_mut() {
@@ -480,7 +475,7 @@ impl<T: Poolable, K: Key> PoolInner<T, K> {
480
475
 
481
476
  for key in keys_to_remove {
482
477
  trace!("idle interval removing empty key {:?}", key);
483
- self.idle.remove(&key);
478
+ self.idle.pop(&key);
484
479
  }
485
480
  }
486
481
  }
@@ -628,7 +623,8 @@ impl<T: Poolable, K: Key> Checkout<T, K> {
628
623
  let entry = {
629
624
  let mut inner = self.pool.inner.as_ref()?.lock();
630
625
  let expiration = Expiration::new(inner.timeout);
631
- let maybe_entry = inner.idle.get(&self.key).and_then(|list| {
626
+ let now = inner.now();
627
+ let maybe_entry = inner.idle.get_mut(&self.key).and_then(|list| {
632
628
  trace!("take? {:?}: expiration = {:?}", self.key, expiration.0);
633
629
  // A block to end the mutable borrow on list,
634
630
  // so the map below can check is_empty()
@@ -637,7 +633,7 @@ impl<T: Poolable, K: Key> Checkout<T, K> {
637
633
  key: &self.key,
638
634
  list,
639
635
  };
640
- popper.pop(&expiration)
636
+ popper.pop(&expiration, now)
641
637
  }
642
638
  .map(|e| (e, list.is_empty()))
643
639
  });
@@ -650,7 +646,7 @@ impl<T: Poolable, K: Key> Checkout<T, K> {
650
646
  };
651
647
 
652
648
  if empty {
653
- inner.idle.remove(&self.key);
649
+ inner.idle.pop(&self.key);
654
650
  }
655
651
 
656
652
  if entry.is_none() && self.waiter.is_none() {
@@ -738,17 +734,17 @@ impl Expiration {
738
734
  Expiration(dur)
739
735
  }
740
736
 
741
- fn expires(&self, instant: Instant) -> bool {
737
+ fn expires(&self, instant: Instant, now: Instant) -> bool {
742
738
  match self.0 {
743
739
  // Avoid `Instant::elapsed` to avoid issues like rust-lang/rust#86470.
744
- Some(timeout) => Instant::now().saturating_duration_since(instant) > timeout,
740
+ Some(timeout) => now.saturating_duration_since(instant) > timeout,
745
741
  None => false,
746
742
  }
747
743
  }
748
744
  }
749
745
 
750
746
  struct IdleTask<T, K: Key> {
751
- timer: ArcTimer,
747
+ timer: Timer,
752
748
  duration: Duration,
753
749
  pool: WeakOpt<Mutex<PoolInner<T, K>>>,
754
750
  // This allows the IdleTask to be notified as soon as the entire
@@ -761,7 +757,7 @@ impl<T: Poolable + 'static, K: Key> IdleTask<T, K> {
761
757
  async fn run(self) {
762
758
  use futures_util::future;
763
759
 
764
- let mut sleep = self.timer.sleep_until(Instant::now() + self.duration);
760
+ let mut sleep = self.timer.sleep_until(self.timer.now() + self.duration);
765
761
  let mut on_pool_drop = self.pool_drop_notifier;
766
762
  loop {
767
763
  match future::select(&mut on_pool_drop, &mut sleep).await {
@@ -777,7 +773,7 @@ impl<T: Poolable + 'static, K: Key> IdleTask<T, K> {
777
773
  drop(inner);
778
774
  }
779
775
 
780
- let deadline = Instant::now() + self.duration;
776
+ let deadline = self.timer.now() + self.duration;
781
777
  self.timer.reset(&mut sleep, deadline);
782
778
  }
783
779
  }
@@ -815,7 +811,7 @@ mod tests {
815
811
 
816
812
  use super::{Connecting, Key, Pool, Poolable, Reservation, WeakOpt};
817
813
  use crate::{
818
- client::core::rt::{ArcTimer, TokioExecutor, TokioTimer},
814
+ rt::{Executor, Timer},
819
815
  sync::MutexGuard,
820
816
  };
821
817
 
@@ -862,8 +858,8 @@ mod tests {
862
858
  max_idle_per_host: max_idle,
863
859
  max_pool_size: None,
864
860
  },
865
- TokioExecutor::new(),
866
- Option::<ArcTimer>::None,
861
+ Executor::default(),
862
+ Timer::empty(),
867
863
  )
868
864
  }
869
865
 
@@ -960,15 +956,24 @@ mod tests {
960
956
  }
961
957
 
962
958
  #[tokio::test]
963
- async fn test_pool_timer_removes_expired() {
959
+ async fn test_pool_timer_removes_expired_realtime() {
960
+ test_pool_timer_removes_expired_inner().await
961
+ }
962
+
963
+ #[tokio::test(start_paused = true)]
964
+ async fn test_pool_timer_removes_expired_faketime() {
965
+ test_pool_timer_removes_expired_inner().await
966
+ }
967
+
968
+ async fn test_pool_timer_removes_expired_inner() {
964
969
  let pool = Pool::new(
965
970
  super::Config {
966
971
  idle_timeout: Some(Duration::from_millis(10)),
967
972
  max_idle_per_host: usize::MAX,
968
973
  max_pool_size: None,
969
974
  },
970
- TokioExecutor::new(),
971
- Some(TokioTimer::new()),
975
+ Executor::default(),
976
+ Timer::default(),
972
977
  );
973
978
 
974
979
  let key = host_key("foo");
@@ -1087,8 +1092,8 @@ mod tests {
1087
1092
  max_idle_per_host: usize::MAX,
1088
1093
  max_pool_size: Some(NonZero::new(2).expect("max pool size")),
1089
1094
  },
1090
- TokioExecutor::new(),
1091
- Option::<ArcTimer>::None,
1095
+ Executor::default(),
1096
+ Timer::default(),
1092
1097
  );
1093
1098
  let key1 = host_key("foo");
1094
1099
  let key2 = host_key("bar");