wreq-rb 0.5.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 +19 -15
- data/ext/wreq_rb/Cargo.toml +4 -6
- data/ext/wreq_rb/src/client.rs +41 -48
- 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 +49 -71
- data/patches/0002-add-cancel-connections.patch +0 -181
- 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
|
@@ -1,124 +1,69 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
mod exec;
|
|
4
|
-
pub mod extra;
|
|
1
|
+
//! Much of this codebase is adapted and refined from [hyper](https://github.com/hyperium/hyper-util),
|
|
2
|
+
|
|
5
3
|
mod lazy;
|
|
6
4
|
mod pool;
|
|
7
|
-
mod util;
|
|
8
5
|
|
|
9
6
|
use std::{
|
|
7
|
+
error::Error as StdError,
|
|
8
|
+
fmt,
|
|
10
9
|
future::Future,
|
|
11
|
-
num::
|
|
12
|
-
pin::Pin,
|
|
13
|
-
sync::Arc,
|
|
10
|
+
num::NonZeroUsize,
|
|
14
11
|
task::{self, Poll},
|
|
15
12
|
time::Duration,
|
|
16
13
|
};
|
|
17
14
|
|
|
18
15
|
use bytes::Bytes;
|
|
19
|
-
use futures_util::future::{Either, FutureExt, TryFutureExt};
|
|
16
|
+
use futures_util::future::{self, BoxFuture, Either, FutureExt, TryFutureExt};
|
|
20
17
|
use http::{
|
|
21
18
|
HeaderValue, Method, Request, Response, Uri, Version,
|
|
22
19
|
header::{HOST, PROXY_AUTHORIZATION},
|
|
20
|
+
uri::{Authority, PathAndQuery, Scheme},
|
|
23
21
|
};
|
|
24
22
|
use http_body::Body;
|
|
25
23
|
use pool::Ver;
|
|
26
24
|
use tokio::io::{AsyncRead, AsyncWrite};
|
|
27
|
-
use tower::util::Oneshot;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
use tower::{BoxError, util::Oneshot};
|
|
26
|
+
use wreq_proto::{
|
|
27
|
+
body::Incoming,
|
|
28
|
+
conn::{self, TrySendError as ConnTrySendError},
|
|
29
|
+
http1::Http1Options,
|
|
30
|
+
http2::Http2Options,
|
|
31
|
+
rt::Executor as _,
|
|
34
32
|
};
|
|
33
|
+
#[cfg(feature = "cookies")]
|
|
34
|
+
use {
|
|
35
|
+
crate::cookie::{CookieStore, Cookies},
|
|
36
|
+
http::header::COOKIE,
|
|
37
|
+
std::sync::Arc,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
use self::lazy::{Started as Lazy, lazy};
|
|
35
41
|
use crate::{
|
|
36
|
-
client::
|
|
37
|
-
conn::{Connected, Connection},
|
|
38
|
-
core::{
|
|
39
|
-
body::Incoming,
|
|
40
|
-
conn::{self, TrySendError as ConnTrySendError},
|
|
41
|
-
http1::Http1Options,
|
|
42
|
-
http2::Http2Options,
|
|
43
|
-
rt::{ArcTimer, Executor, Timer},
|
|
44
|
-
},
|
|
45
|
-
layer::config::RequestOptions,
|
|
46
|
-
},
|
|
42
|
+
client::layer::config::RequestOptions,
|
|
47
43
|
config::RequestConfig,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
conn::{
|
|
45
|
+
Connected, Connection,
|
|
46
|
+
descriptor::{ConnectionDescriptor, ConnectionId},
|
|
47
|
+
proxy,
|
|
48
|
+
},
|
|
49
|
+
error::ProxyConnect,
|
|
50
|
+
rt::{Executor, Timer},
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
-
type BoxSendFuture = Pin<Box<dyn Future<Output = ()> + Send>>;
|
|
54
|
-
|
|
55
|
-
/// Parameters required to initiate a new connection.
|
|
56
|
-
///
|
|
57
|
-
/// [`ConnectRequest`] holds the target URI and all connection-specific options
|
|
58
|
-
/// (protocol, proxy, TCP/TLS settings) needed to establish a new network connection.
|
|
59
|
-
/// Used by connectors to drive the connection setup process.
|
|
60
|
-
#[must_use]
|
|
61
|
-
#[derive(Clone)]
|
|
62
|
-
pub struct ConnectRequest {
|
|
63
|
-
uri: Uri,
|
|
64
|
-
identifier: ConnectIdentity,
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// ===== impl ConnectRequest =====
|
|
68
|
-
|
|
69
|
-
impl ConnectRequest {
|
|
70
|
-
/// Create a new [`ConnectRequest`] with the given URI and identifier.
|
|
71
|
-
#[inline]
|
|
72
|
-
fn new<T>(uri: Uri, identifier: T) -> ConnectRequest
|
|
73
|
-
where
|
|
74
|
-
T: Into<Option<RequestOptions>>,
|
|
75
|
-
{
|
|
76
|
-
ConnectRequest {
|
|
77
|
-
uri: uri.clone(),
|
|
78
|
-
identifier: Arc::new(HashMemo::with_hasher(
|
|
79
|
-
ConnectExtra::new(uri, identifier),
|
|
80
|
-
HASHER,
|
|
81
|
-
)),
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/// Returns a reference to the [`Uri`].
|
|
86
|
-
#[inline]
|
|
87
|
-
pub fn uri(&self) -> &Uri {
|
|
88
|
-
&self.uri
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/// Returns a mutable reference to the [`Uri`].
|
|
92
|
-
#[inline]
|
|
93
|
-
pub fn uri_mut(&mut self) -> &mut Uri {
|
|
94
|
-
&mut self.uri
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/// Returns a unique [`ConnectIdentity`].
|
|
98
|
-
#[inline]
|
|
99
|
-
pub(crate) fn identify(&self) -> ConnectIdentity {
|
|
100
|
-
self.identifier.clone()
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/// Returns the [`ConnectExtra`] connection extra.
|
|
104
|
-
#[inline]
|
|
105
|
-
pub(crate) fn extra(&self) -> &ConnectExtra {
|
|
106
|
-
self.identifier.as_ref().as_ref()
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
53
|
/// A HttpClient to make outgoing HTTP requests.
|
|
111
54
|
///
|
|
112
55
|
/// `HttpClient` is cheap to clone and cloning is the recommended way to share a `HttpClient`. The
|
|
113
56
|
/// underlying connection pool will be reused.
|
|
114
57
|
#[must_use]
|
|
115
|
-
pub struct HttpClient<C, B> {
|
|
58
|
+
pub(crate) struct HttpClient<C, B> {
|
|
116
59
|
config: Config,
|
|
117
60
|
connector: C,
|
|
118
|
-
exec:
|
|
61
|
+
exec: Executor,
|
|
119
62
|
h1_builder: conn::http1::Builder,
|
|
120
|
-
h2_builder: conn::http2::Builder<
|
|
121
|
-
pool: pool::Pool<PoolClient<B>,
|
|
63
|
+
h2_builder: conn::http2::Builder<Executor>,
|
|
64
|
+
pool: pool::Pool<PoolClient<B>, ConnectionId>,
|
|
65
|
+
#[cfg(feature = "cookies")]
|
|
66
|
+
cookie_store: RequestConfig<Arc<dyn CookieStore>>,
|
|
122
67
|
}
|
|
123
68
|
|
|
124
69
|
#[derive(Clone, Copy)]
|
|
@@ -128,21 +73,71 @@ struct Config {
|
|
|
128
73
|
ver: Ver,
|
|
129
74
|
}
|
|
130
75
|
|
|
76
|
+
#[derive(Debug)]
|
|
77
|
+
pub struct Error {
|
|
78
|
+
kind: ErrorKind,
|
|
79
|
+
source: Option<BoxError>,
|
|
80
|
+
#[allow(unused)]
|
|
81
|
+
connect_info: Option<Connected>,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
#[derive(Debug)]
|
|
85
|
+
enum ErrorKind {
|
|
86
|
+
Canceled,
|
|
87
|
+
ChannelClosed,
|
|
88
|
+
Connect,
|
|
89
|
+
ProxyConnect,
|
|
90
|
+
UserUnsupportedRequestMethod,
|
|
91
|
+
UserUnsupportedVersion,
|
|
92
|
+
UserAbsoluteUriRequired,
|
|
93
|
+
SendRequest,
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
enum ClientConnectError {
|
|
97
|
+
Normal(Error),
|
|
98
|
+
CheckoutIsClosed(pool::Error),
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
#[allow(clippy::large_enum_variant)]
|
|
102
|
+
enum TrySendError<B> {
|
|
103
|
+
Retryable {
|
|
104
|
+
error: Error,
|
|
105
|
+
req: Request<B>,
|
|
106
|
+
connection_reused: bool,
|
|
107
|
+
},
|
|
108
|
+
Nope(Error),
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
macro_rules! e {
|
|
112
|
+
($kind:ident) => {
|
|
113
|
+
Error {
|
|
114
|
+
kind: ErrorKind::$kind,
|
|
115
|
+
source: None,
|
|
116
|
+
connect_info: None,
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
($kind:ident, $src:expr) => {
|
|
120
|
+
Error {
|
|
121
|
+
kind: ErrorKind::$kind,
|
|
122
|
+
source: Some($src.into()),
|
|
123
|
+
connect_info: None,
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
131
128
|
// ===== impl HttpClient =====
|
|
132
129
|
|
|
133
130
|
impl HttpClient<(), ()> {
|
|
134
|
-
/// Create a builder to configure a new `HttpClient
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
E: Executor<BoxSendFuture> + Send + Sync + Clone + 'static,
|
|
138
|
-
{
|
|
131
|
+
/// Create a builder to configure a new [`HttpClient`].
|
|
132
|
+
#[inline]
|
|
133
|
+
pub fn builder(executor: Executor) -> Builder {
|
|
139
134
|
Builder::new(executor)
|
|
140
135
|
}
|
|
141
136
|
}
|
|
142
137
|
|
|
143
138
|
impl<C, B> HttpClient<C, B>
|
|
144
139
|
where
|
|
145
|
-
C: tower::Service<
|
|
140
|
+
C: tower::Service<ConnectionDescriptor> + Clone + Send + Sync + 'static,
|
|
146
141
|
C::Response: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static,
|
|
147
142
|
C::Error: Into<BoxError>,
|
|
148
143
|
C::Future: Unpin + Send + 'static,
|
|
@@ -150,64 +145,69 @@ where
|
|
|
150
145
|
B::Data: Send,
|
|
151
146
|
B::Error: Into<BoxError>,
|
|
152
147
|
{
|
|
153
|
-
|
|
154
|
-
|
|
148
|
+
fn request(
|
|
149
|
+
&self,
|
|
150
|
+
mut req: Request<B>,
|
|
151
|
+
) -> BoxFuture<'static, Result<Response<Incoming>, BoxError>> {
|
|
155
152
|
let is_http_connect = req.method() == Method::CONNECT;
|
|
156
153
|
// Validate HTTP version early
|
|
157
154
|
match req.version() {
|
|
158
155
|
Version::HTTP_10 if is_http_connect => {
|
|
159
156
|
warn!("CONNECT is not allowed for HTTP/1.0");
|
|
160
|
-
return
|
|
161
|
-
ErrorKind::UserUnsupportedRequestMethod,
|
|
162
|
-
)));
|
|
157
|
+
return Box::pin(future::err(e!(UserUnsupportedRequestMethod).into()));
|
|
163
158
|
}
|
|
164
159
|
Version::HTTP_10 | Version::HTTP_11 | Version::HTTP_2 => {}
|
|
165
160
|
// completely unsupported HTTP version (like HTTP/0.9)!
|
|
166
161
|
_unsupported => {
|
|
167
162
|
warn!("Request has unsupported version: {:?}", _unsupported);
|
|
168
|
-
return
|
|
169
|
-
ErrorKind::UserUnsupportedVersion,
|
|
170
|
-
)));
|
|
163
|
+
return Box::pin(future::err(e!(UserUnsupportedVersion).into()));
|
|
171
164
|
}
|
|
172
165
|
};
|
|
173
166
|
|
|
174
167
|
// Extract and normalize URI
|
|
175
|
-
let uri = match
|
|
168
|
+
let uri = match normalize_uri(&mut req, is_http_connect) {
|
|
176
169
|
Ok(uri) => uri,
|
|
177
|
-
Err(err) =>
|
|
170
|
+
Err(err) => {
|
|
171
|
+
return Box::pin(future::err(e!(UserAbsoluteUriRequired, err).into()));
|
|
172
|
+
}
|
|
178
173
|
};
|
|
179
174
|
|
|
180
175
|
let mut this = self.clone();
|
|
181
176
|
|
|
182
|
-
// Extract per-request options from the request extensions and apply them to the client
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
177
|
+
// Extract per-request options from the request extensions and apply them to the client.
|
|
178
|
+
let descriptor = {
|
|
179
|
+
let RequestOptions {
|
|
180
|
+
group,
|
|
181
|
+
proxy,
|
|
182
|
+
version,
|
|
183
|
+
tls_options,
|
|
184
|
+
http1_options,
|
|
185
|
+
http2_options,
|
|
186
|
+
socket_bind_options,
|
|
187
|
+
} = RequestConfig::<RequestOptions>::remove(req.extensions_mut()).unwrap_or_default();
|
|
188
|
+
|
|
189
|
+
if let Some(opts) = http1_options {
|
|
190
|
+
this.h1_builder = this.h1_builder.options(opts);
|
|
191
191
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
this.h2_builder.options(opts.clone());
|
|
192
|
+
if let Some(opts) = http2_options {
|
|
193
|
+
this.h2_builder = this.h2_builder.options(opts);
|
|
195
194
|
}
|
|
196
|
-
}
|
|
197
195
|
|
|
198
|
-
|
|
199
|
-
|
|
196
|
+
ConnectionDescriptor::new(uri, group, proxy, version, tls_options, socket_bind_options)
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
Box::pin(this.send_request(req, descriptor).map_err(Into::into))
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
async fn send_request(
|
|
203
203
|
self,
|
|
204
204
|
mut req: Request<B>,
|
|
205
|
-
|
|
205
|
+
descriptor: ConnectionDescriptor,
|
|
206
206
|
) -> Result<Response<Incoming>, Error> {
|
|
207
207
|
let uri = req.uri().clone();
|
|
208
208
|
|
|
209
209
|
loop {
|
|
210
|
-
req = match self.try_send_request(req,
|
|
210
|
+
req = match self.try_send_request(req, descriptor.clone()).await {
|
|
211
211
|
Ok(resp) => return Ok(resp),
|
|
212
212
|
Err(TrySendError::Nope(err)) => return Err(err),
|
|
213
213
|
Err(TrySendError::Retryable {
|
|
@@ -235,41 +235,34 @@ where
|
|
|
235
235
|
async fn try_send_request(
|
|
236
236
|
&self,
|
|
237
237
|
mut req: Request<B>,
|
|
238
|
-
|
|
238
|
+
descriptor: ConnectionDescriptor,
|
|
239
239
|
) -> Result<Response<Incoming>, TrySendError<B>> {
|
|
240
240
|
let mut pooled = self
|
|
241
|
-
.connection_for(
|
|
241
|
+
.connection_for(descriptor)
|
|
242
242
|
.await
|
|
243
243
|
// `connection_for` already retries checkout errors, so if
|
|
244
244
|
// it returns an error, there's not much else to retry
|
|
245
245
|
.map_err(TrySendError::Nope)?;
|
|
246
246
|
|
|
247
|
+
let uri = req.uri().clone();
|
|
248
|
+
|
|
247
249
|
if pooled.is_http1() {
|
|
248
250
|
if req.version() == Version::HTTP_2 {
|
|
249
251
|
warn!("Connection is HTTP/1, but request requires HTTP/2");
|
|
250
252
|
return Err(TrySendError::Nope(
|
|
251
|
-
|
|
252
|
-
.with_connect_info(pooled.conn_info.clone()),
|
|
253
|
+
e!(UserUnsupportedVersion).with_connect_info(pooled.conn_info.clone()),
|
|
253
254
|
));
|
|
254
255
|
}
|
|
255
256
|
|
|
256
257
|
if self.config.set_host {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
if let Some(port) = util::get_non_default_port(&uri) {
|
|
261
|
-
let s = format!("{hostname}:{port}");
|
|
262
|
-
HeaderValue::from_maybe_shared(Bytes::from(s))
|
|
263
|
-
} else {
|
|
264
|
-
HeaderValue::from_str(hostname)
|
|
265
|
-
}
|
|
266
|
-
.expect("uri host is valid header value")
|
|
267
|
-
});
|
|
258
|
+
req.headers_mut()
|
|
259
|
+
.entry(HOST)
|
|
260
|
+
.or_insert_with(|| generate_host_header(&uri));
|
|
268
261
|
}
|
|
269
262
|
|
|
270
263
|
// CONNECT always sends authority-form, so check it first...
|
|
271
264
|
if req.method() == Method::CONNECT {
|
|
272
|
-
|
|
265
|
+
authority_form(req.uri_mut());
|
|
273
266
|
} else if pooled.conn_info.is_proxied() {
|
|
274
267
|
if let Some(auth) = pooled.conn_info.proxy_auth() {
|
|
275
268
|
req.headers_mut()
|
|
@@ -281,12 +274,40 @@ where
|
|
|
281
274
|
crate::util::replace_headers(req.headers_mut(), headers.clone());
|
|
282
275
|
}
|
|
283
276
|
|
|
284
|
-
|
|
277
|
+
absolute_form(req.uri_mut());
|
|
285
278
|
} else {
|
|
286
|
-
|
|
279
|
+
origin_form(req.uri_mut());
|
|
287
280
|
}
|
|
288
281
|
} else if req.method() == Method::CONNECT && !pooled.is_http2() {
|
|
289
|
-
|
|
282
|
+
authority_form(req.uri_mut());
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
#[cfg(feature = "cookies")]
|
|
286
|
+
let cookie_store = self.cookie_store.fetch(req.extensions()).cloned();
|
|
287
|
+
|
|
288
|
+
#[cfg(feature = "cookies")]
|
|
289
|
+
if let Some(ref cookie_store) = cookie_store {
|
|
290
|
+
let headers = req.headers_mut();
|
|
291
|
+
|
|
292
|
+
if !headers.contains_key(COOKIE) {
|
|
293
|
+
let version = if pooled.is_http2() {
|
|
294
|
+
Version::HTTP_2
|
|
295
|
+
} else {
|
|
296
|
+
Version::HTTP_11
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
match cookie_store.cookies(&uri, version) {
|
|
300
|
+
Cookies::Compressed(value) => {
|
|
301
|
+
headers.insert(COOKIE, value);
|
|
302
|
+
}
|
|
303
|
+
Cookies::Uncompressed(values) => {
|
|
304
|
+
for value in values {
|
|
305
|
+
headers.append(COOKIE, value);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
Cookies::Empty => (),
|
|
309
|
+
}
|
|
310
|
+
}
|
|
290
311
|
}
|
|
291
312
|
|
|
292
313
|
let mut res = match pooled.try_send_request(req).await {
|
|
@@ -308,9 +329,24 @@ where
|
|
|
308
329
|
}
|
|
309
330
|
};
|
|
310
331
|
|
|
332
|
+
#[cfg(feature = "cookies")]
|
|
333
|
+
if let Some(cookie_store) = cookie_store {
|
|
334
|
+
let mut cookies = res
|
|
335
|
+
.headers()
|
|
336
|
+
.get_all(http::header::SET_COOKIE)
|
|
337
|
+
.iter()
|
|
338
|
+
.peekable();
|
|
339
|
+
if cookies.peek().is_some() {
|
|
340
|
+
cookie_store.set_cookies(&mut cookies, &uri);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
311
344
|
// If the Connector included 'extra' info, add to Response...
|
|
312
345
|
pooled.conn_info.set_extras(res.extensions_mut());
|
|
313
346
|
|
|
347
|
+
// If the Connector included connection info, add to Response...
|
|
348
|
+
res.extensions_mut().insert(pooled.conn_info.clone());
|
|
349
|
+
|
|
314
350
|
// If pooled is HTTP/2, we can toss this reference immediately.
|
|
315
351
|
//
|
|
316
352
|
// when pooled is dropped, it will try to insert back into the
|
|
@@ -333,10 +369,10 @@ where
|
|
|
333
369
|
|
|
334
370
|
async fn connection_for(
|
|
335
371
|
&self,
|
|
336
|
-
|
|
337
|
-
) -> Result<pool::Pooled<PoolClient<B>,
|
|
372
|
+
descriptor: ConnectionDescriptor,
|
|
373
|
+
) -> Result<pool::Pooled<PoolClient<B>, ConnectionId>, Error> {
|
|
338
374
|
loop {
|
|
339
|
-
match self.one_connection_for(
|
|
375
|
+
match self.one_connection_for(descriptor.clone()).await {
|
|
340
376
|
Ok(pooled) => return Ok(pooled),
|
|
341
377
|
Err(ClientConnectError::Normal(err)) => return Err(err),
|
|
342
378
|
Err(ClientConnectError::CheckoutIsClosed(reason)) => {
|
|
@@ -356,12 +392,12 @@ where
|
|
|
356
392
|
|
|
357
393
|
async fn one_connection_for(
|
|
358
394
|
&self,
|
|
359
|
-
|
|
360
|
-
) -> Result<pool::Pooled<PoolClient<B>,
|
|
395
|
+
descriptor: ConnectionDescriptor,
|
|
396
|
+
) -> Result<pool::Pooled<PoolClient<B>, ConnectionId>, ClientConnectError> {
|
|
361
397
|
// Return a single connection if pooling is not enabled
|
|
362
398
|
if !self.pool.is_enabled() {
|
|
363
399
|
return self
|
|
364
|
-
.connect_to(
|
|
400
|
+
.connect_to(descriptor)
|
|
365
401
|
.await
|
|
366
402
|
.map_err(ClientConnectError::Normal);
|
|
367
403
|
}
|
|
@@ -376,8 +412,8 @@ where
|
|
|
376
412
|
// - If a new connection is started, but the Checkout wins after (an idle connection became
|
|
377
413
|
// available first), the started connection future is spawned into the runtime to
|
|
378
414
|
// complete, and then be inserted into the pool as an idle connection.
|
|
379
|
-
let checkout = self.pool.checkout(
|
|
380
|
-
let connect = self.connect_to(
|
|
415
|
+
let checkout = self.pool.checkout(descriptor.id());
|
|
416
|
+
let connect = self.connect_to(descriptor);
|
|
381
417
|
let is_ver_h2 = self.config.ver == Ver::Http2;
|
|
382
418
|
|
|
383
419
|
// The order of the `select` is depended on below...
|
|
@@ -447,8 +483,8 @@ where
|
|
|
447
483
|
|
|
448
484
|
fn connect_to(
|
|
449
485
|
&self,
|
|
450
|
-
|
|
451
|
-
) -> impl Lazy<Output = Result<pool::Pooled<PoolClient<B>,
|
|
486
|
+
descriptor: ConnectionDescriptor,
|
|
487
|
+
) -> impl Lazy<Output = Result<pool::Pooled<PoolClient<B>, ConnectionId>, Error>>
|
|
452
488
|
+ Send
|
|
453
489
|
+ Unpin
|
|
454
490
|
+ 'static {
|
|
@@ -457,8 +493,8 @@ where
|
|
|
457
493
|
|
|
458
494
|
let h1_builder = self.h1_builder.clone();
|
|
459
495
|
let h2_builder = self.h2_builder.clone();
|
|
460
|
-
let ver = match
|
|
461
|
-
Some(
|
|
496
|
+
let ver = match descriptor.version() {
|
|
497
|
+
Some(Version::HTTP_2) => Ver::Http2,
|
|
462
498
|
_ => self.config.ver,
|
|
463
499
|
};
|
|
464
500
|
let is_ver_h2 = ver == Ver::Http2;
|
|
@@ -469,16 +505,15 @@ where
|
|
|
469
505
|
// If the pool_key is for HTTP/2, and there is already a
|
|
470
506
|
// connection being established, then this can't take a
|
|
471
507
|
// second lock. The "connect_to" future is Canceled.
|
|
472
|
-
let connecting = match pool.connecting(
|
|
508
|
+
let connecting = match pool.connecting(descriptor.id(), ver) {
|
|
473
509
|
Some(lock) => lock,
|
|
474
510
|
None => {
|
|
475
|
-
let canceled = Error::new_kind(ErrorKind::Canceled);
|
|
476
511
|
// HTTP/2 connection in progress.
|
|
477
|
-
return Either::Right(futures_util::future::err(
|
|
512
|
+
return Either::Right(futures_util::future::err(e!(Canceled)));
|
|
478
513
|
}
|
|
479
514
|
};
|
|
480
515
|
Either::Left(
|
|
481
|
-
Oneshot::new(connector,
|
|
516
|
+
Oneshot::new(connector, descriptor)
|
|
482
517
|
.map_err(|src| Error::new(ErrorKind::Connect, src))
|
|
483
518
|
.and_then(move |io| {
|
|
484
519
|
let connected = io.connected();
|
|
@@ -629,7 +664,7 @@ where
|
|
|
629
664
|
|
|
630
665
|
impl<C, B> tower::Service<Request<B>> for HttpClient<C, B>
|
|
631
666
|
where
|
|
632
|
-
C: tower::Service<
|
|
667
|
+
C: tower::Service<ConnectionDescriptor> + Clone + Send + Sync + 'static,
|
|
633
668
|
C::Response: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static,
|
|
634
669
|
C::Error: Into<BoxError>,
|
|
635
670
|
C::Future: Unpin + Send + 'static,
|
|
@@ -638,31 +673,8 @@ where
|
|
|
638
673
|
B::Error: Into<BoxError>,
|
|
639
674
|
{
|
|
640
675
|
type Response = Response<Incoming>;
|
|
641
|
-
type Error =
|
|
642
|
-
type Future =
|
|
643
|
-
|
|
644
|
-
fn poll_ready(&mut self, _: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
|
|
645
|
-
Poll::Ready(Ok(()))
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
fn call(&mut self, req: Request<B>) -> Self::Future {
|
|
649
|
-
self.request(req)
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
impl<C, B> tower::Service<Request<B>> for &'_ HttpClient<C, B>
|
|
654
|
-
where
|
|
655
|
-
C: tower::Service<ConnectRequest> + Clone + Send + Sync + 'static,
|
|
656
|
-
C::Response: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static,
|
|
657
|
-
C::Error: Into<BoxError>,
|
|
658
|
-
C::Future: Unpin + Send + 'static,
|
|
659
|
-
B: Body + Send + 'static + Unpin,
|
|
660
|
-
B::Data: Send,
|
|
661
|
-
B::Error: Into<BoxError>,
|
|
662
|
-
{
|
|
663
|
-
type Response = Response<Incoming>;
|
|
664
|
-
type Error = Error;
|
|
665
|
-
type Future = ResponseFuture;
|
|
676
|
+
type Error = BoxError;
|
|
677
|
+
type Future = BoxFuture<'static, Result<Response<Incoming>, Self::Error>>;
|
|
666
678
|
|
|
667
679
|
fn poll_ready(&mut self, _: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
|
|
668
680
|
Poll::Ready(Ok(()))
|
|
@@ -682,6 +694,8 @@ impl<C: Clone, B> Clone for HttpClient<C, B> {
|
|
|
682
694
|
h2_builder: self.h2_builder.clone(),
|
|
683
695
|
connector: self.connector.clone(),
|
|
684
696
|
pool: self.pool.clone(),
|
|
697
|
+
#[cfg(feature = "cookies")]
|
|
698
|
+
cookie_store: self.cookie_store.clone(),
|
|
685
699
|
}
|
|
686
700
|
}
|
|
687
701
|
}
|
|
@@ -700,40 +714,43 @@ enum PoolTx<B> {
|
|
|
700
714
|
// ===== impl PoolClient =====
|
|
701
715
|
|
|
702
716
|
impl<B> PoolClient<B> {
|
|
717
|
+
#[inline]
|
|
703
718
|
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Error>> {
|
|
704
719
|
match self.tx {
|
|
705
720
|
PoolTx::Http1(ref mut tx) => tx.poll_ready(cx).map_err(Error::closed),
|
|
706
|
-
|
|
707
721
|
PoolTx::Http2(_) => Poll::Ready(Ok(())),
|
|
708
722
|
}
|
|
709
723
|
}
|
|
710
724
|
|
|
725
|
+
#[inline]
|
|
711
726
|
fn is_http1(&self) -> bool {
|
|
712
727
|
!self.is_http2()
|
|
713
728
|
}
|
|
714
729
|
|
|
730
|
+
#[inline]
|
|
715
731
|
fn is_http2(&self) -> bool {
|
|
716
732
|
match self.tx {
|
|
717
733
|
PoolTx::Http1(_) => false,
|
|
718
|
-
|
|
719
734
|
PoolTx::Http2(_) => true,
|
|
720
735
|
}
|
|
721
736
|
}
|
|
722
737
|
|
|
738
|
+
#[inline]
|
|
723
739
|
fn is_poisoned(&self) -> bool {
|
|
724
740
|
self.conn_info.poisoned()
|
|
725
741
|
}
|
|
726
742
|
|
|
743
|
+
#[inline]
|
|
727
744
|
fn is_ready(&self) -> bool {
|
|
728
745
|
match self.tx {
|
|
729
746
|
PoolTx::Http1(ref tx) => tx.is_ready(),
|
|
730
|
-
|
|
731
747
|
PoolTx::Http2(ref tx) => tx.is_ready(),
|
|
732
748
|
}
|
|
733
749
|
}
|
|
734
750
|
}
|
|
735
751
|
|
|
736
752
|
impl<B: Body + 'static> PoolClient<B> {
|
|
753
|
+
#[inline]
|
|
737
754
|
fn try_send_request(
|
|
738
755
|
&mut self,
|
|
739
756
|
req: Request<B>,
|
|
@@ -752,6 +769,7 @@ impl<B> pool::Poolable for PoolClient<B>
|
|
|
752
769
|
where
|
|
753
770
|
B: Send + 'static,
|
|
754
771
|
{
|
|
772
|
+
#[inline]
|
|
755
773
|
fn is_open(&self) -> bool {
|
|
756
774
|
!self.is_poisoned() && self.is_ready()
|
|
757
775
|
}
|
|
@@ -777,61 +795,30 @@ where
|
|
|
777
795
|
}
|
|
778
796
|
}
|
|
779
797
|
|
|
798
|
+
#[inline]
|
|
780
799
|
fn can_share(&self) -> bool {
|
|
781
800
|
self.is_http2()
|
|
782
801
|
}
|
|
783
802
|
}
|
|
784
803
|
|
|
785
|
-
/// A `Future` that will resolve to an HTTP Response.
|
|
786
|
-
#[must_use = "futures do nothing unless polled"]
|
|
787
|
-
pub struct ResponseFuture {
|
|
788
|
-
inner: Pin<Box<dyn Future<Output = Result<Response<Incoming>, Error>> + Send>>,
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
// ===== impl ResponseFuture =====
|
|
792
|
-
|
|
793
|
-
impl ResponseFuture {
|
|
794
|
-
#[inline]
|
|
795
|
-
pub(super) fn new<F>(value: F) -> ResponseFuture
|
|
796
|
-
where
|
|
797
|
-
F: Future<Output = Result<Response<Incoming>, Error>> + Send + 'static,
|
|
798
|
-
{
|
|
799
|
-
ResponseFuture {
|
|
800
|
-
inner: Box::pin(value),
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
impl Future for ResponseFuture {
|
|
806
|
-
type Output = Result<Response<Incoming>, Error>;
|
|
807
|
-
|
|
808
|
-
#[inline]
|
|
809
|
-
fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
|
|
810
|
-
self.inner.as_mut().poll(cx)
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
|
|
814
804
|
/// A builder to configure a new [`HttpClient`].
|
|
815
805
|
#[derive(Clone)]
|
|
816
806
|
pub struct Builder {
|
|
817
807
|
config: Config,
|
|
818
|
-
exec:
|
|
819
|
-
|
|
808
|
+
exec: Executor,
|
|
820
809
|
h1_builder: conn::http1::Builder,
|
|
821
|
-
h2_builder: conn::http2::Builder<
|
|
810
|
+
h2_builder: conn::http2::Builder<Executor>,
|
|
822
811
|
pool_config: pool::Config,
|
|
823
|
-
pool_timer:
|
|
812
|
+
pool_timer: Timer,
|
|
813
|
+
#[cfg(feature = "cookies")]
|
|
814
|
+
cookie_store: Option<Arc<dyn CookieStore>>,
|
|
824
815
|
}
|
|
825
816
|
|
|
826
817
|
// ===== impl Builder =====
|
|
827
818
|
|
|
828
819
|
impl Builder {
|
|
829
820
|
/// Construct a new Builder.
|
|
830
|
-
pub fn new
|
|
831
|
-
where
|
|
832
|
-
E: Executor<BoxSendFuture> + Send + Sync + Clone + 'static,
|
|
833
|
-
{
|
|
834
|
-
let exec = Exec::new(executor);
|
|
821
|
+
pub fn new(exec: Executor) -> Self {
|
|
835
822
|
Self {
|
|
836
823
|
config: Config {
|
|
837
824
|
retry_canceled_requests: true,
|
|
@@ -839,17 +826,19 @@ impl Builder {
|
|
|
839
826
|
ver: Ver::Auto,
|
|
840
827
|
},
|
|
841
828
|
exec: exec.clone(),
|
|
842
|
-
|
|
843
|
-
h1_builder: conn::http1::Builder::new(),
|
|
829
|
+
h1_builder: conn::http1::Builder::default(),
|
|
844
830
|
h2_builder: conn::http2::Builder::new(exec),
|
|
845
831
|
pool_config: pool::Config {
|
|
846
832
|
idle_timeout: Some(Duration::from_secs(90)),
|
|
847
833
|
max_idle_per_host: usize::MAX,
|
|
848
834
|
max_pool_size: None,
|
|
849
835
|
},
|
|
850
|
-
pool_timer:
|
|
836
|
+
pool_timer: Timer::default(),
|
|
837
|
+
#[cfg(feature = "cookies")]
|
|
838
|
+
cookie_store: None,
|
|
851
839
|
}
|
|
852
840
|
}
|
|
841
|
+
|
|
853
842
|
/// Set an optional timeout for idle sockets being kept-alive.
|
|
854
843
|
/// A `Timer` is required for this to take effect. See `Builder::pool_timer`
|
|
855
844
|
///
|
|
@@ -878,7 +867,7 @@ impl Builder {
|
|
|
878
867
|
///
|
|
879
868
|
/// Default is `None` (no limit).
|
|
880
869
|
#[inline]
|
|
881
|
-
pub fn pool_max_size(mut self, max_size: impl Into<Option<
|
|
870
|
+
pub fn pool_max_size(mut self, max_size: impl Into<Option<NonZeroUsize>>) -> Self {
|
|
882
871
|
self.pool_config.max_pool_size = max_size.into();
|
|
883
872
|
self
|
|
884
873
|
}
|
|
@@ -906,11 +895,8 @@ impl Builder {
|
|
|
906
895
|
///
|
|
907
896
|
/// [`http2::client::Builder::timer`]: https://docs.rs/http2/latest/http2/client/struct.Builder.html#method.timer
|
|
908
897
|
#[inline]
|
|
909
|
-
pub fn http2_timer
|
|
910
|
-
|
|
911
|
-
M: Timer + Send + Sync + 'static,
|
|
912
|
-
{
|
|
913
|
-
self.h2_builder.timer(timer);
|
|
898
|
+
pub fn http2_timer(mut self, timer: Timer) -> Self {
|
|
899
|
+
self.h2_builder = self.h2_builder.timer(timer);
|
|
914
900
|
self
|
|
915
901
|
}
|
|
916
902
|
|
|
@@ -921,7 +907,7 @@ impl Builder {
|
|
|
921
907
|
O: Into<Option<Http1Options>>,
|
|
922
908
|
{
|
|
923
909
|
if let Some(opts) = opts.into() {
|
|
924
|
-
self.h1_builder.options(opts);
|
|
910
|
+
self.h1_builder = self.h1_builder.options(opts);
|
|
925
911
|
}
|
|
926
912
|
|
|
927
913
|
self
|
|
@@ -934,54 +920,30 @@ impl Builder {
|
|
|
934
920
|
O: Into<Option<Http2Options>>,
|
|
935
921
|
{
|
|
936
922
|
if let Some(opts) = opts.into() {
|
|
937
|
-
self.h2_builder.options(opts);
|
|
923
|
+
self.h2_builder = self.h2_builder.options(opts);
|
|
938
924
|
}
|
|
939
925
|
self
|
|
940
926
|
}
|
|
941
927
|
|
|
942
928
|
/// Provide a timer to be used for timeouts and intervals in connection pools.
|
|
943
929
|
#[inline]
|
|
944
|
-
pub fn pool_timer
|
|
945
|
-
|
|
946
|
-
M: Timer + Clone + Send + Sync + 'static,
|
|
947
|
-
{
|
|
948
|
-
self.pool_timer = Some(ArcTimer::new(timer));
|
|
949
|
-
self
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
/// Set whether to retry requests that get disrupted before ever starting
|
|
953
|
-
/// to write.
|
|
954
|
-
///
|
|
955
|
-
/// This means a request that is queued, and gets given an idle, reused
|
|
956
|
-
/// connection, and then encounters an error immediately as the idle
|
|
957
|
-
/// connection was found to be unusable.
|
|
958
|
-
///
|
|
959
|
-
/// When this is set to `false`, the related `ResponseFuture` would instead
|
|
960
|
-
/// resolve to an `Error::Cancel`.
|
|
961
|
-
///
|
|
962
|
-
/// Default is `true`.
|
|
963
|
-
#[inline]
|
|
964
|
-
pub fn retry_canceled_requests(mut self, val: bool) -> Self {
|
|
965
|
-
self.config.retry_canceled_requests = val;
|
|
930
|
+
pub fn pool_timer(mut self, timer: Timer) -> Self {
|
|
931
|
+
self.pool_timer = timer;
|
|
966
932
|
self
|
|
967
933
|
}
|
|
968
934
|
|
|
969
|
-
///
|
|
970
|
-
///
|
|
971
|
-
/// If true, and a request does not include a `Host` header, one will be
|
|
972
|
-
/// added automatically, derived from the authority of the `Uri`.
|
|
973
|
-
///
|
|
974
|
-
/// Default is `true`.
|
|
935
|
+
/// Provide a cookie store for automatic cookie management.
|
|
975
936
|
#[inline]
|
|
976
|
-
|
|
977
|
-
|
|
937
|
+
#[cfg(feature = "cookies")]
|
|
938
|
+
pub fn cookie_store(mut self, cookie_store: Option<Arc<dyn CookieStore>>) -> Self {
|
|
939
|
+
self.cookie_store = cookie_store;
|
|
978
940
|
self
|
|
979
941
|
}
|
|
980
942
|
|
|
981
943
|
/// Combine the configuration of this builder with a connector to create a `HttpClient`.
|
|
982
944
|
pub fn build<C, B>(self, connector: C) -> HttpClient<C, B>
|
|
983
945
|
where
|
|
984
|
-
C: tower::Service<
|
|
946
|
+
C: tower::Service<ConnectionDescriptor> + Clone + Send + Sync + 'static,
|
|
985
947
|
C::Response: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static,
|
|
986
948
|
C::Error: Into<BoxError>,
|
|
987
949
|
C::Future: Unpin + Send + 'static,
|
|
@@ -993,11 +955,195 @@ impl Builder {
|
|
|
993
955
|
HttpClient {
|
|
994
956
|
config: self.config,
|
|
995
957
|
exec: exec.clone(),
|
|
996
|
-
|
|
958
|
+
connector,
|
|
997
959
|
h1_builder: self.h1_builder,
|
|
998
960
|
h2_builder: self.h2_builder,
|
|
999
|
-
connector,
|
|
1000
961
|
pool: pool::Pool::new(self.pool_config, exec, timer),
|
|
962
|
+
#[cfg(feature = "cookies")]
|
|
963
|
+
cookie_store: RequestConfig::new(self.cookie_store),
|
|
1001
964
|
}
|
|
1002
965
|
}
|
|
1003
966
|
}
|
|
967
|
+
|
|
968
|
+
// ==== impl Error ====
|
|
969
|
+
|
|
970
|
+
impl fmt::Display for Error {
|
|
971
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
972
|
+
write!(f, "client error ({:?})", self.kind)
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
impl StdError for Error {
|
|
977
|
+
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
|
978
|
+
self.source.as_ref().map(|e| &**e as _)
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
impl Error {
|
|
983
|
+
fn new<E>(kind: ErrorKind, error: E) -> Self
|
|
984
|
+
where
|
|
985
|
+
E: Into<BoxError>,
|
|
986
|
+
{
|
|
987
|
+
let error = error.into();
|
|
988
|
+
let kind = if error.is::<proxy::tunnel::TunnelError>() || error.is::<ProxyConnect>() || {
|
|
989
|
+
#[cfg(feature = "socks")]
|
|
990
|
+
{
|
|
991
|
+
error.is::<proxy::socks::SocksError>()
|
|
992
|
+
}
|
|
993
|
+
#[cfg(not(feature = "socks"))]
|
|
994
|
+
{
|
|
995
|
+
false
|
|
996
|
+
}
|
|
997
|
+
} {
|
|
998
|
+
ErrorKind::ProxyConnect
|
|
999
|
+
} else {
|
|
1000
|
+
kind
|
|
1001
|
+
};
|
|
1002
|
+
|
|
1003
|
+
Self {
|
|
1004
|
+
kind,
|
|
1005
|
+
source: Some(error),
|
|
1006
|
+
connect_info: None,
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
/// Returns true if this was an error from [`ErrorKind::Connect`].
|
|
1011
|
+
#[inline]
|
|
1012
|
+
pub fn is_connect(&self) -> bool {
|
|
1013
|
+
matches!(self.kind, ErrorKind::Connect)
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
/// Returns true if this was an error from [`ErrorKind::ProxyConnect`].
|
|
1017
|
+
#[inline]
|
|
1018
|
+
pub fn is_proxy_connect(&self) -> bool {
|
|
1019
|
+
matches!(self.kind, ErrorKind::ProxyConnect)
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
#[inline]
|
|
1023
|
+
fn with_connect_info(self, connect_info: Connected) -> Self {
|
|
1024
|
+
Self {
|
|
1025
|
+
connect_info: Some(connect_info),
|
|
1026
|
+
..self
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
#[inline]
|
|
1031
|
+
fn is_canceled(&self) -> bool {
|
|
1032
|
+
matches!(self.kind, ErrorKind::Canceled)
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
#[inline]
|
|
1036
|
+
fn tx(src: wreq_proto::Error) -> Self {
|
|
1037
|
+
Self::new(ErrorKind::SendRequest, src)
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
#[inline]
|
|
1041
|
+
fn closed(src: wreq_proto::Error) -> Self {
|
|
1042
|
+
Self::new(ErrorKind::ChannelClosed, src)
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
fn origin_form(uri: &mut Uri) {
|
|
1047
|
+
let path = match uri.path_and_query() {
|
|
1048
|
+
Some(path) if path.as_str() != "/" => {
|
|
1049
|
+
let mut parts = ::http::uri::Parts::default();
|
|
1050
|
+
parts.path_and_query.replace(path.clone());
|
|
1051
|
+
Uri::from_parts(parts).expect("path is valid uri")
|
|
1052
|
+
}
|
|
1053
|
+
_none_or_just_slash => {
|
|
1054
|
+
debug_assert!(Uri::default() == "/");
|
|
1055
|
+
Uri::default()
|
|
1056
|
+
}
|
|
1057
|
+
};
|
|
1058
|
+
*uri = path
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
fn absolute_form(uri: &mut Uri) {
|
|
1062
|
+
debug_assert!(uri.scheme().is_some(), "absolute_form needs a scheme");
|
|
1063
|
+
debug_assert!(
|
|
1064
|
+
uri.authority().is_some(),
|
|
1065
|
+
"absolute_form needs an authority"
|
|
1066
|
+
);
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
fn authority_form(uri: &mut Uri) {
|
|
1070
|
+
if let Some(path) = uri.path_and_query() {
|
|
1071
|
+
// `https://hyper.rs` would parse with `/` path, don't
|
|
1072
|
+
// annoy people about that...
|
|
1073
|
+
if path != "/" {
|
|
1074
|
+
warn!("HTTP/1.1 CONNECT request stripping path: {:?}", path);
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
*uri = match uri.authority() {
|
|
1078
|
+
Some(auth) => {
|
|
1079
|
+
let mut parts = ::http::uri::Parts::default();
|
|
1080
|
+
parts.authority = Some(auth.clone());
|
|
1081
|
+
Uri::from_parts(parts).expect("authority is valid")
|
|
1082
|
+
}
|
|
1083
|
+
None => {
|
|
1084
|
+
unreachable!("authority_form with relative uri");
|
|
1085
|
+
}
|
|
1086
|
+
};
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
fn normalize_uri<B>(req: &mut Request<B>, is_http_connect: bool) -> Result<Uri, Error> {
|
|
1090
|
+
let uri = req.uri().clone();
|
|
1091
|
+
|
|
1092
|
+
let build_base_uri = |scheme: Scheme, authority: Authority| {
|
|
1093
|
+
Uri::builder()
|
|
1094
|
+
.scheme(scheme)
|
|
1095
|
+
.authority(authority)
|
|
1096
|
+
.path_and_query(PathAndQuery::from_static("/"))
|
|
1097
|
+
.build()
|
|
1098
|
+
.expect("valid base URI")
|
|
1099
|
+
};
|
|
1100
|
+
|
|
1101
|
+
match (uri.scheme(), uri.authority()) {
|
|
1102
|
+
(Some(scheme), Some(auth)) => Ok(build_base_uri(scheme.clone(), auth.clone())),
|
|
1103
|
+
(None, Some(auth)) if is_http_connect => {
|
|
1104
|
+
let scheme = match auth.port_u16() {
|
|
1105
|
+
Some(443) => Scheme::HTTPS,
|
|
1106
|
+
_ => Scheme::HTTP,
|
|
1107
|
+
};
|
|
1108
|
+
set_scheme(req.uri_mut(), scheme.clone());
|
|
1109
|
+
Ok(build_base_uri(scheme, auth.clone()))
|
|
1110
|
+
}
|
|
1111
|
+
_ => {
|
|
1112
|
+
debug!("Client requires absolute-form URIs, received: {:?}", uri);
|
|
1113
|
+
Err(e!(UserAbsoluteUriRequired))
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
fn generate_host_header(uri: &Uri) -> HeaderValue {
|
|
1119
|
+
let hostname = uri.host().expect("authority implies host");
|
|
1120
|
+
let port = match (uri.port().map(|p| p.as_u16()), is_schema_secure(uri)) {
|
|
1121
|
+
(Some(443), true) | (Some(80), false) => None,
|
|
1122
|
+
_ => uri.port(),
|
|
1123
|
+
};
|
|
1124
|
+
if let Some(port) = port {
|
|
1125
|
+
let host = format!("{hostname}:{port}");
|
|
1126
|
+
HeaderValue::from_maybe_shared(Bytes::from(host))
|
|
1127
|
+
} else {
|
|
1128
|
+
HeaderValue::from_str(hostname)
|
|
1129
|
+
}
|
|
1130
|
+
.expect("uri host is valid header value")
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
fn set_scheme(uri: &mut Uri, scheme: Scheme) {
|
|
1134
|
+
debug_assert!(
|
|
1135
|
+
uri.scheme().is_none(),
|
|
1136
|
+
"set_scheme expects no existing scheme"
|
|
1137
|
+
);
|
|
1138
|
+
let old = std::mem::take(uri);
|
|
1139
|
+
let mut parts: ::http::uri::Parts = old.into();
|
|
1140
|
+
parts.scheme = Some(scheme);
|
|
1141
|
+
parts.path_and_query = Some(PathAndQuery::from_static("/"));
|
|
1142
|
+
*uri = Uri::from_parts(parts).expect("scheme is valid");
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
fn is_schema_secure(uri: &Uri) -> bool {
|
|
1146
|
+
uri.scheme_str()
|
|
1147
|
+
.map(|scheme_str| matches!(scheme_str, "wss" | "https"))
|
|
1148
|
+
.unwrap_or_default()
|
|
1149
|
+
}
|