wreq-rb 0.6.0 → 0.6.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 +257 -102
- data/ext/wreq_rb/Cargo.toml +4 -4
- data/ext/wreq_rb/src/client.rs +1 -1
- data/lib/wreq-rb/version.rb +1 -1
- data/patches/0001-add-transfer-size-tracking.patch +11 -15
- data/vendor/wreq/Cargo.toml +9 -8
- data/vendor/wreq/README.md +5 -5
- data/vendor/wreq/bench/support/bench.rs +6 -2
- data/vendor/wreq/bench/support/client.rs +88 -1
- data/vendor/wreq/bench/support/exec.rs +0 -0
- data/vendor/wreq/bench/support/rt.rs +34 -0
- data/vendor/wreq/bench/support/server.rs +1 -1
- data/vendor/wreq/bench/support.rs +1 -15
- data/vendor/wreq/examples/cert_store.rs +13 -13
- data/vendor/wreq/examples/request_with_emulate.rs +1 -1
- data/vendor/wreq/examples/tcp_linger.rs +22 -0
- data/vendor/wreq/src/client/layer/client/pool.rs +17 -17
- data/vendor/wreq/src/client/layer/client.rs +2 -0
- data/vendor/wreq/src/client/layer/decoder.rs +71 -17
- data/vendor/wreq/src/client/layer/redirect/future.rs +49 -63
- data/vendor/wreq/src/client/layer/redirect/policy.rs +2 -26
- data/vendor/wreq/src/client/layer/redirect.rs +48 -60
- data/vendor/wreq/src/client/layer/retry.rs +12 -15
- data/vendor/wreq/src/client/layer/timeout/body.rs +27 -21
- data/vendor/wreq/src/client/layer/timeout/future.rs +33 -58
- data/vendor/wreq/src/client/layer/timeout.rs +8 -14
- data/vendor/wreq/src/client/request.rs +4 -0
- data/vendor/wreq/src/client.rs +53 -31
- data/vendor/wreq/src/conn/connector.rs +99 -129
- data/vendor/wreq/src/conn/http.rs +25 -18
- data/vendor/wreq/src/conn/net/tcp.rs +601 -107
- data/vendor/wreq/src/conn/proxy/socks.rs +6 -6
- data/vendor/wreq/src/conn/timeout.rs +166 -0
- data/vendor/wreq/src/conn.rs +5 -4
- data/vendor/wreq/src/cookie/jar.rs +1225 -0
- data/vendor/wreq/src/cookie/store.rs +321 -0
- data/vendor/wreq/src/cookie.rs +108 -612
- data/vendor/wreq/src/dns/resolve.rs +8 -2
- data/vendor/wreq/src/dns.rs +4 -4
- data/vendor/wreq/src/error.rs +53 -20
- data/vendor/wreq/src/lib.rs +1 -0
- data/vendor/wreq/src/proxy/matcher.rs +26 -12
- data/vendor/wreq/src/proxy/win.rs +39 -9
- data/vendor/wreq/src/redirect.rs +515 -100
- data/vendor/wreq/src/tls/conn.rs +3 -11
- data/vendor/wreq/src/tls/session.rs +7 -8
- data/vendor/wreq/src/tls/trust/store.rs +4 -4
- data/vendor/wreq/src/util.rs +23 -0
- data/vendor/wreq/tests/badssl.rs +72 -7
- data/vendor/wreq/tests/brotli.rs +1 -1
- data/vendor/wreq/tests/client.rs +24 -0
- data/vendor/wreq/tests/connector_layers.rs +8 -4
- data/vendor/wreq/tests/cookie.rs +59 -0
- data/vendor/wreq/tests/deflate.rs +1 -1
- data/vendor/wreq/tests/gzip.rs +53 -1
- data/vendor/wreq/tests/layers.rs +8 -4
- data/vendor/wreq/tests/redirect.rs +180 -97
- data/vendor/wreq/tests/timeouts.rs +47 -12
- data/vendor/wreq/tests/zstd.rs +1 -1
- metadata +7 -1
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
use std::task::{Context, Poll};
|
|
4
4
|
|
|
5
|
-
use http::{
|
|
5
|
+
use http::{
|
|
6
|
+
HeaderValue, Request, Response,
|
|
7
|
+
header::{ACCEPT_ENCODING, RANGE},
|
|
8
|
+
};
|
|
6
9
|
use http_body::Body;
|
|
7
10
|
use tower::{Layer, Service};
|
|
8
11
|
use tower_http::decompression::{self, DecompressionBody, ResponseFuture};
|
|
@@ -26,15 +29,26 @@ pub(crate) struct AcceptEncoding {
|
|
|
26
29
|
pub(crate) deflate: bool,
|
|
27
30
|
}
|
|
28
31
|
|
|
29
|
-
///
|
|
32
|
+
/// Builds response decompression middleware for a client service.
|
|
33
|
+
///
|
|
34
|
+
/// `DecompressionLayer` stores the client's default [`AcceptEncoding`] configuration
|
|
35
|
+
/// and applies it when constructing a [`Decompression`] service.
|
|
30
36
|
#[derive(Clone)]
|
|
31
37
|
pub struct DecompressionLayer {
|
|
32
38
|
accept: AcceptEncoding,
|
|
33
39
|
}
|
|
34
40
|
|
|
35
|
-
///
|
|
41
|
+
/// Negotiates response encodings and transparently decodes response bodies.
|
|
42
|
+
///
|
|
43
|
+
/// Before forwarding a request, `Decompression` applies request-specific
|
|
44
|
+
/// [`AcceptEncoding`] settings and keeps range requests on the identity representation.
|
|
45
|
+
/// The wrapped `tower-http` service then advertises enabled encodings and decodes matching
|
|
46
|
+
/// responses.
|
|
36
47
|
#[derive(Clone)]
|
|
37
|
-
pub struct Decompression<S>
|
|
48
|
+
pub struct Decompression<S> {
|
|
49
|
+
decoder: Option<decompression::Decompression<S>>,
|
|
50
|
+
enabled: bool,
|
|
51
|
+
}
|
|
38
52
|
|
|
39
53
|
// ===== AcceptEncoding =====
|
|
40
54
|
|
|
@@ -53,6 +67,32 @@ impl Default for AcceptEncoding {
|
|
|
53
67
|
}
|
|
54
68
|
}
|
|
55
69
|
|
|
70
|
+
impl AcceptEncoding {
|
|
71
|
+
fn is_enabled(&self) -> bool {
|
|
72
|
+
#[cfg(feature = "gzip")]
|
|
73
|
+
if self.gzip {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
#[cfg(feature = "deflate")]
|
|
78
|
+
if self.deflate {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
#[cfg(feature = "brotli")]
|
|
83
|
+
if self.brotli {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
#[cfg(feature = "zstd")]
|
|
88
|
+
if self.zstd {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
false
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
56
96
|
impl_request_config_value!(AcceptEncoding);
|
|
57
97
|
|
|
58
98
|
// ===== impl DecompressionLayer =====
|
|
@@ -75,10 +115,10 @@ impl<S> Layer<S> for DecompressionLayer {
|
|
|
75
115
|
.no_deflate()
|
|
76
116
|
.no_gzip()
|
|
77
117
|
.no_zstd();
|
|
78
|
-
Decompression
|
|
79
|
-
decoder,
|
|
80
|
-
|
|
81
|
-
|
|
118
|
+
Decompression {
|
|
119
|
+
decoder: Some(Decompression::<S>::accept_in_place(decoder, &self.accept)),
|
|
120
|
+
enabled: self.accept.is_enabled(),
|
|
121
|
+
}
|
|
82
122
|
}
|
|
83
123
|
}
|
|
84
124
|
|
|
@@ -127,18 +167,32 @@ where
|
|
|
127
167
|
|
|
128
168
|
#[inline(always)]
|
|
129
169
|
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
|
130
|
-
self.
|
|
170
|
+
self.decoder.as_mut().expect(Self::BUG_MSG).poll_ready(cx)
|
|
131
171
|
}
|
|
132
172
|
|
|
133
|
-
fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
|
|
134
|
-
|
|
135
|
-
if let Some(
|
|
136
|
-
self.
|
|
137
|
-
.
|
|
138
|
-
|
|
139
|
-
|
|
173
|
+
fn call(&mut self, mut req: Request<ReqBody>) -> Self::Future {
|
|
174
|
+
let enabled =
|
|
175
|
+
if let Some(accept_encoding) = RequestConfig::<AcceptEncoding>::get(req.extensions()) {
|
|
176
|
+
if let Some(decoder) = self.decoder.take() {
|
|
177
|
+
self.decoder
|
|
178
|
+
.replace(Decompression::accept_in_place(decoder, accept_encoding));
|
|
179
|
+
}
|
|
180
|
+
debug_assert!(self.decoder.is_some());
|
|
181
|
+
accept_encoding.is_enabled()
|
|
182
|
+
} else {
|
|
183
|
+
self.enabled
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
if enabled && req.headers().contains_key(RANGE) {
|
|
187
|
+
// tower-http does not account for Range when adding Accept-Encoding, so correct it
|
|
188
|
+
// before delegating. RFC 9110 section 14.1.2 applies byte ranges to the encoded
|
|
189
|
+
// representation, and Fetch avoids partial codings by requesting identity:
|
|
190
|
+
// https://www.rfc-editor.org/rfc/rfc9110.html#section-14.1.2
|
|
191
|
+
// https://fetch.spec.whatwg.org/#http-network-or-cache-fetch
|
|
192
|
+
req.headers_mut()
|
|
193
|
+
.insert(ACCEPT_ENCODING, HeaderValue::from_static("identity"));
|
|
140
194
|
}
|
|
141
195
|
|
|
142
|
-
self.
|
|
196
|
+
self.decoder.as_mut().expect(Self::BUG_MSG).call(req)
|
|
143
197
|
}
|
|
144
198
|
}
|
|
@@ -9,42 +9,40 @@ use futures_util::future::Either;
|
|
|
9
9
|
use http::{
|
|
10
10
|
HeaderMap, Method, Request, Response, StatusCode, Uri,
|
|
11
11
|
header::{CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TYPE, LOCATION, TRANSFER_ENCODING},
|
|
12
|
-
request::Parts,
|
|
13
12
|
};
|
|
14
|
-
use http_body::Body;
|
|
15
13
|
use pin_project_lite::pin_project;
|
|
16
14
|
use tower::{BoxError, Service, util::Oneshot};
|
|
17
15
|
use url::Url;
|
|
18
16
|
|
|
19
17
|
use super::{
|
|
20
18
|
BodyRepr,
|
|
21
|
-
policy::{Action, Attempt
|
|
19
|
+
policy::{Action, Attempt},
|
|
22
20
|
};
|
|
23
|
-
use crate::{
|
|
21
|
+
use crate::{Body, ext::RequestUri, into_uri::IntoUriSealed, redirect::FollowRedirectPolicy};
|
|
24
22
|
|
|
25
23
|
/// Pending future state for handling redirects.
|
|
26
|
-
pub struct Pending<
|
|
24
|
+
pub struct Pending<Response> {
|
|
27
25
|
future: Pin<Box<dyn Future<Output = Action> + Send>>,
|
|
28
26
|
location: Uri,
|
|
29
|
-
body:
|
|
27
|
+
body: Body,
|
|
30
28
|
res: Response,
|
|
31
29
|
}
|
|
32
30
|
|
|
33
31
|
pin_project! {
|
|
34
32
|
/// Response future for [`FollowRedirect`].
|
|
35
33
|
#[project = ResponseFutureProj]
|
|
36
|
-
pub enum ResponseFuture<S
|
|
34
|
+
pub enum ResponseFuture<S>
|
|
37
35
|
where
|
|
38
|
-
S: Service<Request<
|
|
36
|
+
S: Service<Request<Body>>,
|
|
39
37
|
{
|
|
40
38
|
Redirect {
|
|
41
39
|
#[pin]
|
|
42
|
-
future: Either<S::Future, Oneshot<S, Request<
|
|
43
|
-
pending_future: Option<Pending<
|
|
40
|
+
future: Either<S::Future, Oneshot<S, Request<Body>>>,
|
|
41
|
+
pending_future: Option<Pending<S::Response>>,
|
|
44
42
|
service: S,
|
|
45
|
-
policy:
|
|
46
|
-
|
|
47
|
-
body_repr: BodyRepr<
|
|
43
|
+
policy: FollowRedirectPolicy,
|
|
44
|
+
request: Request<()>,
|
|
45
|
+
body_repr: BodyRepr<Body>,
|
|
48
46
|
},
|
|
49
47
|
|
|
50
48
|
Direct {
|
|
@@ -54,14 +52,12 @@ pin_project! {
|
|
|
54
52
|
}
|
|
55
53
|
}
|
|
56
54
|
|
|
57
|
-
impl<S,
|
|
55
|
+
impl<S, B> Future for ResponseFuture<S>
|
|
58
56
|
where
|
|
59
|
-
S: Service<Request<
|
|
57
|
+
S: Service<Request<Body>, Response = Response<B>> + Clone,
|
|
60
58
|
S::Error: From<BoxError>,
|
|
61
|
-
P: Policy<ReqBody, S::Error>,
|
|
62
|
-
ReqBody: Body + Default,
|
|
63
59
|
{
|
|
64
|
-
type Output = Result<Response<
|
|
60
|
+
type Output = Result<Response<B>, S::Error>;
|
|
65
61
|
|
|
66
62
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
67
63
|
match self.project() {
|
|
@@ -71,7 +67,7 @@ where
|
|
|
71
67
|
pending_future,
|
|
72
68
|
service,
|
|
73
69
|
policy,
|
|
74
|
-
|
|
70
|
+
request,
|
|
75
71
|
body_repr,
|
|
76
72
|
} => {
|
|
77
73
|
// Check if we have a pending action to resolve
|
|
@@ -91,7 +87,7 @@ where
|
|
|
91
87
|
future: &mut future,
|
|
92
88
|
service,
|
|
93
89
|
policy,
|
|
94
|
-
|
|
90
|
+
request,
|
|
95
91
|
body: state.body,
|
|
96
92
|
body_repr,
|
|
97
93
|
res: state.res,
|
|
@@ -103,7 +99,8 @@ where
|
|
|
103
99
|
// Poll the current future to get the response
|
|
104
100
|
let mut res = {
|
|
105
101
|
let mut res = ready!(future.as_mut().poll(cx)?);
|
|
106
|
-
res.extensions_mut()
|
|
102
|
+
res.extensions_mut()
|
|
103
|
+
.insert(RequestUri(request.uri().clone()));
|
|
107
104
|
res
|
|
108
105
|
};
|
|
109
106
|
|
|
@@ -112,19 +109,19 @@ where
|
|
|
112
109
|
StatusCode::MOVED_PERMANENTLY | StatusCode::FOUND => {
|
|
113
110
|
// User agents MAY change the request method from POST to GET
|
|
114
111
|
// (RFC 7231 section 6.4.2. and 6.4.3.).
|
|
115
|
-
if
|
|
116
|
-
|
|
112
|
+
if request.method() == Method::POST {
|
|
113
|
+
*request.method_mut() = Method::GET;
|
|
117
114
|
*body_repr = BodyRepr::Empty;
|
|
118
|
-
drop_payload_headers(
|
|
115
|
+
drop_payload_headers(request.headers_mut());
|
|
119
116
|
}
|
|
120
117
|
}
|
|
121
118
|
StatusCode::SEE_OTHER => {
|
|
122
119
|
// A user agent can perform a GET or HEAD request (RFC 7231 section 6.4.4.).
|
|
123
|
-
if
|
|
124
|
-
|
|
120
|
+
if request.method() != Method::HEAD {
|
|
121
|
+
*request.method_mut() = Method::GET;
|
|
125
122
|
}
|
|
126
123
|
*body_repr = BodyRepr::Empty;
|
|
127
|
-
drop_payload_headers(
|
|
124
|
+
drop_payload_headers(request.headers_mut());
|
|
128
125
|
}
|
|
129
126
|
StatusCode::TEMPORARY_REDIRECT | StatusCode::PERMANENT_REDIRECT => {}
|
|
130
127
|
_ => {
|
|
@@ -144,7 +141,7 @@ where
|
|
|
144
141
|
.headers()
|
|
145
142
|
.get(LOCATION)
|
|
146
143
|
.and_then(|loc| loc.to_str().ok())
|
|
147
|
-
.and_then(|loc| resolve_uri(loc,
|
|
144
|
+
.and_then(|loc| resolve_uri(loc, request.uri()))
|
|
148
145
|
else {
|
|
149
146
|
return Poll::Ready(Ok(res));
|
|
150
147
|
};
|
|
@@ -154,7 +151,7 @@ where
|
|
|
154
151
|
status: res.status(),
|
|
155
152
|
headers: res.headers(),
|
|
156
153
|
location: &location,
|
|
157
|
-
previous:
|
|
154
|
+
previous: request.uri(),
|
|
158
155
|
};
|
|
159
156
|
|
|
160
157
|
// Resolve the action, awaiting if it's pending
|
|
@@ -180,7 +177,7 @@ where
|
|
|
180
177
|
future: &mut future,
|
|
181
178
|
service,
|
|
182
179
|
policy,
|
|
183
|
-
|
|
180
|
+
request,
|
|
184
181
|
body,
|
|
185
182
|
body_repr,
|
|
186
183
|
res,
|
|
@@ -215,59 +212,48 @@ fn drop_payload_headers(headers: &mut HeaderMap) {
|
|
|
215
212
|
}
|
|
216
213
|
}
|
|
217
214
|
|
|
218
|
-
type RedirectFuturePin<'a, S
|
|
219
|
-
Pin<&'a mut Either<<S as Service<Request<
|
|
215
|
+
type RedirectFuturePin<'a, S> =
|
|
216
|
+
Pin<&'a mut Either<<S as Service<Request<Body>>>::Future, Oneshot<S, Request<Body>>>>;
|
|
220
217
|
|
|
221
|
-
struct RedirectAction<'a, S,
|
|
218
|
+
struct RedirectAction<'a, S, B>
|
|
222
219
|
where
|
|
223
|
-
S: Service<Request<
|
|
224
|
-
P: Policy<ReqBody, S::Error>,
|
|
220
|
+
S: Service<Request<Body>, Response = Response<B>> + Clone,
|
|
225
221
|
{
|
|
226
222
|
action: Action,
|
|
227
|
-
future: &'a mut RedirectFuturePin<'a, S
|
|
223
|
+
future: &'a mut RedirectFuturePin<'a, S>,
|
|
228
224
|
service: &'a S,
|
|
229
|
-
policy: &'a mut
|
|
230
|
-
|
|
231
|
-
body:
|
|
232
|
-
body_repr: &'a mut BodyRepr<
|
|
233
|
-
res: Response<
|
|
225
|
+
policy: &'a mut FollowRedirectPolicy,
|
|
226
|
+
request: &'a mut Request<()>,
|
|
227
|
+
body: Body,
|
|
228
|
+
body_repr: &'a mut BodyRepr<Body>,
|
|
229
|
+
res: Response<B>,
|
|
234
230
|
location: Uri,
|
|
235
231
|
}
|
|
236
232
|
|
|
237
|
-
fn handle_action<S,
|
|
233
|
+
fn handle_action<S, B>(
|
|
238
234
|
cx: &mut Context<'_>,
|
|
239
|
-
redirect: RedirectAction<'_, S,
|
|
240
|
-
) -> Poll<Result<Response<
|
|
235
|
+
redirect: RedirectAction<'_, S, B>,
|
|
236
|
+
) -> Poll<Result<Response<B>, S::Error>>
|
|
241
237
|
where
|
|
242
|
-
S: Service<Request<
|
|
238
|
+
S: Service<Request<Body>, Response = Response<B>> + Clone,
|
|
243
239
|
S::Error: From<BoxError>,
|
|
244
|
-
P: Policy<ReqBody, S::Error>,
|
|
245
|
-
ReqBody: Body + Default,
|
|
246
240
|
{
|
|
247
241
|
match redirect.action {
|
|
248
242
|
Action::Follow => {
|
|
249
|
-
redirect.
|
|
250
|
-
redirect
|
|
251
|
-
|
|
252
|
-
.try_clone_from(&redirect.body, redirect.policy);
|
|
243
|
+
*redirect.request.uri_mut() = redirect.location;
|
|
244
|
+
redirect.body_repr.try_clone_from(&redirect.body);
|
|
245
|
+
redirect.policy.on_request(redirect.request);
|
|
253
246
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
.set(Either::Right(Oneshot::new(redirect.service.clone(), req)));
|
|
247
|
+
redirect.future.set(Either::Right(Oneshot::new(
|
|
248
|
+
redirect.service.clone(),
|
|
249
|
+
redirect.request.clone().map(|_| redirect.body),
|
|
250
|
+
)));
|
|
259
251
|
|
|
260
252
|
cx.waker().wake_by_ref();
|
|
261
253
|
Poll::Pending
|
|
262
254
|
}
|
|
263
255
|
Action::Stop => Poll::Ready(Ok(redirect.res)),
|
|
264
|
-
Action::Pending(_) => Poll::Ready(Err(S::Error::from(
|
|
265
|
-
Error::redirect(
|
|
266
|
-
"Nested pending Action is not supported in redirect policy",
|
|
267
|
-
redirect.parts.uri.clone(),
|
|
268
|
-
)
|
|
269
|
-
.into(),
|
|
270
|
-
))),
|
|
271
256
|
Action::Error(err) => Poll::Ready(Err(err.into())),
|
|
257
|
+
Action::Pending(_) => unreachable!(),
|
|
272
258
|
}
|
|
273
259
|
}
|
|
@@ -2,34 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
use std::{fmt, pin::Pin};
|
|
4
4
|
|
|
5
|
-
use http::{HeaderMap,
|
|
5
|
+
use http::{HeaderMap, StatusCode, Uri};
|
|
6
6
|
|
|
7
7
|
use crate::error::BoxError;
|
|
8
8
|
|
|
9
|
-
/// Trait for the policy on handling redirection responses.
|
|
10
|
-
pub trait Policy<B, E> {
|
|
11
|
-
/// Invoked when the service received a response with a redirection status code (`3xx`).
|
|
12
|
-
///
|
|
13
|
-
/// This method returns an [`Action`] which indicates whether the service should follow
|
|
14
|
-
/// the redirection.
|
|
15
|
-
fn redirect(&mut self, attempt: Attempt<'_>) -> Result<Action, E>;
|
|
16
|
-
|
|
17
|
-
/// Returns whether redirection is currently permitted by this policy.
|
|
18
|
-
///
|
|
19
|
-
/// This method is called to determine whether the client should follow redirects at all.
|
|
20
|
-
/// It allows policies to enable or disable redirection behavior based on the [`Request`].
|
|
21
|
-
fn follow_redirects(&mut self, _request: &mut Request<B>) -> bool;
|
|
22
|
-
|
|
23
|
-
/// Invoked right before the service makes a [`Request`].
|
|
24
|
-
fn on_request(&mut self, _request: &mut Request<B>);
|
|
25
|
-
|
|
26
|
-
/// Invoked right after the service received a [`Response`].
|
|
27
|
-
fn on_response<Body>(&mut self, _response: &mut Response<Body>);
|
|
28
|
-
|
|
29
|
-
/// Try to clone a request body before the service makes a redirected request.
|
|
30
|
-
fn clone_body(&self, _body: &B) -> Option<B>;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
9
|
/// A type that holds information on a redirection attempt.
|
|
34
10
|
pub struct Attempt<'a> {
|
|
35
11
|
pub(crate) status: StatusCode,
|
|
@@ -38,7 +14,7 @@ pub struct Attempt<'a> {
|
|
|
38
14
|
pub(crate) previous: &'a Uri,
|
|
39
15
|
}
|
|
40
16
|
|
|
41
|
-
/// A value
|
|
17
|
+
/// A value which indicates the action
|
|
42
18
|
/// [`FollowRedirect`][super::FollowRedirect] should take for a redirection response.
|
|
43
19
|
pub enum Action {
|
|
44
20
|
/// Follow the redirection.
|
|
@@ -10,11 +10,12 @@ use std::{
|
|
|
10
10
|
|
|
11
11
|
use futures_util::future::Either;
|
|
12
12
|
use http::{Request, Response};
|
|
13
|
-
use http_body::Body;
|
|
13
|
+
use http_body::Body as HttpBody;
|
|
14
14
|
use tower::{BoxError, Layer, Service};
|
|
15
15
|
|
|
16
16
|
use self::future::ResponseFuture;
|
|
17
|
-
pub use self::policy::{Action, Attempt
|
|
17
|
+
pub use self::policy::{Action, Attempt};
|
|
18
|
+
use crate::{client::body::Body, redirect::FollowRedirectPolicy};
|
|
18
19
|
|
|
19
20
|
enum BodyRepr<B> {
|
|
20
21
|
Some(B),
|
|
@@ -22,31 +23,25 @@ enum BodyRepr<B> {
|
|
|
22
23
|
None,
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
impl
|
|
26
|
-
|
|
27
|
-
B: Body + Default,
|
|
28
|
-
{
|
|
29
|
-
fn take(&mut self) -> Option<B> {
|
|
26
|
+
impl BodyRepr<Body> {
|
|
27
|
+
fn take(&mut self) -> Option<Body> {
|
|
30
28
|
match mem::replace(self, BodyRepr::None) {
|
|
31
29
|
BodyRepr::Some(body) => Some(body),
|
|
32
30
|
BodyRepr::Empty => {
|
|
33
31
|
*self = BodyRepr::Empty;
|
|
34
|
-
Some(
|
|
32
|
+
Some(Body::default())
|
|
35
33
|
}
|
|
36
34
|
BodyRepr::None => None,
|
|
37
35
|
}
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
fn try_clone_from
|
|
41
|
-
where
|
|
42
|
-
P: Policy<B, E>,
|
|
43
|
-
{
|
|
38
|
+
fn try_clone_from(&mut self, body: &Body) {
|
|
44
39
|
match self {
|
|
45
40
|
BodyRepr::Some(_) | BodyRepr::Empty => {}
|
|
46
41
|
BodyRepr::None => {
|
|
47
42
|
if body.size_hint().exact() == Some(0) {
|
|
48
|
-
*self = BodyRepr::Some(
|
|
49
|
-
} else if let Some(cloned) =
|
|
43
|
+
*self = BodyRepr::Some(Body::default());
|
|
44
|
+
} else if let Some(cloned) = body.try_clone() {
|
|
50
45
|
*self = BodyRepr::Some(cloned);
|
|
51
46
|
}
|
|
52
47
|
}
|
|
@@ -55,25 +50,24 @@ where
|
|
|
55
50
|
}
|
|
56
51
|
|
|
57
52
|
/// [`Layer`] for retrying requests with a [`Service`] to follow redirection responses.
|
|
58
|
-
#[derive(Clone
|
|
59
|
-
pub struct FollowRedirectLayer
|
|
60
|
-
policy:
|
|
53
|
+
#[derive(Clone)]
|
|
54
|
+
pub struct FollowRedirectLayer {
|
|
55
|
+
policy: FollowRedirectPolicy,
|
|
61
56
|
}
|
|
62
57
|
|
|
63
|
-
impl
|
|
64
|
-
/// Create a new [`FollowRedirectLayer`] with the given redirection
|
|
58
|
+
impl FollowRedirectLayer {
|
|
59
|
+
/// Create a new [`FollowRedirectLayer`] with the given redirection policy.
|
|
65
60
|
#[inline(always)]
|
|
66
|
-
pub fn with_policy(policy:
|
|
61
|
+
pub(crate) fn with_policy(policy: FollowRedirectPolicy) -> Self {
|
|
67
62
|
FollowRedirectLayer { policy }
|
|
68
63
|
}
|
|
69
64
|
}
|
|
70
65
|
|
|
71
|
-
impl<S
|
|
66
|
+
impl<S> Layer<S> for FollowRedirectLayer
|
|
72
67
|
where
|
|
73
68
|
S: Clone,
|
|
74
|
-
P: Clone,
|
|
75
69
|
{
|
|
76
|
-
type Service = FollowRedirect<S
|
|
70
|
+
type Service = FollowRedirect<S>;
|
|
77
71
|
|
|
78
72
|
#[inline(always)]
|
|
79
73
|
fn layer(&self, inner: S) -> Self::Service {
|
|
@@ -82,63 +76,57 @@ where
|
|
|
82
76
|
}
|
|
83
77
|
|
|
84
78
|
/// Middleware that retries requests with a [`Service`] to follow redirection responses.
|
|
85
|
-
#[derive(Clone
|
|
86
|
-
pub struct FollowRedirect<S
|
|
79
|
+
#[derive(Clone)]
|
|
80
|
+
pub struct FollowRedirect<S> {
|
|
87
81
|
inner: S,
|
|
88
|
-
policy:
|
|
82
|
+
policy: FollowRedirectPolicy,
|
|
89
83
|
}
|
|
90
84
|
|
|
91
|
-
impl<S
|
|
92
|
-
|
|
93
|
-
P: Clone,
|
|
94
|
-
{
|
|
95
|
-
/// Create a new [`FollowRedirect`] with the given redirection [`Policy`].
|
|
85
|
+
impl<S> FollowRedirect<S> {
|
|
86
|
+
/// Create a new [`FollowRedirect`] with the given redirection policy.
|
|
96
87
|
#[inline(always)]
|
|
97
|
-
|
|
88
|
+
fn with_policy(inner: S, policy: FollowRedirectPolicy) -> Self {
|
|
98
89
|
FollowRedirect { inner, policy }
|
|
99
90
|
}
|
|
100
91
|
}
|
|
101
92
|
|
|
102
|
-
impl<
|
|
93
|
+
impl<ResBody, S> Service<Request<Body>> for FollowRedirect<S>
|
|
103
94
|
where
|
|
104
|
-
S: Service<Request<
|
|
95
|
+
S: Service<Request<Body>, Response = Response<ResBody>> + Clone,
|
|
105
96
|
S::Error: From<BoxError>,
|
|
106
|
-
P: Policy<ReqBody, S::Error> + Clone,
|
|
107
|
-
ReqBody: Body + Default,
|
|
108
97
|
{
|
|
109
98
|
type Response = Response<ResBody>;
|
|
110
99
|
type Error = S::Error;
|
|
111
|
-
type Future = ResponseFuture<S
|
|
100
|
+
type Future = ResponseFuture<S>;
|
|
112
101
|
|
|
113
102
|
#[inline(always)]
|
|
114
103
|
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
|
115
104
|
self.inner.poll_ready(cx)
|
|
116
105
|
}
|
|
117
106
|
|
|
118
|
-
fn call(&mut self, mut req: Request<
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
let mut service = mem::replace(&mut self.inner, service);
|
|
122
|
-
let mut policy = self.policy.clone();
|
|
123
|
-
|
|
124
|
-
let mut body_repr = BodyRepr::None;
|
|
125
|
-
body_repr.try_clone_from(req.body(), &policy);
|
|
126
|
-
policy.on_request(&mut req);
|
|
127
|
-
|
|
128
|
-
let (parts, body) = req.into_parts();
|
|
129
|
-
let req = Request::from_parts(parts.clone(), body);
|
|
130
|
-
ResponseFuture::Redirect {
|
|
131
|
-
future: Either::Left(service.call(req)),
|
|
132
|
-
pending_future: None,
|
|
133
|
-
service,
|
|
134
|
-
policy,
|
|
135
|
-
parts,
|
|
136
|
-
body_repr,
|
|
137
|
-
}
|
|
138
|
-
} else {
|
|
139
|
-
ResponseFuture::Direct {
|
|
107
|
+
fn call(&mut self, mut req: Request<Body>) -> Self::Future {
|
|
108
|
+
let Some(mut policy) = self.policy.for_request(&mut req) else {
|
|
109
|
+
return ResponseFuture::Direct {
|
|
140
110
|
future: self.inner.call(req),
|
|
141
|
-
}
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
let service = self.inner.clone();
|
|
115
|
+
let mut service = mem::replace(&mut self.inner, service);
|
|
116
|
+
|
|
117
|
+
let mut body_repr = BodyRepr::None;
|
|
118
|
+
body_repr.try_clone_from(req.body());
|
|
119
|
+
|
|
120
|
+
policy.on_request(&mut req);
|
|
121
|
+
let (parts, body) = req.into_parts();
|
|
122
|
+
let request = Request::from_parts(parts, ());
|
|
123
|
+
ResponseFuture::Redirect {
|
|
124
|
+
future: Either::Left(service.call(request.clone().map(|_| body))),
|
|
125
|
+
pending_future: None,
|
|
126
|
+
service,
|
|
127
|
+
policy,
|
|
128
|
+
request,
|
|
129
|
+
body_repr,
|
|
142
130
|
}
|
|
143
131
|
}
|
|
144
132
|
}
|
|
@@ -132,22 +132,19 @@ fn is_retryable_error(err: &(dyn StdError + 'static)) -> bool {
|
|
|
132
132
|
return false;
|
|
133
133
|
};
|
|
134
134
|
|
|
135
|
-
if let Some(cause) = err.source()
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
135
|
+
if let Some(cause) = err.source()
|
|
136
|
+
&& let Some(err) = cause.downcast_ref::<http2::Error>()
|
|
137
|
+
{
|
|
138
|
+
// They sent us a graceful shutdown, try with a new connection!
|
|
139
|
+
if err.is_go_away() && err.is_remote() && err.reason() == Some(http2::Reason::NO_ERROR) {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
{
|
|
149
|
-
return true;
|
|
150
|
-
}
|
|
143
|
+
// REFUSED_STREAM was sent from the server, which is safe to retry.
|
|
144
|
+
// https://www.rfc-editor.org/rfc/rfc9113.html#section-8.7-3.2
|
|
145
|
+
if err.is_reset() && err.is_remote() && err.reason() == Some(http2::Reason::REFUSED_STREAM)
|
|
146
|
+
{
|
|
147
|
+
return true;
|
|
151
148
|
}
|
|
152
149
|
}
|
|
153
150
|
false
|