wreq-rb 0.5.1 → 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/README.md +28 -0
- data/ext/wreq_rb/Cargo.toml +4 -4
- data/ext/wreq_rb/src/client.rs +176 -2
- 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 +8 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
pub mod bench;
|
|
2
2
|
pub mod client;
|
|
3
|
+
pub mod rt;
|
|
3
4
|
pub mod server;
|
|
4
5
|
|
|
5
6
|
use std::fmt;
|
|
@@ -39,18 +40,3 @@ impl fmt::Display for Tls {
|
|
|
39
40
|
f.write_str(value)
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
|
-
|
|
43
|
-
pub fn current_thread_runtime() -> tokio::runtime::Runtime {
|
|
44
|
-
tokio::runtime::Builder::new_current_thread()
|
|
45
|
-
.enable_all()
|
|
46
|
-
.build()
|
|
47
|
-
.expect("Failed to build current-thread runtime")
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
pub fn multi_thread_runtime() -> tokio::runtime::Runtime {
|
|
51
|
-
tokio::runtime::Builder::new_multi_thread()
|
|
52
|
-
.worker_threads(4)
|
|
53
|
-
.enable_all()
|
|
54
|
-
.build()
|
|
55
|
-
.expect("Failed to build multi-thread runtime")
|
|
56
|
-
}
|
|
@@ -56,21 +56,21 @@ async fn main() -> wreq::Result<()> {
|
|
|
56
56
|
|
|
57
57
|
// Use the API you're already familiar with
|
|
58
58
|
let resp = client.get("https://self-signed.badssl.com/").send().await?;
|
|
59
|
-
if let Some(tls_info) = resp.extensions().get::<TlsInfo>()
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
if let Some(tls_info) = resp.extensions().get::<TlsInfo>()
|
|
60
|
+
&& let Some(peer_cert_der) = tls_info.peer_certificate()
|
|
61
|
+
{
|
|
62
|
+
// Create self-signed certificate Store
|
|
63
|
+
let self_signed_store = CertStore::from_der_certs([peer_cert_der])?;
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
// Create a client with self-signed certificate store
|
|
66
|
+
let client = Client::builder()
|
|
67
|
+
.tls_cert_store(self_signed_store)
|
|
68
|
+
.connect_timeout(Duration::from_secs(10))
|
|
69
|
+
.build()?;
|
|
69
70
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
71
|
+
// Use the API you're already familiar with
|
|
72
|
+
let resp = client.get("https://self-signed.badssl.com/").send().await?;
|
|
73
|
+
println!("{}", resp.text().await?);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
Ok(())
|
|
@@ -105,7 +105,7 @@ async fn main() -> wreq::Result<()> {
|
|
|
105
105
|
.build(Default::default());
|
|
106
106
|
|
|
107
107
|
// Use the API you're already familiar with
|
|
108
|
-
let resp = wreq::get("https://
|
|
108
|
+
let resp = wreq::get("https://pingly.us.kg/api/all")
|
|
109
109
|
.emulation(emulation)
|
|
110
110
|
.send()
|
|
111
111
|
.await?;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
use std::time::Duration;
|
|
2
|
+
|
|
3
|
+
use wreq::Client;
|
|
4
|
+
|
|
5
|
+
#[tokio::main]
|
|
6
|
+
async fn main() -> wreq::Result<()> {
|
|
7
|
+
// Reset connections on close rather than closing them gracefully.
|
|
8
|
+
let client = Client::builder().tcp_linger(Duration::ZERO).build()?;
|
|
9
|
+
|
|
10
|
+
let resp = client.get("https://tls.peet.ws/api/all").send().await?;
|
|
11
|
+
println!("{}", resp.text().await?);
|
|
12
|
+
|
|
13
|
+
// Non-zero duration waits for unsent data to be flushed.
|
|
14
|
+
let client = Client::builder()
|
|
15
|
+
.tcp_linger(Duration::from_secs(5))
|
|
16
|
+
.build()?;
|
|
17
|
+
|
|
18
|
+
let resp = client.get("https://tls.peet.ws/api/all").send().await?;
|
|
19
|
+
println!("{}", resp.text().await?);
|
|
20
|
+
|
|
21
|
+
Ok(())
|
|
22
|
+
}
|
|
@@ -156,20 +156,20 @@ impl<T: Poolable, K: Key> Pool<T, K> {
|
|
|
156
156
|
/// Ensure that there is only ever 1 connecting task for HTTP/2
|
|
157
157
|
/// connections. This does nothing for HTTP/1.
|
|
158
158
|
pub fn connecting(&self, key: K, ver: Ver) -> Option<Connecting<T, K>> {
|
|
159
|
-
if ver == Ver::Http2
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
Some(connecting)
|
|
168
|
-
} else {
|
|
169
|
-
trace!("HTTP/2 connecting already in progress for {:?}", key);
|
|
170
|
-
None
|
|
159
|
+
if ver == Ver::Http2
|
|
160
|
+
&& let Some(ref enabled) = self.inner
|
|
161
|
+
{
|
|
162
|
+
let mut inner = enabled.lock();
|
|
163
|
+
return if inner.connecting.insert(key.clone()) {
|
|
164
|
+
let connecting = Connecting {
|
|
165
|
+
key,
|
|
166
|
+
pool: WeakOpt::downgrade(enabled),
|
|
171
167
|
};
|
|
172
|
-
|
|
168
|
+
Some(connecting)
|
|
169
|
+
} else {
|
|
170
|
+
trace!("HTTP/2 connecting already in progress for {:?}", key);
|
|
171
|
+
None
|
|
172
|
+
};
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
// else
|
|
@@ -233,10 +233,10 @@ impl<T: Poolable, K: Key> Pool<T, K> {
|
|
|
233
233
|
// unique or shared. So, the hack is to just assume Ver::Http2 means
|
|
234
234
|
// shared... :(
|
|
235
235
|
let mut pool_ref = WeakOpt::none();
|
|
236
|
-
if !value.can_share()
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
236
|
+
if !value.can_share()
|
|
237
|
+
&& let Some(ref enabled) = self.inner
|
|
238
|
+
{
|
|
239
|
+
pool_ref = WeakOpt::downgrade(enabled);
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
Pooled {
|
|
@@ -232,6 +232,7 @@ where
|
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
+
#[allow(clippy::result_large_err)]
|
|
235
236
|
async fn try_send_request(
|
|
236
237
|
&self,
|
|
237
238
|
mut req: Request<B>,
|
|
@@ -751,6 +752,7 @@ impl<B> PoolClient<B> {
|
|
|
751
752
|
|
|
752
753
|
impl<B: Body + 'static> PoolClient<B> {
|
|
753
754
|
#[inline]
|
|
755
|
+
#[allow(clippy::result_large_err)]
|
|
754
756
|
fn try_send_request(
|
|
755
757
|
&mut self,
|
|
756
758
|
req: Request<B>,
|
|
@@ -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.
|