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.
- checksums.yaml +4 -4
- data/Cargo.lock +1922 -397
- data/LICENSE +203 -0
- data/README.md +47 -16
- data/exe/wreq +211 -0
- data/ext/wreq_rb/Cargo.toml +4 -6
- data/ext/wreq_rb/src/client.rs +145 -41
- data/lib/wreq-rb/version.rb +1 -1
- data/patches/0001-add-transfer-size-tracking.patch +76 -67
- data/vendor/wreq/Cargo.toml +119 -71
- data/vendor/wreq/README.md +25 -20
- data/vendor/wreq/bench/http1.rs +25 -0
- data/vendor/wreq/bench/http1_over_tls.rs +25 -0
- data/vendor/wreq/bench/http2.rs +25 -0
- data/vendor/wreq/bench/http2_over_tls.rs +25 -0
- data/vendor/wreq/bench/support/bench.rs +91 -0
- data/vendor/wreq/bench/support/client.rs +217 -0
- data/vendor/wreq/bench/support/server.rs +188 -0
- data/vendor/wreq/bench/support.rs +56 -0
- data/vendor/wreq/examples/cert_store.rs +4 -4
- data/vendor/wreq/examples/{emulation.rs → emulate.rs} +2 -2
- data/vendor/wreq/examples/http2_websocket.rs +2 -2
- data/vendor/wreq/examples/keylog.rs +3 -3
- data/vendor/wreq/examples/{request_with_emulation.rs → request_with_emulate.rs} +2 -2
- data/vendor/wreq/examples/rt.rs +23 -0
- data/vendor/wreq/src/client/body.rs +23 -61
- data/vendor/wreq/src/client/emulate.rs +119 -0
- data/vendor/wreq/src/client/{http/future.rs → future.rs} +11 -32
- data/vendor/wreq/src/client/{http → layer}/client/pool.rs +66 -61
- data/vendor/wreq/src/client/{http → layer}/client.rs +416 -270
- data/vendor/wreq/src/client/layer/config.rs +27 -6
- data/vendor/wreq/src/client/layer/decoder.rs +9 -4
- data/vendor/wreq/src/client/layer/redirect/future.rs +6 -3
- data/vendor/wreq/src/client/layer/redirect.rs +4 -5
- data/vendor/wreq/src/client/layer/retry.rs +8 -5
- data/vendor/wreq/src/client/layer/timeout/body.rs +15 -6
- data/vendor/wreq/src/client/layer/timeout/future.rs +23 -18
- data/vendor/wreq/src/client/layer/timeout.rs +24 -74
- data/vendor/wreq/src/client/layer.rs +1 -2
- data/vendor/wreq/src/client/multipart.rs +137 -154
- data/vendor/wreq/src/client/request.rs +202 -118
- data/vendor/wreq/src/client/response.rs +46 -45
- data/vendor/wreq/src/client/upgrade.rs +15 -0
- data/vendor/wreq/src/client/ws.rs +73 -25
- data/vendor/wreq/src/client.rs +1655 -17
- data/vendor/wreq/src/config.rs +11 -11
- data/vendor/wreq/src/{client/conn → conn}/connector.rs +139 -137
- data/vendor/wreq/src/conn/descriptor.rs +143 -0
- data/vendor/wreq/src/conn/http.rs +484 -0
- data/vendor/wreq/src/conn/net/io.rs +75 -0
- data/vendor/wreq/src/conn/net/tcp/compio.rs +71 -0
- data/vendor/wreq/src/conn/net/tcp/tokio.rs +57 -0
- data/vendor/wreq/src/conn/net/tcp.rs +561 -0
- data/vendor/wreq/src/conn/net/uds/compio.rs +60 -0
- data/vendor/wreq/src/{client/conn/uds.rs → conn/net/uds/tokio.rs} +18 -12
- data/vendor/wreq/src/conn/net/uds.rs +11 -0
- data/vendor/wreq/src/conn/net.rs +130 -0
- data/vendor/wreq/src/{client/conn → conn}/proxy/socks.rs +2 -9
- data/vendor/wreq/src/{client/conn → conn}/proxy/tunnel.rs +21 -56
- data/vendor/wreq/src/conn/tls_info.rs +47 -0
- data/vendor/wreq/src/{client/conn.rs → conn.rs} +202 -54
- data/vendor/wreq/src/cookie.rs +302 -142
- data/vendor/wreq/src/dns/gai/compio.rs +77 -0
- data/vendor/wreq/src/dns/gai/tokio.rs +90 -0
- data/vendor/wreq/src/dns/gai.rs +14 -164
- data/vendor/wreq/src/dns/hickory.rs +16 -23
- data/vendor/wreq/src/dns/resolve.rs +7 -41
- data/vendor/wreq/src/dns.rs +90 -7
- data/vendor/wreq/src/error.rs +57 -31
- data/vendor/wreq/src/ext.rs +25 -0
- data/vendor/wreq/src/group.rs +211 -0
- data/vendor/wreq/src/header.rs +100 -112
- data/vendor/wreq/src/lib.rs +124 -73
- data/vendor/wreq/src/proxy.rs +6 -20
- data/vendor/wreq/src/redirect.rs +1 -1
- data/vendor/wreq/src/rt.rs +208 -0
- data/vendor/wreq/src/sync.rs +97 -98
- data/vendor/wreq/src/tls/compress.rs +124 -0
- data/vendor/wreq/src/tls/conn/ext.rs +54 -45
- data/vendor/wreq/src/tls/conn/service.rs +14 -18
- data/vendor/wreq/src/tls/conn.rs +169 -241
- data/vendor/wreq/src/tls/keylog.rs +68 -5
- data/vendor/wreq/src/tls/session.rs +205 -0
- data/vendor/wreq/src/tls/{x509 → trust}/identity.rs +4 -21
- data/vendor/wreq/src/tls/{x509/parser.rs → trust/parse.rs} +1 -1
- data/vendor/wreq/src/tls/{x509 → trust}/store.rs +42 -81
- data/vendor/wreq/src/tls/{x509.rs → trust.rs} +8 -2
- data/vendor/wreq/src/tls.rs +489 -25
- data/vendor/wreq/src/trace.rs +0 -12
- data/vendor/wreq/src/util.rs +1 -1
- data/vendor/wreq/tests/badssl.rs +10 -10
- data/vendor/wreq/tests/client.rs +3 -9
- data/vendor/wreq/tests/cookie.rs +6 -8
- data/vendor/wreq/tests/{emulation.rs → emulate.rs} +130 -22
- data/vendor/wreq/tests/multipart.rs +43 -1
- data/vendor/wreq/tests/proxy.rs +1 -1
- data/vendor/wreq/tests/support/layer.rs +1 -0
- metadata +53 -72
- data/vendor/wreq/src/client/conn/conn.rs +0 -231
- data/vendor/wreq/src/client/conn/http.rs +0 -1023
- data/vendor/wreq/src/client/conn/tls_info.rs +0 -98
- data/vendor/wreq/src/client/core/body/incoming.rs +0 -485
- data/vendor/wreq/src/client/core/body/length.rs +0 -118
- data/vendor/wreq/src/client/core/body.rs +0 -34
- data/vendor/wreq/src/client/core/common/buf.rs +0 -149
- data/vendor/wreq/src/client/core/common/rewind.rs +0 -141
- data/vendor/wreq/src/client/core/common/watch.rs +0 -76
- data/vendor/wreq/src/client/core/common.rs +0 -3
- data/vendor/wreq/src/client/core/conn/http1.rs +0 -342
- data/vendor/wreq/src/client/core/conn/http2.rs +0 -307
- data/vendor/wreq/src/client/core/conn.rs +0 -11
- data/vendor/wreq/src/client/core/dispatch.rs +0 -299
- data/vendor/wreq/src/client/core/error.rs +0 -435
- data/vendor/wreq/src/client/core/ext.rs +0 -201
- data/vendor/wreq/src/client/core/http1.rs +0 -178
- data/vendor/wreq/src/client/core/http2.rs +0 -483
- data/vendor/wreq/src/client/core/proto/h1/conn.rs +0 -988
- data/vendor/wreq/src/client/core/proto/h1/decode.rs +0 -1170
- data/vendor/wreq/src/client/core/proto/h1/dispatch.rs +0 -684
- data/vendor/wreq/src/client/core/proto/h1/encode.rs +0 -580
- data/vendor/wreq/src/client/core/proto/h1/io.rs +0 -879
- data/vendor/wreq/src/client/core/proto/h1/role.rs +0 -694
- data/vendor/wreq/src/client/core/proto/h1.rs +0 -104
- data/vendor/wreq/src/client/core/proto/h2/client.rs +0 -650
- data/vendor/wreq/src/client/core/proto/h2/ping.rs +0 -539
- data/vendor/wreq/src/client/core/proto/h2.rs +0 -379
- data/vendor/wreq/src/client/core/proto/headers.rs +0 -138
- data/vendor/wreq/src/client/core/proto.rs +0 -58
- data/vendor/wreq/src/client/core/rt/bounds.rs +0 -57
- data/vendor/wreq/src/client/core/rt/timer.rs +0 -150
- data/vendor/wreq/src/client/core/rt/tokio.rs +0 -99
- data/vendor/wreq/src/client/core/rt.rs +0 -25
- data/vendor/wreq/src/client/core/upgrade.rs +0 -267
- data/vendor/wreq/src/client/core.rs +0 -16
- data/vendor/wreq/src/client/emulation.rs +0 -161
- data/vendor/wreq/src/client/http/client/error.rs +0 -142
- data/vendor/wreq/src/client/http/client/exec.rs +0 -29
- data/vendor/wreq/src/client/http/client/extra.rs +0 -77
- data/vendor/wreq/src/client/http/client/util.rs +0 -104
- data/vendor/wreq/src/client/http.rs +0 -1629
- data/vendor/wreq/src/client/layer/config/options.rs +0 -156
- data/vendor/wreq/src/client/layer/cookie.rs +0 -161
- data/vendor/wreq/src/hash.rs +0 -143
- data/vendor/wreq/src/tls/conn/cache.rs +0 -123
- data/vendor/wreq/src/tls/conn/cert_compression.rs +0 -125
- data/vendor/wreq/src/tls/keylog/handle.rs +0 -64
- data/vendor/wreq/src/tls/options.rs +0 -464
- /data/vendor/wreq/src/client/{http → layer}/client/lazy.rs +0 -0
- /data/vendor/wreq/src/{client/conn → conn}/proxy.rs +0 -0
- /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
|
|
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
|
-
|
|
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::
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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<
|
|
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 =
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
|
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::
|
|
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
|
|
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
|
-
|
|
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:
|
|
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:
|
|
98
|
-
timer:
|
|
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<
|
|
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
|
|
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::
|
|
128
|
-
idle:
|
|
129
|
-
|
|
130
|
-
|
|
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::
|
|
135
|
-
exec
|
|
136
|
-
timer
|
|
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:
|
|
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
|
-
.
|
|
352
|
+
.get_or_insert_mut(key.clone(), Vec::<Idle<T>>::default);
|
|
354
353
|
|
|
355
|
-
if
|
|
356
|
-
|
|
357
|
-
|
|
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
|
-
|
|
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 =
|
|
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.
|
|
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
|
|
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.
|
|
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) =>
|
|
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:
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
866
|
-
|
|
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
|
|
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
|
-
|
|
971
|
-
|
|
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
|
-
|
|
1091
|
-
|
|
1095
|
+
Executor::default(),
|
|
1096
|
+
Timer::default(),
|
|
1092
1097
|
);
|
|
1093
1098
|
let key1 = host_key("foo");
|
|
1094
1099
|
let key2 = host_key("bar");
|