wreq-rb 0.3.0
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 +7 -0
- data/Cargo.lock +2688 -0
- data/Cargo.toml +6 -0
- data/README.md +179 -0
- data/ext/wreq_rb/Cargo.toml +39 -0
- data/ext/wreq_rb/extconf.rb +22 -0
- data/ext/wreq_rb/src/client.rs +565 -0
- data/ext/wreq_rb/src/error.rs +25 -0
- data/ext/wreq_rb/src/lib.rs +20 -0
- data/ext/wreq_rb/src/response.rs +132 -0
- data/lib/wreq-rb/version.rb +5 -0
- data/lib/wreq-rb.rb +17 -0
- data/patches/0001-add-transfer-size-tracking.patch +292 -0
- data/vendor/wreq/Cargo.toml +306 -0
- data/vendor/wreq/LICENSE +202 -0
- data/vendor/wreq/README.md +122 -0
- data/vendor/wreq/examples/cert_store.rs +77 -0
- data/vendor/wreq/examples/connect_via_lower_priority_tokio_runtime.rs +258 -0
- data/vendor/wreq/examples/emulation.rs +118 -0
- data/vendor/wreq/examples/form.rs +14 -0
- data/vendor/wreq/examples/http1_websocket.rs +37 -0
- data/vendor/wreq/examples/http2_websocket.rs +45 -0
- data/vendor/wreq/examples/json_dynamic.rs +41 -0
- data/vendor/wreq/examples/json_typed.rs +47 -0
- data/vendor/wreq/examples/keylog.rs +16 -0
- data/vendor/wreq/examples/request_with_emulation.rs +115 -0
- data/vendor/wreq/examples/request_with_interface.rs +37 -0
- data/vendor/wreq/examples/request_with_local_address.rs +16 -0
- data/vendor/wreq/examples/request_with_proxy.rs +13 -0
- data/vendor/wreq/examples/request_with_redirect.rs +22 -0
- data/vendor/wreq/examples/request_with_version.rs +15 -0
- data/vendor/wreq/examples/tor_socks.rs +24 -0
- data/vendor/wreq/examples/unix_socket.rs +33 -0
- data/vendor/wreq/src/client/body.rs +304 -0
- data/vendor/wreq/src/client/conn/conn.rs +231 -0
- data/vendor/wreq/src/client/conn/connector.rs +549 -0
- data/vendor/wreq/src/client/conn/http.rs +1023 -0
- data/vendor/wreq/src/client/conn/proxy/socks.rs +233 -0
- data/vendor/wreq/src/client/conn/proxy/tunnel.rs +260 -0
- data/vendor/wreq/src/client/conn/proxy.rs +39 -0
- data/vendor/wreq/src/client/conn/tls_info.rs +98 -0
- data/vendor/wreq/src/client/conn/uds.rs +44 -0
- data/vendor/wreq/src/client/conn/verbose.rs +149 -0
- data/vendor/wreq/src/client/conn.rs +323 -0
- data/vendor/wreq/src/client/core/body/incoming.rs +485 -0
- data/vendor/wreq/src/client/core/body/length.rs +118 -0
- data/vendor/wreq/src/client/core/body.rs +34 -0
- data/vendor/wreq/src/client/core/common/buf.rs +149 -0
- data/vendor/wreq/src/client/core/common/rewind.rs +141 -0
- data/vendor/wreq/src/client/core/common/watch.rs +76 -0
- data/vendor/wreq/src/client/core/common.rs +3 -0
- data/vendor/wreq/src/client/core/conn/http1.rs +342 -0
- data/vendor/wreq/src/client/core/conn/http2.rs +307 -0
- data/vendor/wreq/src/client/core/conn.rs +11 -0
- data/vendor/wreq/src/client/core/dispatch.rs +299 -0
- data/vendor/wreq/src/client/core/error.rs +435 -0
- data/vendor/wreq/src/client/core/ext.rs +201 -0
- data/vendor/wreq/src/client/core/http1.rs +178 -0
- data/vendor/wreq/src/client/core/http2.rs +483 -0
- data/vendor/wreq/src/client/core/proto/h1/conn.rs +988 -0
- data/vendor/wreq/src/client/core/proto/h1/decode.rs +1170 -0
- data/vendor/wreq/src/client/core/proto/h1/dispatch.rs +684 -0
- data/vendor/wreq/src/client/core/proto/h1/encode.rs +580 -0
- data/vendor/wreq/src/client/core/proto/h1/io.rs +879 -0
- data/vendor/wreq/src/client/core/proto/h1/role.rs +694 -0
- data/vendor/wreq/src/client/core/proto/h1.rs +104 -0
- data/vendor/wreq/src/client/core/proto/h2/client.rs +650 -0
- data/vendor/wreq/src/client/core/proto/h2/ping.rs +539 -0
- data/vendor/wreq/src/client/core/proto/h2.rs +379 -0
- data/vendor/wreq/src/client/core/proto/headers.rs +138 -0
- data/vendor/wreq/src/client/core/proto.rs +58 -0
- data/vendor/wreq/src/client/core/rt/bounds.rs +57 -0
- data/vendor/wreq/src/client/core/rt/timer.rs +150 -0
- data/vendor/wreq/src/client/core/rt/tokio.rs +99 -0
- data/vendor/wreq/src/client/core/rt.rs +25 -0
- data/vendor/wreq/src/client/core/upgrade.rs +267 -0
- data/vendor/wreq/src/client/core.rs +16 -0
- data/vendor/wreq/src/client/emulation.rs +161 -0
- data/vendor/wreq/src/client/http/client/error.rs +142 -0
- data/vendor/wreq/src/client/http/client/exec.rs +29 -0
- data/vendor/wreq/src/client/http/client/extra.rs +77 -0
- data/vendor/wreq/src/client/http/client/lazy.rs +79 -0
- data/vendor/wreq/src/client/http/client/pool.rs +1105 -0
- data/vendor/wreq/src/client/http/client/util.rs +104 -0
- data/vendor/wreq/src/client/http/client.rs +1003 -0
- data/vendor/wreq/src/client/http/future.rs +99 -0
- data/vendor/wreq/src/client/http.rs +1629 -0
- data/vendor/wreq/src/client/layer/config/options.rs +156 -0
- data/vendor/wreq/src/client/layer/config.rs +116 -0
- data/vendor/wreq/src/client/layer/cookie.rs +161 -0
- data/vendor/wreq/src/client/layer/decoder.rs +139 -0
- data/vendor/wreq/src/client/layer/redirect/future.rs +270 -0
- data/vendor/wreq/src/client/layer/redirect/policy.rs +63 -0
- data/vendor/wreq/src/client/layer/redirect.rs +145 -0
- data/vendor/wreq/src/client/layer/retry/classify.rs +105 -0
- data/vendor/wreq/src/client/layer/retry/scope.rs +51 -0
- data/vendor/wreq/src/client/layer/retry.rs +151 -0
- data/vendor/wreq/src/client/layer/timeout/body.rs +233 -0
- data/vendor/wreq/src/client/layer/timeout/future.rs +90 -0
- data/vendor/wreq/src/client/layer/timeout.rs +177 -0
- data/vendor/wreq/src/client/layer.rs +15 -0
- data/vendor/wreq/src/client/multipart.rs +717 -0
- data/vendor/wreq/src/client/request.rs +818 -0
- data/vendor/wreq/src/client/response.rs +534 -0
- data/vendor/wreq/src/client/ws/json.rs +99 -0
- data/vendor/wreq/src/client/ws/message.rs +453 -0
- data/vendor/wreq/src/client/ws.rs +714 -0
- data/vendor/wreq/src/client.rs +27 -0
- data/vendor/wreq/src/config.rs +140 -0
- data/vendor/wreq/src/cookie.rs +579 -0
- data/vendor/wreq/src/dns/gai.rs +249 -0
- data/vendor/wreq/src/dns/hickory.rs +78 -0
- data/vendor/wreq/src/dns/resolve.rs +180 -0
- data/vendor/wreq/src/dns.rs +69 -0
- data/vendor/wreq/src/error.rs +502 -0
- data/vendor/wreq/src/ext.rs +398 -0
- data/vendor/wreq/src/hash.rs +143 -0
- data/vendor/wreq/src/header.rs +506 -0
- data/vendor/wreq/src/into_uri.rs +187 -0
- data/vendor/wreq/src/lib.rs +586 -0
- data/vendor/wreq/src/proxy/mac.rs +82 -0
- data/vendor/wreq/src/proxy/matcher.rs +806 -0
- data/vendor/wreq/src/proxy/uds.rs +66 -0
- data/vendor/wreq/src/proxy/win.rs +31 -0
- data/vendor/wreq/src/proxy.rs +569 -0
- data/vendor/wreq/src/redirect.rs +575 -0
- data/vendor/wreq/src/retry.rs +198 -0
- data/vendor/wreq/src/sync.rs +129 -0
- data/vendor/wreq/src/tls/conn/cache.rs +123 -0
- data/vendor/wreq/src/tls/conn/cert_compression.rs +125 -0
- data/vendor/wreq/src/tls/conn/ext.rs +82 -0
- data/vendor/wreq/src/tls/conn/macros.rs +34 -0
- data/vendor/wreq/src/tls/conn/service.rs +138 -0
- data/vendor/wreq/src/tls/conn.rs +681 -0
- data/vendor/wreq/src/tls/keylog/handle.rs +64 -0
- data/vendor/wreq/src/tls/keylog.rs +99 -0
- data/vendor/wreq/src/tls/options.rs +464 -0
- data/vendor/wreq/src/tls/x509/identity.rs +122 -0
- data/vendor/wreq/src/tls/x509/parser.rs +71 -0
- data/vendor/wreq/src/tls/x509/store.rs +228 -0
- data/vendor/wreq/src/tls/x509.rs +68 -0
- data/vendor/wreq/src/tls.rs +154 -0
- data/vendor/wreq/src/trace.rs +55 -0
- data/vendor/wreq/src/util.rs +122 -0
- data/vendor/wreq/tests/badssl.rs +228 -0
- data/vendor/wreq/tests/brotli.rs +350 -0
- data/vendor/wreq/tests/client.rs +1098 -0
- data/vendor/wreq/tests/connector_layers.rs +227 -0
- data/vendor/wreq/tests/cookie.rs +306 -0
- data/vendor/wreq/tests/deflate.rs +347 -0
- data/vendor/wreq/tests/emulation.rs +260 -0
- data/vendor/wreq/tests/gzip.rs +347 -0
- data/vendor/wreq/tests/layers.rs +261 -0
- data/vendor/wreq/tests/multipart.rs +165 -0
- data/vendor/wreq/tests/proxy.rs +438 -0
- data/vendor/wreq/tests/redirect.rs +629 -0
- data/vendor/wreq/tests/retry.rs +135 -0
- data/vendor/wreq/tests/support/delay_server.rs +117 -0
- data/vendor/wreq/tests/support/error.rs +16 -0
- data/vendor/wreq/tests/support/layer.rs +183 -0
- data/vendor/wreq/tests/support/mod.rs +9 -0
- data/vendor/wreq/tests/support/server.rs +232 -0
- data/vendor/wreq/tests/timeouts.rs +281 -0
- data/vendor/wreq/tests/unix_socket.rs +135 -0
- data/vendor/wreq/tests/upgrade.rs +98 -0
- data/vendor/wreq/tests/zstd.rs +559 -0
- metadata +225 -0
|
@@ -0,0 +1,1629 @@
|
|
|
1
|
+
pub mod client;
|
|
2
|
+
pub mod future;
|
|
3
|
+
|
|
4
|
+
use std::{
|
|
5
|
+
borrow::Cow,
|
|
6
|
+
collections::HashMap,
|
|
7
|
+
convert::TryInto,
|
|
8
|
+
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
|
|
9
|
+
num::NonZeroU32,
|
|
10
|
+
sync::Arc,
|
|
11
|
+
task::{Context, Poll},
|
|
12
|
+
time::Duration,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
use http::header::{HeaderMap, HeaderValue, USER_AGENT};
|
|
16
|
+
use tower::{
|
|
17
|
+
Layer, Service, ServiceBuilder, ServiceExt,
|
|
18
|
+
retry::{Retry, RetryLayer},
|
|
19
|
+
util::{BoxCloneSyncService, BoxCloneSyncServiceLayer, Either, MapErr, Oneshot},
|
|
20
|
+
};
|
|
21
|
+
#[cfg(feature = "cookies")]
|
|
22
|
+
use {super::layer::cookie::CookieServiceLayer, crate::cookie};
|
|
23
|
+
|
|
24
|
+
pub(crate) use self::client::{
|
|
25
|
+
ConnectRequest, HttpClient,
|
|
26
|
+
extra::{ConnectExtra, ConnectIdentity},
|
|
27
|
+
};
|
|
28
|
+
use self::future::Pending;
|
|
29
|
+
#[cfg(any(
|
|
30
|
+
feature = "gzip",
|
|
31
|
+
feature = "zstd",
|
|
32
|
+
feature = "brotli",
|
|
33
|
+
feature = "deflate",
|
|
34
|
+
))]
|
|
35
|
+
use super::layer::decoder::{AcceptEncoding, DecompressionLayer};
|
|
36
|
+
#[cfg(feature = "ws")]
|
|
37
|
+
use super::ws::WebSocketRequestBuilder;
|
|
38
|
+
use super::{
|
|
39
|
+
Body, EmulationFactory,
|
|
40
|
+
conn::{
|
|
41
|
+
BoxedConnectorLayer, BoxedConnectorService, Conn, Connector, TcpConnectOptions, Unnameable,
|
|
42
|
+
},
|
|
43
|
+
core::{
|
|
44
|
+
body::Incoming,
|
|
45
|
+
rt::{TokioExecutor, TokioTimer},
|
|
46
|
+
},
|
|
47
|
+
layer::{
|
|
48
|
+
config::{ConfigService, ConfigServiceLayer, TransportOptions},
|
|
49
|
+
redirect::{FollowRedirect, FollowRedirectLayer},
|
|
50
|
+
retry::RetryPolicy,
|
|
51
|
+
timeout::{
|
|
52
|
+
ResponseBodyTimeout, ResponseBodyTimeoutLayer, Timeout, TimeoutBody, TimeoutLayer,
|
|
53
|
+
TimeoutOptions,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
request::{Request, RequestBuilder},
|
|
57
|
+
response::Response,
|
|
58
|
+
};
|
|
59
|
+
#[cfg(feature = "hickory-dns")]
|
|
60
|
+
use crate::dns::hickory::HickoryDnsResolver;
|
|
61
|
+
use crate::{
|
|
62
|
+
IntoUri, Method, Proxy,
|
|
63
|
+
dns::{DnsResolverWithOverrides, DynResolver, GaiResolver, IntoResolve, Resolve},
|
|
64
|
+
error::{self, BoxError, Error},
|
|
65
|
+
header::OrigHeaderMap,
|
|
66
|
+
http1::Http1Options,
|
|
67
|
+
http2::Http2Options,
|
|
68
|
+
proxy::Matcher as ProxyMatcher,
|
|
69
|
+
redirect::{self, FollowRedirectPolicy},
|
|
70
|
+
retry,
|
|
71
|
+
tls::{AlpnProtocol, CertStore, Identity, KeyLog, TlsOptions, TlsVersion},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/// Service type for cookie handling. Identity type when cookies feature is disabled.
|
|
75
|
+
#[cfg(not(feature = "cookies"))]
|
|
76
|
+
type CookieService<T> = T;
|
|
77
|
+
|
|
78
|
+
/// Service wrapper that handles cookie storage and injection.
|
|
79
|
+
#[cfg(feature = "cookies")]
|
|
80
|
+
type CookieService<T> = super::layer::cookie::CookieService<T>;
|
|
81
|
+
|
|
82
|
+
/// Decompression service type. Identity type when compression features are disabled.
|
|
83
|
+
#[cfg(not(any(
|
|
84
|
+
feature = "gzip",
|
|
85
|
+
feature = "zstd",
|
|
86
|
+
feature = "brotli",
|
|
87
|
+
feature = "deflate"
|
|
88
|
+
)))]
|
|
89
|
+
type Decompression<T> = T;
|
|
90
|
+
|
|
91
|
+
/// Service wrapper that handles response body decompression.
|
|
92
|
+
#[cfg(any(
|
|
93
|
+
feature = "gzip",
|
|
94
|
+
feature = "zstd",
|
|
95
|
+
feature = "brotli",
|
|
96
|
+
feature = "deflate"
|
|
97
|
+
))]
|
|
98
|
+
type Decompression<T> = super::layer::decoder::Decompression<T>;
|
|
99
|
+
|
|
100
|
+
/// Response body type with timeout and optional decompression.
|
|
101
|
+
#[cfg(any(
|
|
102
|
+
feature = "gzip",
|
|
103
|
+
feature = "zstd",
|
|
104
|
+
feature = "brotli",
|
|
105
|
+
feature = "deflate"
|
|
106
|
+
))]
|
|
107
|
+
type ResponseBody = TimeoutBody<tower_http::decompression::DecompressionBody<Incoming>>;
|
|
108
|
+
|
|
109
|
+
/// Response body type with timeout only (no compression features).
|
|
110
|
+
#[cfg(not(any(
|
|
111
|
+
feature = "gzip",
|
|
112
|
+
feature = "zstd",
|
|
113
|
+
feature = "brotli",
|
|
114
|
+
feature = "deflate"
|
|
115
|
+
)))]
|
|
116
|
+
type ResponseBody = TimeoutBody<Incoming>;
|
|
117
|
+
|
|
118
|
+
/// The complete HTTP client service stack with all middleware layers.
|
|
119
|
+
type ClientService = Timeout<
|
|
120
|
+
ResponseBodyTimeout<
|
|
121
|
+
ConfigService<
|
|
122
|
+
Decompression<
|
|
123
|
+
Retry<
|
|
124
|
+
RetryPolicy,
|
|
125
|
+
FollowRedirect<
|
|
126
|
+
CookieService<
|
|
127
|
+
MapErr<
|
|
128
|
+
HttpClient<Connector, Body>,
|
|
129
|
+
fn(client::error::Error) -> BoxError,
|
|
130
|
+
>,
|
|
131
|
+
>,
|
|
132
|
+
FollowRedirectPolicy,
|
|
133
|
+
>,
|
|
134
|
+
>,
|
|
135
|
+
>,
|
|
136
|
+
>,
|
|
137
|
+
>,
|
|
138
|
+
>;
|
|
139
|
+
|
|
140
|
+
/// Type-erased client service for dynamic middleware composition.
|
|
141
|
+
type BoxedClientService =
|
|
142
|
+
BoxCloneSyncService<http::Request<Body>, http::Response<ResponseBody>, BoxError>;
|
|
143
|
+
|
|
144
|
+
/// Layer type for wrapping boxed client services with additional middleware.
|
|
145
|
+
type BoxedClientLayer = BoxCloneSyncServiceLayer<
|
|
146
|
+
BoxedClientService,
|
|
147
|
+
http::Request<Body>,
|
|
148
|
+
http::Response<ResponseBody>,
|
|
149
|
+
BoxError,
|
|
150
|
+
>;
|
|
151
|
+
|
|
152
|
+
/// Client reference type that can be either the generic service or a boxed service.
|
|
153
|
+
type ClientRef = Either<ClientService, BoxedClientService>;
|
|
154
|
+
|
|
155
|
+
/// An [`Client`] to make Requests with.
|
|
156
|
+
///
|
|
157
|
+
/// The Client has various configuration values to tweak, but the defaults
|
|
158
|
+
/// are set to what is usually the most commonly desired value. To configure a
|
|
159
|
+
/// [`Client`], use [`Client::builder()`].
|
|
160
|
+
///
|
|
161
|
+
/// The [`Client`] holds a connection pool internally, so it is advised that
|
|
162
|
+
/// you create one and **reuse** it.
|
|
163
|
+
///
|
|
164
|
+
/// You do **not** have to wrap the [`Client`] in an [`Rc`] or [`Arc`] to **reuse** it,
|
|
165
|
+
/// because it already uses an [`Arc`] internally.
|
|
166
|
+
///
|
|
167
|
+
/// [`Rc`]: std::rc::Rc
|
|
168
|
+
#[derive(Clone)]
|
|
169
|
+
pub struct Client {
|
|
170
|
+
inner: Arc<ClientRef>,
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/// A [`ClientBuilder`] can be used to create a [`Client`] with custom configuration.
|
|
174
|
+
#[must_use]
|
|
175
|
+
pub struct ClientBuilder {
|
|
176
|
+
config: Config,
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/// The HTTP version preference for the client.
|
|
180
|
+
#[repr(u8)]
|
|
181
|
+
enum HttpVersionPref {
|
|
182
|
+
Http1,
|
|
183
|
+
Http2,
|
|
184
|
+
All,
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
struct Config {
|
|
188
|
+
error: Option<Error>,
|
|
189
|
+
headers: HeaderMap,
|
|
190
|
+
orig_headers: OrigHeaderMap,
|
|
191
|
+
#[cfg(any(
|
|
192
|
+
feature = "gzip",
|
|
193
|
+
feature = "zstd",
|
|
194
|
+
feature = "brotli",
|
|
195
|
+
feature = "deflate",
|
|
196
|
+
))]
|
|
197
|
+
accept_encoding: AcceptEncoding,
|
|
198
|
+
connect_timeout: Option<Duration>,
|
|
199
|
+
connection_verbose: bool,
|
|
200
|
+
pool_idle_timeout: Option<Duration>,
|
|
201
|
+
pool_max_idle_per_host: usize,
|
|
202
|
+
pool_max_size: Option<NonZeroU32>,
|
|
203
|
+
tcp_nodelay: bool,
|
|
204
|
+
tcp_reuse_address: bool,
|
|
205
|
+
tcp_keepalive: Option<Duration>,
|
|
206
|
+
tcp_keepalive_interval: Option<Duration>,
|
|
207
|
+
tcp_keepalive_retries: Option<u32>,
|
|
208
|
+
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
|
209
|
+
tcp_user_timeout: Option<Duration>,
|
|
210
|
+
tcp_send_buffer_size: Option<usize>,
|
|
211
|
+
tcp_recv_buffer_size: Option<usize>,
|
|
212
|
+
tcp_happy_eyeballs_timeout: Option<Duration>,
|
|
213
|
+
tcp_connect_options: TcpConnectOptions,
|
|
214
|
+
proxies: Vec<ProxyMatcher>,
|
|
215
|
+
auto_sys_proxy: bool,
|
|
216
|
+
retry_policy: retry::Policy,
|
|
217
|
+
redirect_policy: redirect::Policy,
|
|
218
|
+
referer: bool,
|
|
219
|
+
timeout_options: TimeoutOptions,
|
|
220
|
+
#[cfg(feature = "cookies")]
|
|
221
|
+
cookie_store: Option<Arc<dyn cookie::CookieStore>>,
|
|
222
|
+
#[cfg(feature = "hickory-dns")]
|
|
223
|
+
hickory_dns: bool,
|
|
224
|
+
dns_overrides: HashMap<Cow<'static, str>, Vec<SocketAddr>>,
|
|
225
|
+
dns_resolver: Option<Arc<dyn Resolve>>,
|
|
226
|
+
http_version_pref: HttpVersionPref,
|
|
227
|
+
https_only: bool,
|
|
228
|
+
layers: Vec<BoxedClientLayer>,
|
|
229
|
+
connector_layers: Vec<BoxedConnectorLayer>,
|
|
230
|
+
keylog: Option<KeyLog>,
|
|
231
|
+
tls_info: bool,
|
|
232
|
+
tls_sni: bool,
|
|
233
|
+
verify_hostname: bool,
|
|
234
|
+
identity: Option<Identity>,
|
|
235
|
+
cert_store: CertStore,
|
|
236
|
+
cert_verification: bool,
|
|
237
|
+
min_tls_version: Option<TlsVersion>,
|
|
238
|
+
max_tls_version: Option<TlsVersion>,
|
|
239
|
+
transport_options: TransportOptions,
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// ===== impl Client =====
|
|
243
|
+
|
|
244
|
+
impl Default for Client {
|
|
245
|
+
fn default() -> Self {
|
|
246
|
+
Self::new()
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
impl Client {
|
|
251
|
+
/// Constructs a new [`Client`].
|
|
252
|
+
///
|
|
253
|
+
/// # Panics
|
|
254
|
+
///
|
|
255
|
+
/// This method panics if a TLS backend cannot be initialized, or the resolver
|
|
256
|
+
/// cannot load the system configuration.
|
|
257
|
+
///
|
|
258
|
+
/// Use [`Client::builder()`] if you wish to handle the failure as an [`Error`]
|
|
259
|
+
/// instead of panicking.
|
|
260
|
+
#[inline]
|
|
261
|
+
pub fn new() -> Client {
|
|
262
|
+
Client::builder().build().expect("Client::new()")
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/// Creates a [`ClientBuilder`] to configure a [`Client`].
|
|
266
|
+
pub fn builder() -> ClientBuilder {
|
|
267
|
+
ClientBuilder {
|
|
268
|
+
config: Config {
|
|
269
|
+
error: None,
|
|
270
|
+
headers: HeaderMap::new(),
|
|
271
|
+
orig_headers: OrigHeaderMap::new(),
|
|
272
|
+
#[cfg(any(
|
|
273
|
+
feature = "gzip",
|
|
274
|
+
feature = "zstd",
|
|
275
|
+
feature = "brotli",
|
|
276
|
+
feature = "deflate",
|
|
277
|
+
))]
|
|
278
|
+
accept_encoding: AcceptEncoding::default(),
|
|
279
|
+
connect_timeout: None,
|
|
280
|
+
connection_verbose: false,
|
|
281
|
+
pool_idle_timeout: Some(Duration::from_secs(90)),
|
|
282
|
+
pool_max_idle_per_host: usize::MAX,
|
|
283
|
+
pool_max_size: None,
|
|
284
|
+
tcp_keepalive: Some(Duration::from_secs(15)),
|
|
285
|
+
tcp_keepalive_interval: Some(Duration::from_secs(15)),
|
|
286
|
+
tcp_keepalive_retries: Some(3),
|
|
287
|
+
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
|
288
|
+
tcp_user_timeout: Some(Duration::from_secs(30)),
|
|
289
|
+
tcp_connect_options: TcpConnectOptions::default(),
|
|
290
|
+
tcp_nodelay: true,
|
|
291
|
+
tcp_reuse_address: false,
|
|
292
|
+
tcp_send_buffer_size: None,
|
|
293
|
+
tcp_recv_buffer_size: None,
|
|
294
|
+
tcp_happy_eyeballs_timeout: Some(Duration::from_millis(300)),
|
|
295
|
+
proxies: Vec::new(),
|
|
296
|
+
auto_sys_proxy: true,
|
|
297
|
+
retry_policy: retry::Policy::default(),
|
|
298
|
+
redirect_policy: redirect::Policy::none(),
|
|
299
|
+
referer: true,
|
|
300
|
+
timeout_options: TimeoutOptions::default(),
|
|
301
|
+
#[cfg(feature = "hickory-dns")]
|
|
302
|
+
hickory_dns: cfg!(feature = "hickory-dns"),
|
|
303
|
+
#[cfg(feature = "cookies")]
|
|
304
|
+
cookie_store: None,
|
|
305
|
+
dns_overrides: HashMap::new(),
|
|
306
|
+
dns_resolver: None,
|
|
307
|
+
http_version_pref: HttpVersionPref::All,
|
|
308
|
+
https_only: false,
|
|
309
|
+
layers: Vec::new(),
|
|
310
|
+
connector_layers: Vec::new(),
|
|
311
|
+
keylog: None,
|
|
312
|
+
tls_info: false,
|
|
313
|
+
tls_sni: true,
|
|
314
|
+
verify_hostname: true,
|
|
315
|
+
identity: None,
|
|
316
|
+
cert_store: CertStore::default(),
|
|
317
|
+
cert_verification: true,
|
|
318
|
+
min_tls_version: None,
|
|
319
|
+
max_tls_version: None,
|
|
320
|
+
transport_options: TransportOptions::default(),
|
|
321
|
+
},
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/// Convenience method to make a `GET` request to a URI.
|
|
326
|
+
///
|
|
327
|
+
/// # Errors
|
|
328
|
+
///
|
|
329
|
+
/// This method fails whenever the supplied `Uri` cannot be parsed.
|
|
330
|
+
#[inline]
|
|
331
|
+
pub fn get<U: IntoUri>(&self, uri: U) -> RequestBuilder {
|
|
332
|
+
self.request(Method::GET, uri)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/// Convenience method to make a `POST` request to a URI.
|
|
336
|
+
///
|
|
337
|
+
/// # Errors
|
|
338
|
+
///
|
|
339
|
+
/// This method fails whenever the supplied `Uri` cannot be parsed.
|
|
340
|
+
#[inline]
|
|
341
|
+
pub fn post<U: IntoUri>(&self, uri: U) -> RequestBuilder {
|
|
342
|
+
self.request(Method::POST, uri)
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/// Convenience method to make a `PUT` request to a URI.
|
|
346
|
+
///
|
|
347
|
+
/// # Errors
|
|
348
|
+
///
|
|
349
|
+
/// This method fails whenever the supplied `Uri` cannot be parsed.
|
|
350
|
+
#[inline]
|
|
351
|
+
pub fn put<U: IntoUri>(&self, uri: U) -> RequestBuilder {
|
|
352
|
+
self.request(Method::PUT, uri)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/// Convenience method to make a `PATCH` request to a URI.
|
|
356
|
+
///
|
|
357
|
+
/// # Errors
|
|
358
|
+
///
|
|
359
|
+
/// This method fails whenever the supplied `Uri` cannot be parsed.
|
|
360
|
+
#[inline]
|
|
361
|
+
pub fn patch<U: IntoUri>(&self, uri: U) -> RequestBuilder {
|
|
362
|
+
self.request(Method::PATCH, uri)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/// Convenience method to make a `DELETE` request to a URI.
|
|
366
|
+
///
|
|
367
|
+
/// # Errors
|
|
368
|
+
///
|
|
369
|
+
/// This method fails whenever the supplied `Uri` cannot be parsed.
|
|
370
|
+
#[inline]
|
|
371
|
+
pub fn delete<U: IntoUri>(&self, uri: U) -> RequestBuilder {
|
|
372
|
+
self.request(Method::DELETE, uri)
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/// Convenience method to make a `HEAD` request to a URI.
|
|
376
|
+
///
|
|
377
|
+
/// # Errors
|
|
378
|
+
///
|
|
379
|
+
/// This method fails whenever the supplied `Uri` cannot be parsed.
|
|
380
|
+
#[inline]
|
|
381
|
+
pub fn head<U: IntoUri>(&self, uri: U) -> RequestBuilder {
|
|
382
|
+
self.request(Method::HEAD, uri)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/// Convenience method to make a `OPTIONS` request to a URI.
|
|
386
|
+
///
|
|
387
|
+
/// # Errors
|
|
388
|
+
///
|
|
389
|
+
/// This method fails whenever the supplied `Uri` cannot be parsed.
|
|
390
|
+
#[inline]
|
|
391
|
+
pub fn options<U: IntoUri>(&self, uri: U) -> RequestBuilder {
|
|
392
|
+
self.request(Method::OPTIONS, uri)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/// Start building a `Request` with the `Method` and `Uri`.
|
|
396
|
+
///
|
|
397
|
+
/// Returns a `RequestBuilder`, which will allow setting headers and
|
|
398
|
+
/// the request body before sending.
|
|
399
|
+
///
|
|
400
|
+
/// # Errors
|
|
401
|
+
///
|
|
402
|
+
/// This method fails whenever the supplied `Uri` cannot be parsed.
|
|
403
|
+
pub fn request<U: IntoUri>(&self, method: Method, uri: U) -> RequestBuilder {
|
|
404
|
+
let req = uri.into_uri().map(move |uri| Request::new(method, uri));
|
|
405
|
+
RequestBuilder::new(self.clone(), req)
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/// Upgrades the [`RequestBuilder`] to perform a
|
|
409
|
+
/// websocket handshake. This returns a wrapped type, so you must do
|
|
410
|
+
/// this after you set up your request, and just before you send the
|
|
411
|
+
/// request.
|
|
412
|
+
#[inline]
|
|
413
|
+
#[cfg(feature = "ws")]
|
|
414
|
+
#[cfg_attr(docsrs, doc(cfg(feature = "ws")))]
|
|
415
|
+
pub fn websocket<U: IntoUri>(&self, uri: U) -> WebSocketRequestBuilder {
|
|
416
|
+
WebSocketRequestBuilder::new(self.request(Method::GET, uri))
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/// Executes a `Request`.
|
|
420
|
+
///
|
|
421
|
+
/// A `Request` can be built manually with `Request::new()` or obtained
|
|
422
|
+
/// from a RequestBuilder with `RequestBuilder::build()`.
|
|
423
|
+
///
|
|
424
|
+
/// You should prefer to use the `RequestBuilder` and
|
|
425
|
+
/// `RequestBuilder::send()`.
|
|
426
|
+
///
|
|
427
|
+
/// # Errors
|
|
428
|
+
///
|
|
429
|
+
/// This method fails if there was an error while sending request,
|
|
430
|
+
/// redirect loop was detected or redirect limit was exhausted.
|
|
431
|
+
pub fn execute(&self, request: Request) -> Pending {
|
|
432
|
+
let req = http::Request::<Body>::from(request);
|
|
433
|
+
// Prepare the future request by ensuring we use the exact same Service instance
|
|
434
|
+
// for both poll_ready and call.
|
|
435
|
+
let uri = req.uri().clone();
|
|
436
|
+
let fut = Oneshot::new(self.inner.as_ref().clone(), req);
|
|
437
|
+
Pending::request(uri, fut)
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
impl tower::Service<Request> for Client {
|
|
442
|
+
type Response = Response;
|
|
443
|
+
type Error = Error;
|
|
444
|
+
type Future = Pending;
|
|
445
|
+
|
|
446
|
+
#[inline(always)]
|
|
447
|
+
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
|
448
|
+
Poll::Ready(Ok(()))
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
#[inline(always)]
|
|
452
|
+
fn call(&mut self, req: Request) -> Self::Future {
|
|
453
|
+
self.execute(req)
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
impl tower::Service<Request> for &'_ Client {
|
|
458
|
+
type Response = Response;
|
|
459
|
+
type Error = Error;
|
|
460
|
+
type Future = Pending;
|
|
461
|
+
|
|
462
|
+
#[inline(always)]
|
|
463
|
+
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
|
464
|
+
Poll::Ready(Ok(()))
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
#[inline(always)]
|
|
468
|
+
fn call(&mut self, req: Request) -> Self::Future {
|
|
469
|
+
self.execute(req)
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// ===== impl ClientBuilder =====
|
|
474
|
+
|
|
475
|
+
impl ClientBuilder {
|
|
476
|
+
/// Returns a [`Client`] that uses this [`ClientBuilder`] configuration.
|
|
477
|
+
///
|
|
478
|
+
/// # Errors
|
|
479
|
+
///
|
|
480
|
+
/// This method fails if a TLS backend cannot be initialized, or the resolver
|
|
481
|
+
/// cannot load the system configuration.
|
|
482
|
+
pub fn build(self) -> crate::Result<Client> {
|
|
483
|
+
let mut config = self.config;
|
|
484
|
+
|
|
485
|
+
if let Some(err) = config.error {
|
|
486
|
+
return Err(err);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Prepare proxies
|
|
490
|
+
if config.auto_sys_proxy {
|
|
491
|
+
config.proxies.push(ProxyMatcher::system());
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Create base client service
|
|
495
|
+
let service = {
|
|
496
|
+
let (tls_options, http1_options, http2_options) = config.transport_options.into();
|
|
497
|
+
|
|
498
|
+
let resolver = {
|
|
499
|
+
let mut resolver: Arc<dyn Resolve> = match config.dns_resolver {
|
|
500
|
+
Some(dns_resolver) => dns_resolver,
|
|
501
|
+
#[cfg(feature = "hickory-dns")]
|
|
502
|
+
None if config.hickory_dns => Arc::new(HickoryDnsResolver::new()),
|
|
503
|
+
None => Arc::new(GaiResolver::new()),
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
if !config.dns_overrides.is_empty() {
|
|
507
|
+
resolver = Arc::new(DnsResolverWithOverrides::new(
|
|
508
|
+
resolver,
|
|
509
|
+
config.dns_overrides,
|
|
510
|
+
));
|
|
511
|
+
}
|
|
512
|
+
DynResolver::new(resolver)
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
// Build connector
|
|
516
|
+
let connector = Connector::builder(config.proxies, resolver)
|
|
517
|
+
.timeout(config.connect_timeout)
|
|
518
|
+
.tls_info(config.tls_info)
|
|
519
|
+
.tls_options(tls_options)
|
|
520
|
+
.verbose(config.connection_verbose)
|
|
521
|
+
.with_tls(|tls| {
|
|
522
|
+
let alpn_protocol = match config.http_version_pref {
|
|
523
|
+
HttpVersionPref::Http1 => Some(AlpnProtocol::HTTP1),
|
|
524
|
+
HttpVersionPref::Http2 => Some(AlpnProtocol::HTTP2),
|
|
525
|
+
_ => None,
|
|
526
|
+
};
|
|
527
|
+
tls.alpn_protocol(alpn_protocol)
|
|
528
|
+
.max_version(config.max_tls_version)
|
|
529
|
+
.min_version(config.min_tls_version)
|
|
530
|
+
.tls_sni(config.tls_sni)
|
|
531
|
+
.verify_hostname(config.verify_hostname)
|
|
532
|
+
.cert_verification(config.cert_verification)
|
|
533
|
+
.cert_store(config.cert_store)
|
|
534
|
+
.identity(config.identity)
|
|
535
|
+
.keylog(config.keylog)
|
|
536
|
+
})
|
|
537
|
+
.with_http(|http| {
|
|
538
|
+
http.enforce_http(false);
|
|
539
|
+
http.set_keepalive(config.tcp_keepalive);
|
|
540
|
+
http.set_keepalive_interval(config.tcp_keepalive_interval);
|
|
541
|
+
http.set_keepalive_retries(config.tcp_keepalive_retries);
|
|
542
|
+
http.set_reuse_address(config.tcp_reuse_address);
|
|
543
|
+
http.set_connect_options(config.tcp_connect_options);
|
|
544
|
+
http.set_connect_timeout(config.connect_timeout);
|
|
545
|
+
http.set_nodelay(config.tcp_nodelay);
|
|
546
|
+
http.set_send_buffer_size(config.tcp_send_buffer_size);
|
|
547
|
+
http.set_recv_buffer_size(config.tcp_recv_buffer_size);
|
|
548
|
+
http.set_happy_eyeballs_timeout(config.tcp_happy_eyeballs_timeout);
|
|
549
|
+
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
|
550
|
+
http.set_tcp_user_timeout(config.tcp_user_timeout);
|
|
551
|
+
})
|
|
552
|
+
.build(config.connector_layers)?;
|
|
553
|
+
|
|
554
|
+
// Build client
|
|
555
|
+
HttpClient::builder(TokioExecutor::new())
|
|
556
|
+
.http1_options(http1_options)
|
|
557
|
+
.http2_options(http2_options)
|
|
558
|
+
.http2_only(matches!(config.http_version_pref, HttpVersionPref::Http2))
|
|
559
|
+
.http2_timer(TokioTimer::new())
|
|
560
|
+
.pool_timer(TokioTimer::new())
|
|
561
|
+
.pool_idle_timeout(config.pool_idle_timeout)
|
|
562
|
+
.pool_max_idle_per_host(config.pool_max_idle_per_host)
|
|
563
|
+
.pool_max_size(config.pool_max_size)
|
|
564
|
+
.build(connector)
|
|
565
|
+
.map_err(Into::into as _)
|
|
566
|
+
};
|
|
567
|
+
|
|
568
|
+
// Configured client service with layers
|
|
569
|
+
let client = {
|
|
570
|
+
#[cfg(feature = "cookies")]
|
|
571
|
+
let service = ServiceBuilder::new()
|
|
572
|
+
.layer(CookieServiceLayer::new(config.cookie_store))
|
|
573
|
+
.service(service);
|
|
574
|
+
|
|
575
|
+
let service = ServiceBuilder::new()
|
|
576
|
+
.layer(RetryLayer::new(RetryPolicy::new(config.retry_policy)))
|
|
577
|
+
.layer({
|
|
578
|
+
let policy = FollowRedirectPolicy::new(config.redirect_policy)
|
|
579
|
+
.with_referer(config.referer)
|
|
580
|
+
.with_https_only(config.https_only);
|
|
581
|
+
FollowRedirectLayer::with_policy(policy)
|
|
582
|
+
})
|
|
583
|
+
.service(service);
|
|
584
|
+
|
|
585
|
+
#[cfg(any(
|
|
586
|
+
feature = "gzip",
|
|
587
|
+
feature = "zstd",
|
|
588
|
+
feature = "brotli",
|
|
589
|
+
feature = "deflate",
|
|
590
|
+
))]
|
|
591
|
+
let service = ServiceBuilder::new()
|
|
592
|
+
.layer(DecompressionLayer::new(config.accept_encoding))
|
|
593
|
+
.service(service);
|
|
594
|
+
|
|
595
|
+
let service = ServiceBuilder::new()
|
|
596
|
+
.layer(ResponseBodyTimeoutLayer::new(config.timeout_options))
|
|
597
|
+
.layer(ConfigServiceLayer::new(
|
|
598
|
+
config.https_only,
|
|
599
|
+
config.headers,
|
|
600
|
+
config.orig_headers,
|
|
601
|
+
))
|
|
602
|
+
.service(service);
|
|
603
|
+
|
|
604
|
+
if config.layers.is_empty() {
|
|
605
|
+
let service = ServiceBuilder::new()
|
|
606
|
+
.layer(TimeoutLayer::new(config.timeout_options))
|
|
607
|
+
.service(service);
|
|
608
|
+
|
|
609
|
+
ClientRef::Left(service)
|
|
610
|
+
} else {
|
|
611
|
+
let service = config
|
|
612
|
+
.layers
|
|
613
|
+
.into_iter()
|
|
614
|
+
.fold(BoxCloneSyncService::new(service), |service, layer| {
|
|
615
|
+
ServiceBuilder::new().layer(layer).service(service)
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
let service = ServiceBuilder::new()
|
|
619
|
+
.layer(TimeoutLayer::new(config.timeout_options))
|
|
620
|
+
.service(service)
|
|
621
|
+
.map_err(error::map_timeout_to_request_error);
|
|
622
|
+
|
|
623
|
+
ClientRef::Right(BoxCloneSyncService::new(service))
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
|
|
627
|
+
Ok(Client {
|
|
628
|
+
inner: Arc::new(client),
|
|
629
|
+
})
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Higher-level options
|
|
633
|
+
|
|
634
|
+
/// Sets the `User-Agent` header to be used by this client.
|
|
635
|
+
///
|
|
636
|
+
/// # Example
|
|
637
|
+
///
|
|
638
|
+
/// ```rust
|
|
639
|
+
/// # async fn doc() -> wreq::Result<()> {
|
|
640
|
+
/// // Name your user agent after your app?
|
|
641
|
+
/// static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),);
|
|
642
|
+
///
|
|
643
|
+
/// let client = wreq::Client::builder().user_agent(APP_USER_AGENT).build()?;
|
|
644
|
+
/// let res = client.get("https://www.rust-lang.org").send().await?;
|
|
645
|
+
/// # Ok(())
|
|
646
|
+
/// # }
|
|
647
|
+
/// ```
|
|
648
|
+
pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
|
|
649
|
+
where
|
|
650
|
+
V: TryInto<HeaderValue>,
|
|
651
|
+
V::Error: Into<http::Error>,
|
|
652
|
+
{
|
|
653
|
+
match value.try_into() {
|
|
654
|
+
Ok(value) => {
|
|
655
|
+
self.config.headers.insert(USER_AGENT, value);
|
|
656
|
+
}
|
|
657
|
+
Err(err) => {
|
|
658
|
+
self.config.error = Some(Error::builder(err.into()));
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
self
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/// Sets the default headers for every request.
|
|
665
|
+
///
|
|
666
|
+
/// # Example
|
|
667
|
+
///
|
|
668
|
+
/// ```rust
|
|
669
|
+
/// use wreq::header;
|
|
670
|
+
/// # async fn doc() -> wreq::Result<()> {
|
|
671
|
+
/// let mut headers = header::HeaderMap::new();
|
|
672
|
+
/// headers.insert("X-MY-HEADER", header::HeaderValue::from_static("value"));
|
|
673
|
+
///
|
|
674
|
+
/// // Consider marking security-sensitive headers with `set_sensitive`.
|
|
675
|
+
/// let mut auth_value = header::HeaderValue::from_static("secret");
|
|
676
|
+
/// auth_value.set_sensitive(true);
|
|
677
|
+
/// headers.insert(header::AUTHORIZATION, auth_value);
|
|
678
|
+
///
|
|
679
|
+
/// // get a client builder
|
|
680
|
+
/// let client = wreq::Client::builder().default_headers(headers).build()?;
|
|
681
|
+
/// let res = client.get("https://www.rust-lang.org").send().await?;
|
|
682
|
+
/// # Ok(())
|
|
683
|
+
/// # }
|
|
684
|
+
/// ```
|
|
685
|
+
///
|
|
686
|
+
/// Override the default headers:
|
|
687
|
+
///
|
|
688
|
+
/// ```rust
|
|
689
|
+
/// use wreq::header;
|
|
690
|
+
/// # async fn doc() -> wreq::Result<()> {
|
|
691
|
+
/// let mut headers = header::HeaderMap::new();
|
|
692
|
+
/// headers.insert("X-MY-HEADER", header::HeaderValue::from_static("value"));
|
|
693
|
+
///
|
|
694
|
+
/// // get a client builder
|
|
695
|
+
/// let client = wreq::Client::builder().default_headers(headers).build()?;
|
|
696
|
+
/// let res = client
|
|
697
|
+
/// .get("https://www.rust-lang.org")
|
|
698
|
+
/// .header("X-MY-HEADER", "new_value")
|
|
699
|
+
/// .send()
|
|
700
|
+
/// .await?;
|
|
701
|
+
/// # Ok(())
|
|
702
|
+
/// # }
|
|
703
|
+
/// ```
|
|
704
|
+
#[inline]
|
|
705
|
+
pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
|
|
706
|
+
crate::util::replace_headers(&mut self.config.headers, headers);
|
|
707
|
+
self
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/// Sets the original headers for every request.
|
|
711
|
+
#[inline]
|
|
712
|
+
pub fn orig_headers(mut self, orig_headers: OrigHeaderMap) -> ClientBuilder {
|
|
713
|
+
self.config.orig_headers.extend(orig_headers);
|
|
714
|
+
self
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/// Enable a persistent cookie store for the client.
|
|
718
|
+
///
|
|
719
|
+
/// Cookies received in responses will be preserved and included in
|
|
720
|
+
/// additional requests.
|
|
721
|
+
///
|
|
722
|
+
/// By default, no cookie store is used.
|
|
723
|
+
///
|
|
724
|
+
/// # Optional
|
|
725
|
+
///
|
|
726
|
+
/// This requires the optional `cookies` feature to be enabled.
|
|
727
|
+
#[inline]
|
|
728
|
+
#[cfg(feature = "cookies")]
|
|
729
|
+
#[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
|
|
730
|
+
pub fn cookie_store(mut self, enable: bool) -> ClientBuilder {
|
|
731
|
+
if enable {
|
|
732
|
+
self.cookie_provider(Arc::new(cookie::Jar::default()))
|
|
733
|
+
} else {
|
|
734
|
+
self.config.cookie_store = None;
|
|
735
|
+
self
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/// Set the persistent cookie store for the client.
|
|
740
|
+
///
|
|
741
|
+
/// Cookies received in responses will be passed to this store, and
|
|
742
|
+
/// additional requests will query this store for cookies.
|
|
743
|
+
///
|
|
744
|
+
/// By default, no cookie store is used.
|
|
745
|
+
///
|
|
746
|
+
/// # Optional
|
|
747
|
+
///
|
|
748
|
+
/// This requires the optional `cookies` feature to be enabled.
|
|
749
|
+
#[inline]
|
|
750
|
+
#[cfg(feature = "cookies")]
|
|
751
|
+
#[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
|
|
752
|
+
pub fn cookie_provider<C>(mut self, cookie_store: C) -> ClientBuilder
|
|
753
|
+
where
|
|
754
|
+
C: cookie::IntoCookieStore,
|
|
755
|
+
{
|
|
756
|
+
self.config.cookie_store = Some(cookie_store.into_cookie_store());
|
|
757
|
+
self
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/// Enable auto gzip decompression by checking the `Content-Encoding` response header.
|
|
761
|
+
///
|
|
762
|
+
/// If auto gzip decompression is turned on:
|
|
763
|
+
///
|
|
764
|
+
/// - When sending a request and if the request's headers do not already contain an
|
|
765
|
+
/// `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to `gzip`.
|
|
766
|
+
/// The request body is **not** automatically compressed.
|
|
767
|
+
/// - When receiving a response, if its headers contain a `Content-Encoding` value of `gzip`,
|
|
768
|
+
/// both `Content-Encoding` and `Content-Length` are removed from the headers' set. The
|
|
769
|
+
/// response body is automatically decompressed.
|
|
770
|
+
///
|
|
771
|
+
/// If the `gzip` feature is turned on, the default option is enabled.
|
|
772
|
+
///
|
|
773
|
+
/// # Optional
|
|
774
|
+
///
|
|
775
|
+
/// This requires the optional `gzip` feature to be enabled
|
|
776
|
+
#[inline]
|
|
777
|
+
#[cfg(feature = "gzip")]
|
|
778
|
+
#[cfg_attr(docsrs, doc(cfg(feature = "gzip")))]
|
|
779
|
+
pub fn gzip(mut self, enable: bool) -> ClientBuilder {
|
|
780
|
+
self.config.accept_encoding.gzip = enable;
|
|
781
|
+
self
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/// Enable auto brotli decompression by checking the `Content-Encoding` response header.
|
|
785
|
+
///
|
|
786
|
+
/// If auto brotli decompression is turned on:
|
|
787
|
+
///
|
|
788
|
+
/// - When sending a request and if the request's headers do not already contain an
|
|
789
|
+
/// `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to `br`. The
|
|
790
|
+
/// request body is **not** automatically compressed.
|
|
791
|
+
/// - When receiving a response, if its headers contain a `Content-Encoding` value of `br`, both
|
|
792
|
+
/// `Content-Encoding` and `Content-Length` are removed from the headers' set. The response
|
|
793
|
+
/// body is automatically decompressed.
|
|
794
|
+
///
|
|
795
|
+
/// If the `brotli` feature is turned on, the default option is enabled.
|
|
796
|
+
///
|
|
797
|
+
/// # Optional
|
|
798
|
+
///
|
|
799
|
+
/// This requires the optional `brotli` feature to be enabled
|
|
800
|
+
#[inline]
|
|
801
|
+
#[cfg(feature = "brotli")]
|
|
802
|
+
#[cfg_attr(docsrs, doc(cfg(feature = "brotli")))]
|
|
803
|
+
pub fn brotli(mut self, enable: bool) -> ClientBuilder {
|
|
804
|
+
self.config.accept_encoding.brotli = enable;
|
|
805
|
+
self
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/// Enable auto zstd decompression by checking the `Content-Encoding` response header.
|
|
809
|
+
///
|
|
810
|
+
/// If auto zstd decompression is turned on:
|
|
811
|
+
///
|
|
812
|
+
/// - When sending a request and if the request's headers do not already contain an
|
|
813
|
+
/// `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to `zstd`.
|
|
814
|
+
/// The request body is **not** automatically compressed.
|
|
815
|
+
/// - When receiving a response, if its headers contain a `Content-Encoding` value of `zstd`,
|
|
816
|
+
/// both `Content-Encoding` and `Content-Length` are removed from the headers' set. The
|
|
817
|
+
/// response body is automatically decompressed.
|
|
818
|
+
///
|
|
819
|
+
/// If the `zstd` feature is turned on, the default option is enabled.
|
|
820
|
+
///
|
|
821
|
+
/// # Optional
|
|
822
|
+
///
|
|
823
|
+
/// This requires the optional `zstd` feature to be enabled
|
|
824
|
+
#[inline]
|
|
825
|
+
#[cfg(feature = "zstd")]
|
|
826
|
+
#[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
|
|
827
|
+
pub fn zstd(mut self, enable: bool) -> ClientBuilder {
|
|
828
|
+
self.config.accept_encoding.zstd = enable;
|
|
829
|
+
self
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/// Enable auto deflate decompression by checking the `Content-Encoding` response header.
|
|
833
|
+
///
|
|
834
|
+
/// If auto deflate decompression is turned on:
|
|
835
|
+
///
|
|
836
|
+
/// - When sending a request and if the request's headers do not already contain an
|
|
837
|
+
/// `Accept-Encoding` **and** `Range` values, the `Accept-Encoding` header is set to
|
|
838
|
+
/// `deflate`. The request body is **not** automatically compressed.
|
|
839
|
+
/// - When receiving a response, if it's headers contain a `Content-Encoding` value that equals
|
|
840
|
+
/// to `deflate`, both values `Content-Encoding` and `Content-Length` are removed from the
|
|
841
|
+
/// headers' set. The response body is automatically decompressed.
|
|
842
|
+
///
|
|
843
|
+
/// If the `deflate` feature is turned on, the default option is enabled.
|
|
844
|
+
///
|
|
845
|
+
/// # Optional
|
|
846
|
+
///
|
|
847
|
+
/// This requires the optional `deflate` feature to be enabled
|
|
848
|
+
#[inline]
|
|
849
|
+
#[cfg(feature = "deflate")]
|
|
850
|
+
#[cfg_attr(docsrs, doc(cfg(feature = "deflate")))]
|
|
851
|
+
pub fn deflate(mut self, enable: bool) -> ClientBuilder {
|
|
852
|
+
self.config.accept_encoding.deflate = enable;
|
|
853
|
+
self
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
/// Disable auto response body zstd decompression.
|
|
857
|
+
///
|
|
858
|
+
/// This method exists even if the optional `zstd` feature is not enabled.
|
|
859
|
+
/// This can be used to ensure a `Client` doesn't use zstd decompression
|
|
860
|
+
/// even if another dependency were to enable the optional `zstd` feature.
|
|
861
|
+
#[inline]
|
|
862
|
+
pub fn no_zstd(self) -> ClientBuilder {
|
|
863
|
+
#[cfg(feature = "zstd")]
|
|
864
|
+
{
|
|
865
|
+
self.zstd(false)
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
#[cfg(not(feature = "zstd"))]
|
|
869
|
+
{
|
|
870
|
+
self
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/// Disable auto response body gzip decompression.
|
|
875
|
+
///
|
|
876
|
+
/// This method exists even if the optional `gzip` feature is not enabled.
|
|
877
|
+
/// This can be used to ensure a `Client` doesn't use gzip decompression
|
|
878
|
+
/// even if another dependency were to enable the optional `gzip` feature.
|
|
879
|
+
#[inline]
|
|
880
|
+
pub fn no_gzip(self) -> ClientBuilder {
|
|
881
|
+
#[cfg(feature = "gzip")]
|
|
882
|
+
{
|
|
883
|
+
self.gzip(false)
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
#[cfg(not(feature = "gzip"))]
|
|
887
|
+
{
|
|
888
|
+
self
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/// Disable auto response body brotli decompression.
|
|
893
|
+
///
|
|
894
|
+
/// This method exists even if the optional `brotli` feature is not enabled.
|
|
895
|
+
/// This can be used to ensure a `Client` doesn't use brotli decompression
|
|
896
|
+
/// even if another dependency were to enable the optional `brotli` feature.
|
|
897
|
+
#[inline]
|
|
898
|
+
pub fn no_brotli(self) -> ClientBuilder {
|
|
899
|
+
#[cfg(feature = "brotli")]
|
|
900
|
+
{
|
|
901
|
+
self.brotli(false)
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
#[cfg(not(feature = "brotli"))]
|
|
905
|
+
{
|
|
906
|
+
self
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/// Disable auto response body deflate decompression.
|
|
911
|
+
///
|
|
912
|
+
/// This method exists even if the optional `deflate` feature is not enabled.
|
|
913
|
+
/// This can be used to ensure a `Client` doesn't use deflate decompression
|
|
914
|
+
/// even if another dependency were to enable the optional `deflate` feature.
|
|
915
|
+
#[inline]
|
|
916
|
+
pub fn no_deflate(self) -> ClientBuilder {
|
|
917
|
+
#[cfg(feature = "deflate")]
|
|
918
|
+
{
|
|
919
|
+
self.deflate(false)
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
#[cfg(not(feature = "deflate"))]
|
|
923
|
+
{
|
|
924
|
+
self
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// Redirect options
|
|
929
|
+
|
|
930
|
+
/// Set a `RedirectPolicy` for this client.
|
|
931
|
+
///
|
|
932
|
+
/// Default will follow redirects up to a maximum of 10.
|
|
933
|
+
#[inline]
|
|
934
|
+
pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
|
|
935
|
+
self.config.redirect_policy = policy;
|
|
936
|
+
self
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
/// Enable or disable automatic setting of the `Referer` header.
|
|
940
|
+
///
|
|
941
|
+
/// Default is `true`.
|
|
942
|
+
#[inline]
|
|
943
|
+
pub fn referer(mut self, enable: bool) -> ClientBuilder {
|
|
944
|
+
self.config.referer = enable;
|
|
945
|
+
self
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// Retry options
|
|
949
|
+
|
|
950
|
+
/// Set a request retry policy.
|
|
951
|
+
pub fn retry(mut self, policy: retry::Policy) -> ClientBuilder {
|
|
952
|
+
self.config.retry_policy = policy;
|
|
953
|
+
self
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
// Proxy options
|
|
957
|
+
|
|
958
|
+
/// Add a `Proxy` to the list of proxies the `Client` will use.
|
|
959
|
+
///
|
|
960
|
+
/// # Note
|
|
961
|
+
///
|
|
962
|
+
/// Adding a proxy will disable the automatic usage of the "system" proxy.
|
|
963
|
+
///
|
|
964
|
+
/// # Example
|
|
965
|
+
/// ```
|
|
966
|
+
/// use wreq::{Client, Proxy};
|
|
967
|
+
///
|
|
968
|
+
/// let proxy = Proxy::http("http://proxy:8080").unwrap();
|
|
969
|
+
/// let client = Client::builder().proxy(proxy).build().unwrap();
|
|
970
|
+
/// ```
|
|
971
|
+
#[inline]
|
|
972
|
+
pub fn proxy(mut self, proxy: Proxy) -> ClientBuilder {
|
|
973
|
+
self.config.proxies.push(proxy.into_matcher());
|
|
974
|
+
self.config.auto_sys_proxy = false;
|
|
975
|
+
self
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
/// Clear all `Proxies`, so `Client` will use no proxy anymore.
|
|
979
|
+
///
|
|
980
|
+
/// # Note
|
|
981
|
+
/// To add a proxy exclusion list, use [crate::proxy::Proxy::no_proxy()]
|
|
982
|
+
/// on all desired proxies instead.
|
|
983
|
+
///
|
|
984
|
+
/// This also disables the automatic usage of the "system" proxy.
|
|
985
|
+
#[inline]
|
|
986
|
+
pub fn no_proxy(mut self) -> ClientBuilder {
|
|
987
|
+
self.config.proxies.clear();
|
|
988
|
+
self.config.auto_sys_proxy = false;
|
|
989
|
+
self
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
// Timeout options
|
|
993
|
+
|
|
994
|
+
/// Enables a request timeout.
|
|
995
|
+
///
|
|
996
|
+
/// The timeout is applied from when the request starts connecting until the
|
|
997
|
+
/// response body has finished.
|
|
998
|
+
///
|
|
999
|
+
/// Default is no timeout.
|
|
1000
|
+
#[inline]
|
|
1001
|
+
pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
|
|
1002
|
+
self.config.timeout_options.total_timeout(timeout);
|
|
1003
|
+
self
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
/// Set a timeout for only the read phase of a `Client`.
|
|
1007
|
+
///
|
|
1008
|
+
/// Default is `None`.
|
|
1009
|
+
#[inline]
|
|
1010
|
+
pub fn read_timeout(mut self, timeout: Duration) -> ClientBuilder {
|
|
1011
|
+
self.config.timeout_options.read_timeout(timeout);
|
|
1012
|
+
self
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
/// Set a timeout for only the connect phase of a `Client`.
|
|
1016
|
+
///
|
|
1017
|
+
/// Default is `None`.
|
|
1018
|
+
///
|
|
1019
|
+
/// # Note
|
|
1020
|
+
///
|
|
1021
|
+
/// This **requires** the futures be executed in a tokio runtime with
|
|
1022
|
+
/// a tokio timer enabled.
|
|
1023
|
+
#[inline]
|
|
1024
|
+
pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder {
|
|
1025
|
+
self.config.connect_timeout = Some(timeout);
|
|
1026
|
+
self
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/// Set whether connections should emit verbose logs.
|
|
1030
|
+
///
|
|
1031
|
+
/// Enabling this option will emit [log][] messages at the `TRACE` level
|
|
1032
|
+
/// for read and write operations on connections.
|
|
1033
|
+
///
|
|
1034
|
+
/// [log]: https://crates.io/crates/log
|
|
1035
|
+
#[inline]
|
|
1036
|
+
pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder {
|
|
1037
|
+
self.config.connection_verbose = verbose;
|
|
1038
|
+
self
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// HTTP options
|
|
1042
|
+
|
|
1043
|
+
/// Set an optional timeout for idle sockets being kept-alive.
|
|
1044
|
+
///
|
|
1045
|
+
/// Pass `None` to disable timeout.
|
|
1046
|
+
///
|
|
1047
|
+
/// Default is 90 seconds.
|
|
1048
|
+
#[inline]
|
|
1049
|
+
pub fn pool_idle_timeout<D>(mut self, val: D) -> ClientBuilder
|
|
1050
|
+
where
|
|
1051
|
+
D: Into<Option<Duration>>,
|
|
1052
|
+
{
|
|
1053
|
+
self.config.pool_idle_timeout = val.into();
|
|
1054
|
+
self
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
/// Sets the maximum idle connection per host allowed in the pool.
|
|
1058
|
+
#[inline]
|
|
1059
|
+
pub fn pool_max_idle_per_host(mut self, max: usize) -> ClientBuilder {
|
|
1060
|
+
self.config.pool_max_idle_per_host = max;
|
|
1061
|
+
self
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
/// Sets the maximum number of connections in the pool.
|
|
1065
|
+
#[inline]
|
|
1066
|
+
pub fn pool_max_size(mut self, max: u32) -> ClientBuilder {
|
|
1067
|
+
self.config.pool_max_size = NonZeroU32::new(max);
|
|
1068
|
+
self
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/// Restrict the Client to be used with HTTPS only requests.
|
|
1072
|
+
///
|
|
1073
|
+
/// Defaults to false.
|
|
1074
|
+
#[inline]
|
|
1075
|
+
pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
|
|
1076
|
+
self.config.https_only = enabled;
|
|
1077
|
+
self
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
/// Only use HTTP/1.
|
|
1081
|
+
#[inline]
|
|
1082
|
+
pub fn http1_only(mut self) -> ClientBuilder {
|
|
1083
|
+
self.config.http_version_pref = HttpVersionPref::Http1;
|
|
1084
|
+
self
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
/// Only use HTTP/2.
|
|
1088
|
+
#[inline]
|
|
1089
|
+
pub fn http2_only(mut self) -> ClientBuilder {
|
|
1090
|
+
self.config.http_version_pref = HttpVersionPref::Http2;
|
|
1091
|
+
self
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
/// Sets the HTTP/1 options for the client.
|
|
1095
|
+
#[inline]
|
|
1096
|
+
pub fn http1_options(mut self, options: Http1Options) -> ClientBuilder {
|
|
1097
|
+
*self.config.transport_options.http1_options_mut() = Some(options);
|
|
1098
|
+
self
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
/// Sets the HTTP/2 options for the client.
|
|
1102
|
+
#[inline]
|
|
1103
|
+
pub fn http2_options(mut self, options: Http2Options) -> ClientBuilder {
|
|
1104
|
+
*self.config.transport_options.http2_options_mut() = Some(options);
|
|
1105
|
+
self
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
// TCP options
|
|
1109
|
+
|
|
1110
|
+
/// Set whether sockets have `TCP_NODELAY` enabled.
|
|
1111
|
+
///
|
|
1112
|
+
/// Default is `true`.
|
|
1113
|
+
#[inline]
|
|
1114
|
+
pub fn tcp_nodelay(mut self, enabled: bool) -> ClientBuilder {
|
|
1115
|
+
self.config.tcp_nodelay = enabled;
|
|
1116
|
+
self
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
/// Set that all sockets have `SO_KEEPALIVE` set with the supplied duration.
|
|
1120
|
+
///
|
|
1121
|
+
/// If `None`, the option will not be set.
|
|
1122
|
+
///
|
|
1123
|
+
/// Default is 15 seconds.
|
|
1124
|
+
#[inline]
|
|
1125
|
+
pub fn tcp_keepalive<D>(mut self, val: D) -> ClientBuilder
|
|
1126
|
+
where
|
|
1127
|
+
D: Into<Option<Duration>>,
|
|
1128
|
+
{
|
|
1129
|
+
self.config.tcp_keepalive = val.into();
|
|
1130
|
+
self
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
/// Set that all sockets have `SO_KEEPALIVE` set with the supplied interval.
|
|
1134
|
+
///
|
|
1135
|
+
/// If `None`, the option will not be set.
|
|
1136
|
+
///
|
|
1137
|
+
/// Default is 15 seconds.
|
|
1138
|
+
#[inline]
|
|
1139
|
+
pub fn tcp_keepalive_interval<D>(mut self, val: D) -> ClientBuilder
|
|
1140
|
+
where
|
|
1141
|
+
D: Into<Option<Duration>>,
|
|
1142
|
+
{
|
|
1143
|
+
self.config.tcp_keepalive_interval = val.into();
|
|
1144
|
+
self
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
/// Set that all sockets have `SO_KEEPALIVE` set with the supplied retry count.
|
|
1148
|
+
///
|
|
1149
|
+
/// If `None`, the option will not be set.
|
|
1150
|
+
///
|
|
1151
|
+
/// Default is 3 retries.
|
|
1152
|
+
#[inline]
|
|
1153
|
+
pub fn tcp_keepalive_retries<C>(mut self, retries: C) -> ClientBuilder
|
|
1154
|
+
where
|
|
1155
|
+
C: Into<Option<u32>>,
|
|
1156
|
+
{
|
|
1157
|
+
self.config.tcp_keepalive_retries = retries.into();
|
|
1158
|
+
self
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
/// Set that all sockets have `TCP_USER_TIMEOUT` set with the supplied duration.
|
|
1162
|
+
///
|
|
1163
|
+
/// This option controls how long transmitted data may remain unacknowledged before
|
|
1164
|
+
/// the connection is force-closed.
|
|
1165
|
+
///
|
|
1166
|
+
/// If `None`, the option will not be set.
|
|
1167
|
+
///
|
|
1168
|
+
/// Default is 30 seconds.
|
|
1169
|
+
#[inline]
|
|
1170
|
+
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
|
1171
|
+
#[cfg_attr(
|
|
1172
|
+
docsrs,
|
|
1173
|
+
doc(cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))
|
|
1174
|
+
)]
|
|
1175
|
+
pub fn tcp_user_timeout<D>(mut self, val: D) -> ClientBuilder
|
|
1176
|
+
where
|
|
1177
|
+
D: Into<Option<Duration>>,
|
|
1178
|
+
{
|
|
1179
|
+
self.config.tcp_user_timeout = val.into();
|
|
1180
|
+
self
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
/// Set whether sockets have `SO_REUSEADDR` enabled.
|
|
1184
|
+
#[inline]
|
|
1185
|
+
pub fn tcp_reuse_address(mut self, enabled: bool) -> ClientBuilder {
|
|
1186
|
+
self.config.tcp_reuse_address = enabled;
|
|
1187
|
+
self
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
/// Sets the size of the TCP send buffer on this client socket.
|
|
1191
|
+
///
|
|
1192
|
+
/// On most operating systems, this sets the `SO_SNDBUF` socket option.
|
|
1193
|
+
#[inline]
|
|
1194
|
+
pub fn tcp_send_buffer_size<S>(mut self, size: S) -> ClientBuilder
|
|
1195
|
+
where
|
|
1196
|
+
S: Into<Option<usize>>,
|
|
1197
|
+
{
|
|
1198
|
+
self.config.tcp_send_buffer_size = size.into();
|
|
1199
|
+
self
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
/// Sets the size of the TCP receive buffer on this client socket.
|
|
1203
|
+
///
|
|
1204
|
+
/// On most operating systems, this sets the `SO_RCVBUF` socket option.
|
|
1205
|
+
#[inline]
|
|
1206
|
+
pub fn tcp_recv_buffer_size<S>(mut self, size: S) -> ClientBuilder
|
|
1207
|
+
where
|
|
1208
|
+
S: Into<Option<usize>>,
|
|
1209
|
+
{
|
|
1210
|
+
self.config.tcp_recv_buffer_size = size.into();
|
|
1211
|
+
self
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
/// Set timeout for [RFC 6555 (Happy Eyeballs)][RFC 6555] algorithm.
|
|
1215
|
+
///
|
|
1216
|
+
/// If hostname resolves to both IPv4 and IPv6 addresses and connection
|
|
1217
|
+
/// cannot be established using preferred address family before timeout
|
|
1218
|
+
/// elapses, then connector will in parallel attempt connection using other
|
|
1219
|
+
/// address family.
|
|
1220
|
+
///
|
|
1221
|
+
/// If `None`, parallel connection attempts are disabled.
|
|
1222
|
+
///
|
|
1223
|
+
/// Default is 300 milliseconds.
|
|
1224
|
+
///
|
|
1225
|
+
/// [RFC 6555]: https://tools.ietf.org/html/rfc6555
|
|
1226
|
+
#[inline]
|
|
1227
|
+
pub fn tcp_happy_eyeballs_timeout<D>(mut self, val: D) -> ClientBuilder
|
|
1228
|
+
where
|
|
1229
|
+
D: Into<Option<Duration>>,
|
|
1230
|
+
{
|
|
1231
|
+
self.config.tcp_happy_eyeballs_timeout = val.into();
|
|
1232
|
+
self
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
/// Bind to a local IP Address.
|
|
1236
|
+
///
|
|
1237
|
+
/// # Example
|
|
1238
|
+
///
|
|
1239
|
+
/// ```
|
|
1240
|
+
/// use std::net::IpAddr;
|
|
1241
|
+
/// let local_addr = IpAddr::from([12, 4, 1, 8]);
|
|
1242
|
+
/// let client = wreq::Client::builder()
|
|
1243
|
+
/// .local_address(local_addr)
|
|
1244
|
+
/// .build()
|
|
1245
|
+
/// .unwrap();
|
|
1246
|
+
/// ```
|
|
1247
|
+
#[inline]
|
|
1248
|
+
pub fn local_address<T>(mut self, addr: T) -> ClientBuilder
|
|
1249
|
+
where
|
|
1250
|
+
T: Into<Option<IpAddr>>,
|
|
1251
|
+
{
|
|
1252
|
+
self.config
|
|
1253
|
+
.tcp_connect_options
|
|
1254
|
+
.set_local_address(addr.into());
|
|
1255
|
+
self
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
/// Set that all sockets are bound to the configured IPv4 or IPv6 address (depending on host's
|
|
1259
|
+
/// preferences) before connection.
|
|
1260
|
+
///
|
|
1261
|
+
/// # Example
|
|
1262
|
+
/// ///
|
|
1263
|
+
/// ```
|
|
1264
|
+
/// use std::net::{Ipv4Addr, Ipv6Addr};
|
|
1265
|
+
/// let ipv4 = Ipv4Addr::new(127, 0, 0, 1);
|
|
1266
|
+
/// let ipv6 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
|
|
1267
|
+
/// let client = wreq::Client::builder()
|
|
1268
|
+
/// .local_addresses(ipv4, ipv6)
|
|
1269
|
+
/// .build()
|
|
1270
|
+
/// .unwrap();
|
|
1271
|
+
/// ```
|
|
1272
|
+
#[inline]
|
|
1273
|
+
pub fn local_addresses<V4, V6>(mut self, ipv4: V4, ipv6: V6) -> ClientBuilder
|
|
1274
|
+
where
|
|
1275
|
+
V4: Into<Option<Ipv4Addr>>,
|
|
1276
|
+
V6: Into<Option<Ipv6Addr>>,
|
|
1277
|
+
{
|
|
1278
|
+
self.config
|
|
1279
|
+
.tcp_connect_options
|
|
1280
|
+
.set_local_addresses(ipv4, ipv6);
|
|
1281
|
+
self
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
/// Bind connections only on the specified network interface.
|
|
1285
|
+
///
|
|
1286
|
+
/// This option is only available on the following operating systems:
|
|
1287
|
+
///
|
|
1288
|
+
/// - Android
|
|
1289
|
+
/// - Fuchsia
|
|
1290
|
+
/// - Linux,
|
|
1291
|
+
/// - macOS and macOS-like systems (iOS, tvOS, watchOS and visionOS)
|
|
1292
|
+
/// - Solaris and illumos
|
|
1293
|
+
///
|
|
1294
|
+
/// On Android, Linux, and Fuchsia, this uses the
|
|
1295
|
+
/// [`SO_BINDTODEVICE`][man-7-socket] socket option. On macOS and macOS-like
|
|
1296
|
+
/// systems, Solaris, and illumos, this instead uses the [`IP_BOUND_IF` and
|
|
1297
|
+
/// `IPV6_BOUND_IF`][man-7p-ip] socket options (as appropriate).
|
|
1298
|
+
///
|
|
1299
|
+
/// Note that connections will fail if the provided interface name is not a
|
|
1300
|
+
/// network interface that currently exists when a connection is established.
|
|
1301
|
+
///
|
|
1302
|
+
/// # Example
|
|
1303
|
+
///
|
|
1304
|
+
/// ```
|
|
1305
|
+
/// # fn doc() -> Result<(), wreq::Error> {
|
|
1306
|
+
/// let interface = "lo";
|
|
1307
|
+
/// let client = wreq::Client::builder()
|
|
1308
|
+
/// .interface(interface)
|
|
1309
|
+
/// .build()?;
|
|
1310
|
+
/// # Ok(())
|
|
1311
|
+
/// # }
|
|
1312
|
+
/// ```
|
|
1313
|
+
///
|
|
1314
|
+
/// [man-7-socket]: https://man7.org/linux/man-pages/man7/socket.7.html
|
|
1315
|
+
/// [man-7p-ip]: https://docs.oracle.com/cd/E86824_01/html/E54777/ip-7p.html
|
|
1316
|
+
#[inline]
|
|
1317
|
+
#[cfg(any(
|
|
1318
|
+
target_os = "android",
|
|
1319
|
+
target_os = "fuchsia",
|
|
1320
|
+
target_os = "illumos",
|
|
1321
|
+
target_os = "ios",
|
|
1322
|
+
target_os = "linux",
|
|
1323
|
+
target_os = "macos",
|
|
1324
|
+
target_os = "solaris",
|
|
1325
|
+
target_os = "tvos",
|
|
1326
|
+
target_os = "visionos",
|
|
1327
|
+
target_os = "watchos",
|
|
1328
|
+
))]
|
|
1329
|
+
#[cfg_attr(
|
|
1330
|
+
docsrs,
|
|
1331
|
+
doc(cfg(any(
|
|
1332
|
+
target_os = "android",
|
|
1333
|
+
target_os = "fuchsia",
|
|
1334
|
+
target_os = "illumos",
|
|
1335
|
+
target_os = "ios",
|
|
1336
|
+
target_os = "linux",
|
|
1337
|
+
target_os = "macos",
|
|
1338
|
+
target_os = "solaris",
|
|
1339
|
+
target_os = "tvos",
|
|
1340
|
+
target_os = "visionos",
|
|
1341
|
+
target_os = "watchos",
|
|
1342
|
+
)))
|
|
1343
|
+
)]
|
|
1344
|
+
pub fn interface<T>(mut self, interface: T) -> ClientBuilder
|
|
1345
|
+
where
|
|
1346
|
+
T: Into<std::borrow::Cow<'static, str>>,
|
|
1347
|
+
{
|
|
1348
|
+
self.config.tcp_connect_options.set_interface(interface);
|
|
1349
|
+
self
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
// TLS options
|
|
1353
|
+
|
|
1354
|
+
/// Sets the identity to be used for client certificate authentication.
|
|
1355
|
+
#[inline]
|
|
1356
|
+
pub fn identity(mut self, identity: Identity) -> ClientBuilder {
|
|
1357
|
+
self.config.identity = Some(identity);
|
|
1358
|
+
self
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
/// Sets the verify certificate store for the client.
|
|
1362
|
+
///
|
|
1363
|
+
/// This method allows you to specify a custom verify certificate store to be used
|
|
1364
|
+
/// for TLS connections. By default, the system's verify certificate store is used.
|
|
1365
|
+
#[inline]
|
|
1366
|
+
pub fn cert_store(mut self, store: CertStore) -> ClientBuilder {
|
|
1367
|
+
self.config.cert_store = store;
|
|
1368
|
+
self
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
/// Controls the use of certificate validation.
|
|
1372
|
+
///
|
|
1373
|
+
/// Defaults to `true`.
|
|
1374
|
+
///
|
|
1375
|
+
/// # Warning
|
|
1376
|
+
///
|
|
1377
|
+
/// You should think very carefully before using this method. If
|
|
1378
|
+
/// invalid certificates are trusted, *any* certificate for *any* site
|
|
1379
|
+
/// will be trusted for use. This includes expired certificates. This
|
|
1380
|
+
/// introduces significant vulnerabilities, and should only be used
|
|
1381
|
+
/// as a last resort.
|
|
1382
|
+
#[inline]
|
|
1383
|
+
pub fn cert_verification(mut self, cert_verification: bool) -> ClientBuilder {
|
|
1384
|
+
self.config.cert_verification = cert_verification;
|
|
1385
|
+
self
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
/// Configures the use of hostname verification when connecting.
|
|
1389
|
+
///
|
|
1390
|
+
/// Defaults to `true`.
|
|
1391
|
+
/// # Warning
|
|
1392
|
+
///
|
|
1393
|
+
/// You should think very carefully before you use this method. If hostname verification is not
|
|
1394
|
+
/// used, *any* valid certificate for *any* site will be trusted for use from any other. This
|
|
1395
|
+
/// introduces a significant vulnerability to man-in-the-middle attacks.
|
|
1396
|
+
#[inline]
|
|
1397
|
+
pub fn verify_hostname(mut self, verify_hostname: bool) -> ClientBuilder {
|
|
1398
|
+
self.config.verify_hostname = verify_hostname;
|
|
1399
|
+
self
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
/// Configures the use of Server Name Indication (SNI) when connecting.
|
|
1403
|
+
///
|
|
1404
|
+
/// Defaults to `true`.
|
|
1405
|
+
#[inline]
|
|
1406
|
+
pub fn tls_sni(mut self, tls_sni: bool) -> ClientBuilder {
|
|
1407
|
+
self.config.tls_sni = tls_sni;
|
|
1408
|
+
self
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
/// Configures TLS key logging for the client.
|
|
1412
|
+
#[inline]
|
|
1413
|
+
pub fn keylog(mut self, keylog: KeyLog) -> ClientBuilder {
|
|
1414
|
+
self.config.keylog = Some(keylog);
|
|
1415
|
+
self
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
/// Set the minimum required TLS version for connections.
|
|
1419
|
+
///
|
|
1420
|
+
/// By default the TLS backend's own default is used.
|
|
1421
|
+
#[inline]
|
|
1422
|
+
pub fn min_tls_version(mut self, version: TlsVersion) -> ClientBuilder {
|
|
1423
|
+
self.config.min_tls_version = Some(version);
|
|
1424
|
+
self
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
/// Set the maximum allowed TLS version for connections.
|
|
1428
|
+
///
|
|
1429
|
+
/// By default there's no maximum.
|
|
1430
|
+
#[inline]
|
|
1431
|
+
pub fn max_tls_version(mut self, version: TlsVersion) -> ClientBuilder {
|
|
1432
|
+
self.config.max_tls_version = Some(version);
|
|
1433
|
+
self
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
/// Add TLS information as `TlsInfo` extension to responses.
|
|
1437
|
+
///
|
|
1438
|
+
/// # Optional
|
|
1439
|
+
///
|
|
1440
|
+
/// feature to be enabled.
|
|
1441
|
+
#[inline]
|
|
1442
|
+
pub fn tls_info(mut self, tls_info: bool) -> ClientBuilder {
|
|
1443
|
+
self.config.tls_info = tls_info;
|
|
1444
|
+
self
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
/// Sets the TLS options for the client.
|
|
1448
|
+
#[inline]
|
|
1449
|
+
pub fn tls_options(mut self, options: TlsOptions) -> ClientBuilder {
|
|
1450
|
+
*self.config.transport_options.tls_options_mut() = Some(options);
|
|
1451
|
+
self
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
// DNS options
|
|
1455
|
+
|
|
1456
|
+
/// Disables the hickory-dns async resolver.
|
|
1457
|
+
///
|
|
1458
|
+
/// This method exists even if the optional `hickory-dns` feature is not enabled.
|
|
1459
|
+
/// This can be used to ensure a `Client` doesn't use the hickory-dns async resolver
|
|
1460
|
+
/// even if another dependency were to enable the optional `hickory-dns` feature.
|
|
1461
|
+
#[inline]
|
|
1462
|
+
#[cfg(feature = "hickory-dns")]
|
|
1463
|
+
#[cfg_attr(docsrs, doc(cfg(feature = "hickory-dns")))]
|
|
1464
|
+
pub fn no_hickory_dns(mut self) -> ClientBuilder {
|
|
1465
|
+
self.config.hickory_dns = false;
|
|
1466
|
+
self
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
/// Override DNS resolution for specific domains to a particular IP address.
|
|
1470
|
+
///
|
|
1471
|
+
/// Warning
|
|
1472
|
+
///
|
|
1473
|
+
/// Since the DNS protocol has no notion of ports, if you wish to send
|
|
1474
|
+
/// traffic to a particular port you must include this port in the URI
|
|
1475
|
+
/// itself, any port in the overridden addr will be ignored and traffic sent
|
|
1476
|
+
/// to the conventional port for the given scheme (e.g. 80 for http).
|
|
1477
|
+
#[inline]
|
|
1478
|
+
pub fn resolve<D>(self, domain: D, addr: SocketAddr) -> ClientBuilder
|
|
1479
|
+
where
|
|
1480
|
+
D: Into<Cow<'static, str>>,
|
|
1481
|
+
{
|
|
1482
|
+
self.resolve_to_addrs(domain, std::iter::once(addr))
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
/// Override DNS resolution for specific domains to particular IP addresses.
|
|
1486
|
+
///
|
|
1487
|
+
/// Warning
|
|
1488
|
+
///
|
|
1489
|
+
/// Since the DNS protocol has no notion of ports, if you wish to send
|
|
1490
|
+
/// traffic to a particular port you must include this port in the URI
|
|
1491
|
+
/// itself, any port in the overridden addresses will be ignored and traffic sent
|
|
1492
|
+
/// to the conventional port for the given scheme (e.g. 80 for http).
|
|
1493
|
+
#[inline]
|
|
1494
|
+
pub fn resolve_to_addrs<D, A>(mut self, domain: D, addrs: A) -> ClientBuilder
|
|
1495
|
+
where
|
|
1496
|
+
D: Into<Cow<'static, str>>,
|
|
1497
|
+
A: IntoIterator<Item = SocketAddr>,
|
|
1498
|
+
{
|
|
1499
|
+
self.config
|
|
1500
|
+
.dns_overrides
|
|
1501
|
+
.insert(domain.into(), addrs.into_iter().collect());
|
|
1502
|
+
self
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
/// Override the DNS resolver implementation.
|
|
1506
|
+
///
|
|
1507
|
+
/// Pass any type implementing `IntoResolve`.
|
|
1508
|
+
/// Overrides for specific names passed to `resolve` and `resolve_to_addrs` will
|
|
1509
|
+
/// still be applied on top of this resolver.
|
|
1510
|
+
#[inline]
|
|
1511
|
+
pub fn dns_resolver<R>(mut self, resolver: R) -> ClientBuilder
|
|
1512
|
+
where
|
|
1513
|
+
R: IntoResolve,
|
|
1514
|
+
{
|
|
1515
|
+
self.config.dns_resolver = Some(resolver.into_resolve());
|
|
1516
|
+
self
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
// Tower middleware options
|
|
1520
|
+
|
|
1521
|
+
/// Adds a new Tower [`Layer`](https://docs.rs/tower/latest/tower/trait.Layer.html) to the
|
|
1522
|
+
/// request [`Service`](https://docs.rs/tower/latest/tower/trait.Service.html) which is responsible
|
|
1523
|
+
/// for request processing.
|
|
1524
|
+
///
|
|
1525
|
+
/// Each subsequent invocation of this function will wrap previous layers.
|
|
1526
|
+
///
|
|
1527
|
+
/// If configured, the `timeout` will be the outermost layer.
|
|
1528
|
+
///
|
|
1529
|
+
/// Example usage:
|
|
1530
|
+
/// ```
|
|
1531
|
+
/// use std::time::Duration;
|
|
1532
|
+
///
|
|
1533
|
+
/// let client = wreq::Client::builder()
|
|
1534
|
+
/// .timeout(Duration::from_millis(200))
|
|
1535
|
+
/// .layer(tower::timeout::TimeoutLayer::new(Duration::from_millis(50)))
|
|
1536
|
+
/// .build()
|
|
1537
|
+
/// .unwrap();
|
|
1538
|
+
/// ```
|
|
1539
|
+
#[inline]
|
|
1540
|
+
pub fn layer<L>(mut self, layer: L) -> ClientBuilder
|
|
1541
|
+
where
|
|
1542
|
+
L: Layer<BoxedClientService> + Clone + Send + Sync + 'static,
|
|
1543
|
+
L::Service: Service<http::Request<Body>, Response = http::Response<ResponseBody>, Error = BoxError>
|
|
1544
|
+
+ Clone
|
|
1545
|
+
+ Send
|
|
1546
|
+
+ Sync
|
|
1547
|
+
+ 'static,
|
|
1548
|
+
<L::Service as Service<http::Request<Body>>>::Future: Send + 'static,
|
|
1549
|
+
{
|
|
1550
|
+
let layer = BoxCloneSyncServiceLayer::new(layer);
|
|
1551
|
+
self.config.layers.push(layer);
|
|
1552
|
+
self
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
/// Adds a new Tower [`Layer`](https://docs.rs/tower/latest/tower/trait.Layer.html) to the
|
|
1556
|
+
/// base connector [`Service`](https://docs.rs/tower/latest/tower/trait.Service.html) which
|
|
1557
|
+
/// is responsible for connection establishment.a
|
|
1558
|
+
///
|
|
1559
|
+
/// Each subsequent invocation of this function will wrap previous layers.
|
|
1560
|
+
///
|
|
1561
|
+
/// If configured, the `connect_timeout` will be the outermost layer.
|
|
1562
|
+
///
|
|
1563
|
+
/// Example usage:
|
|
1564
|
+
/// ```
|
|
1565
|
+
/// use std::time::Duration;
|
|
1566
|
+
///
|
|
1567
|
+
/// let client = wreq::Client::builder()
|
|
1568
|
+
/// // resolved to outermost layer, meaning while we are waiting on concurrency limit
|
|
1569
|
+
/// .connect_timeout(Duration::from_millis(200))
|
|
1570
|
+
/// // underneath the concurrency check, so only after concurrency limit lets us through
|
|
1571
|
+
/// .connector_layer(tower::timeout::TimeoutLayer::new(Duration::from_millis(50)))
|
|
1572
|
+
/// .connector_layer(tower::limit::concurrency::ConcurrencyLimitLayer::new(2))
|
|
1573
|
+
/// .build()
|
|
1574
|
+
/// .unwrap();
|
|
1575
|
+
/// ```
|
|
1576
|
+
#[inline]
|
|
1577
|
+
pub fn connector_layer<L>(mut self, layer: L) -> ClientBuilder
|
|
1578
|
+
where
|
|
1579
|
+
L: Layer<BoxedConnectorService> + Clone + Send + Sync + 'static,
|
|
1580
|
+
L::Service:
|
|
1581
|
+
Service<Unnameable, Response = Conn, Error = BoxError> + Clone + Send + Sync + 'static,
|
|
1582
|
+
<L::Service as Service<Unnameable>>::Future: Send + 'static,
|
|
1583
|
+
{
|
|
1584
|
+
let layer = BoxCloneSyncServiceLayer::new(layer);
|
|
1585
|
+
self.config.connector_layers.push(layer);
|
|
1586
|
+
self
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
// TLS/HTTP2 emulation options
|
|
1590
|
+
|
|
1591
|
+
/// Configures the client builder to emulation the specified HTTP context.
|
|
1592
|
+
///
|
|
1593
|
+
/// This method sets the necessary headers, HTTP/1 and HTTP/2 options configurations, and TLS
|
|
1594
|
+
/// options config to use the specified HTTP context. It allows the client to mimic the
|
|
1595
|
+
/// behavior of different versions or setups, which can be useful for testing or ensuring
|
|
1596
|
+
/// compatibility with various environments.
|
|
1597
|
+
///
|
|
1598
|
+
/// # Note
|
|
1599
|
+
/// This will overwrite the existing configuration.
|
|
1600
|
+
/// You must set emulation before you can perform subsequent HTTP1/HTTP2/TLS fine-tuning.
|
|
1601
|
+
///
|
|
1602
|
+
/// # Example
|
|
1603
|
+
///
|
|
1604
|
+
/// ```rust
|
|
1605
|
+
/// use wreq::{
|
|
1606
|
+
/// Client,
|
|
1607
|
+
/// Emulation,
|
|
1608
|
+
/// };
|
|
1609
|
+
/// use wreq_util::Emulation;
|
|
1610
|
+
///
|
|
1611
|
+
/// let client = Client::builder()
|
|
1612
|
+
/// .emulation(Emulation::Firefox128)
|
|
1613
|
+
/// .build()
|
|
1614
|
+
/// .unwrap();
|
|
1615
|
+
/// ```
|
|
1616
|
+
#[inline]
|
|
1617
|
+
pub fn emulation<P>(mut self, factory: P) -> ClientBuilder
|
|
1618
|
+
where
|
|
1619
|
+
P: EmulationFactory,
|
|
1620
|
+
{
|
|
1621
|
+
let emulation = factory.emulation();
|
|
1622
|
+
let (transport_opts, headers, orig_headers) = emulation.into_parts();
|
|
1623
|
+
|
|
1624
|
+
self.config
|
|
1625
|
+
.transport_options
|
|
1626
|
+
.apply_transport_options(transport_opts);
|
|
1627
|
+
self.default_headers(headers).orig_headers(orig_headers)
|
|
1628
|
+
}
|
|
1629
|
+
}
|