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
data/vendor/wreq/src/cookie.rs
CHANGED
|
@@ -1,19 +1,12 @@
|
|
|
1
1
|
//! HTTP Cookies
|
|
2
2
|
|
|
3
|
-
use std::{convert::TryInto, fmt, sync::Arc, time::SystemTime};
|
|
3
|
+
use std::{collections::HashMap, convert::TryInto, fmt, sync::Arc, time::SystemTime};
|
|
4
4
|
|
|
5
5
|
use bytes::Bytes;
|
|
6
|
-
use cookie::{Cookie as RawCookie, CookieJar, Expiration, SameSite};
|
|
7
|
-
use http::Uri;
|
|
8
|
-
|
|
9
|
-
use crate::{
|
|
10
|
-
IntoUri,
|
|
11
|
-
error::Error,
|
|
12
|
-
ext::UriExt,
|
|
13
|
-
hash::{HASHER, HashMap},
|
|
14
|
-
header::HeaderValue,
|
|
15
|
-
sync::RwLock,
|
|
16
|
-
};
|
|
6
|
+
use cookie::{Cookie as RawCookie, CookieJar, Expiration, SameSite, time::Duration};
|
|
7
|
+
use http::{Uri, Version};
|
|
8
|
+
|
|
9
|
+
use crate::{IntoUri, error::Error, ext::UriExt, header::HeaderValue, sync::RwLock};
|
|
17
10
|
|
|
18
11
|
/// Cookie header values in two forms.
|
|
19
12
|
#[derive(Debug, Clone)]
|
|
@@ -34,23 +27,28 @@ pub trait CookieStore: Send + Sync {
|
|
|
34
27
|
/// Store a set of Set-Cookie header values received from `uri`
|
|
35
28
|
fn set_cookies(&self, cookie_headers: &mut dyn Iterator<Item = &HeaderValue>, uri: &Uri);
|
|
36
29
|
|
|
37
|
-
///
|
|
38
|
-
|
|
30
|
+
/// Returns cookies for the given URI and HTTP version.
|
|
31
|
+
///
|
|
32
|
+
/// Following [RFC 9112 §5.6.3], HTTP/1.1 combines all cookies into a single header.
|
|
33
|
+
/// For [HTTP/2] and above, cookies are sent as separate header fields
|
|
34
|
+
/// as per [RFC 9113 §8.1.2.5].
|
|
35
|
+
///
|
|
36
|
+
/// [RFC 9112 §5.6.3]: https://www.rfc-editor.org/rfc/rfc9112#section-5.6.3
|
|
37
|
+
/// [RFC 9113 §8.1.2.5]: https://www.rfc-editor.org/rfc/rfc9113#section-8.1.2.5
|
|
38
|
+
/// [HTTP/2]: https://datatracker.ietf.org/doc/html/rfc9113
|
|
39
|
+
fn cookies(&self, uri: &Uri, version: Version) -> Cookies;
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
///
|
|
43
|
-
/// Implemented for any [`CookieStore`] type, [`Arc<T>`] where `T: CookieStore`, and [`Arc<dyn
|
|
44
|
-
/// CookieStore>`]. Enables ergonomic conversion to a trait object for use in APIs without manual
|
|
45
|
-
/// boxing.
|
|
46
|
-
pub trait IntoCookieStore {
|
|
47
|
-
/// Converts the implementor into an [`Arc<dyn CookieStore>`].
|
|
42
|
+
impl_into_shared!(
|
|
43
|
+
/// Trait for converting types into a shared cookie store ([`Arc<dyn CookieStore>`]).
|
|
48
44
|
///
|
|
49
|
-
///
|
|
50
|
-
///
|
|
51
|
-
///
|
|
52
|
-
|
|
53
|
-
|
|
45
|
+
/// Implemented for any [`CookieStore`] type, [`Arc<T>`] where `T: CookieStore`, and [`Arc<dyn
|
|
46
|
+
/// CookieStore>`]. Enables ergonomic conversion to a trait object for use in APIs without manual
|
|
47
|
+
/// boxing.
|
|
48
|
+
pub trait IntoCookieStore => CookieStore
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
impl_request_config_value!(Arc<dyn CookieStore>);
|
|
54
52
|
|
|
55
53
|
/// Trait for converting types into an owned cookie ([`Cookie<'static>`]).
|
|
56
54
|
pub trait IntoCookie {
|
|
@@ -67,43 +65,8 @@ pub struct Cookie<'a>(RawCookie<'a>);
|
|
|
67
65
|
/// This is the implementation used when simply calling `cookie_store(true)`.
|
|
68
66
|
/// This type is exposed to allow creating one and filling it with some
|
|
69
67
|
/// existing cookies more easily, before creating a [`crate::Client`].
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
store: Arc<RwLock<HashMap<String, HashMap<String, CookieJar>>>>,
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// ===== impl CookieStore =====
|
|
76
|
-
|
|
77
|
-
impl_request_config_value!(Arc<dyn CookieStore>);
|
|
78
|
-
|
|
79
|
-
// ===== impl IntoCookieStore =====
|
|
80
|
-
|
|
81
|
-
impl IntoCookieStore for Arc<dyn CookieStore> {
|
|
82
|
-
#[inline]
|
|
83
|
-
fn into_cookie_store(self) -> Arc<dyn CookieStore> {
|
|
84
|
-
self
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
impl<R> IntoCookieStore for Arc<R>
|
|
89
|
-
where
|
|
90
|
-
R: CookieStore + 'static,
|
|
91
|
-
{
|
|
92
|
-
#[inline]
|
|
93
|
-
fn into_cookie_store(self) -> Arc<dyn CookieStore> {
|
|
94
|
-
self
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
impl<R> IntoCookieStore for R
|
|
99
|
-
where
|
|
100
|
-
R: CookieStore + 'static,
|
|
101
|
-
{
|
|
102
|
-
#[inline]
|
|
103
|
-
fn into_cookie_store(self) -> Arc<dyn CookieStore> {
|
|
104
|
-
Arc::new(self)
|
|
105
|
-
}
|
|
106
|
-
}
|
|
68
|
+
#[derive(Debug, Default)]
|
|
69
|
+
pub struct Jar(RwLock<HashMap<String, HashMap<String, CookieJar>>>);
|
|
107
70
|
|
|
108
71
|
// ===== impl IntoCookie =====
|
|
109
72
|
|
|
@@ -243,30 +206,6 @@ macro_rules! into_uri {
|
|
|
243
206
|
}
|
|
244
207
|
|
|
245
208
|
impl Jar {
|
|
246
|
-
/// Creates a new [`Jar`] with the specified compression setting.
|
|
247
|
-
pub fn new(compression: bool) -> Self {
|
|
248
|
-
Self {
|
|
249
|
-
compression,
|
|
250
|
-
store: Arc::new(RwLock::new(HashMap::with_hasher(HASHER))),
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/// Clone this [`Jar`], sharing storage but enabling compression.
|
|
255
|
-
pub fn compressed(self: &Arc<Self>) -> Arc<Self> {
|
|
256
|
-
Arc::new(Jar {
|
|
257
|
-
compression: true,
|
|
258
|
-
store: self.store.clone(),
|
|
259
|
-
})
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/// Clone this [`Jar`], sharing storage but disabling compression.
|
|
263
|
-
pub fn uncompressed(self: &Arc<Self>) -> Arc<Self> {
|
|
264
|
-
Arc::new(Jar {
|
|
265
|
-
compression: false,
|
|
266
|
-
store: self.store.clone(),
|
|
267
|
-
})
|
|
268
|
-
}
|
|
269
|
-
|
|
270
209
|
/// Get a cookie by name for a given Uri.
|
|
271
210
|
///
|
|
272
211
|
/// Returns the cookie with the specified name for the domain and path
|
|
@@ -276,16 +215,17 @@ impl Jar {
|
|
|
276
215
|
/// ```
|
|
277
216
|
/// use wreq::cookie::Jar;
|
|
278
217
|
/// let jar = Jar::default();
|
|
279
|
-
/// jar.
|
|
218
|
+
/// jar.add("foo=bar; Path=/foo; Domain=example.com", "http://example.com/foo");
|
|
280
219
|
/// let cookie = jar.get("foo", "http://example.com/foo").unwrap();
|
|
281
220
|
/// assert_eq!(cookie.value(), "bar");
|
|
282
221
|
/// ```
|
|
283
222
|
pub fn get<U: IntoUri>(&self, name: &str, uri: U) -> Option<Cookie<'static>> {
|
|
284
223
|
let uri = uri.into_uri().ok()?;
|
|
224
|
+
let host = normalize_domain(uri.host()?);
|
|
285
225
|
let cookie = self
|
|
286
|
-
.
|
|
226
|
+
.0
|
|
287
227
|
.read()
|
|
288
|
-
.get(
|
|
228
|
+
.get(host)?
|
|
289
229
|
.get(uri.path())?
|
|
290
230
|
.get(name)?
|
|
291
231
|
.clone()
|
|
@@ -302,20 +242,30 @@ impl Jar {
|
|
|
302
242
|
/// ```
|
|
303
243
|
/// use wreq::cookie::Jar;
|
|
304
244
|
/// let jar = Jar::default();
|
|
305
|
-
/// jar.
|
|
245
|
+
/// jar.add("foo=bar; Domain=example.com", "http://example.com");
|
|
306
246
|
/// for cookie in jar.get_all() {
|
|
307
247
|
/// println!("{}={}", cookie.name(), cookie.value());
|
|
308
248
|
/// }
|
|
309
249
|
/// ```
|
|
310
250
|
pub fn get_all(&self) -> impl Iterator<Item = Cookie<'static>> {
|
|
311
|
-
self.
|
|
251
|
+
self.0
|
|
312
252
|
.read()
|
|
313
253
|
.iter()
|
|
314
|
-
.flat_map(|(
|
|
315
|
-
path_map.iter().flat_map(|(
|
|
316
|
-
name_map
|
|
317
|
-
.
|
|
318
|
-
|
|
254
|
+
.flat_map(|(domain, path_map)| {
|
|
255
|
+
path_map.iter().flat_map(|(path, name_map)| {
|
|
256
|
+
name_map.iter().map(|cookie| {
|
|
257
|
+
let mut cookie = cookie.clone().into_owned();
|
|
258
|
+
|
|
259
|
+
if cookie.domain().is_none() {
|
|
260
|
+
cookie.set_domain(domain.to_owned());
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if cookie.path().is_none() {
|
|
264
|
+
cookie.set_path(path.to_owned());
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
Cookie(cookie)
|
|
268
|
+
})
|
|
319
269
|
})
|
|
320
270
|
})
|
|
321
271
|
.collect::<Vec<_>>()
|
|
@@ -348,19 +298,51 @@ impl Jar {
|
|
|
348
298
|
U: IntoUri,
|
|
349
299
|
{
|
|
350
300
|
if let Some(cookie) = cookie.into_cookie() {
|
|
351
|
-
let cookie: RawCookie<'static> = cookie.into();
|
|
352
301
|
let uri = into_uri!(uri);
|
|
353
|
-
let
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
302
|
+
let mut cookie: RawCookie<'static> = cookie.into();
|
|
303
|
+
|
|
304
|
+
// If the request-uri contains no host component:
|
|
305
|
+
let Some(host) = uri.host() else {
|
|
306
|
+
return;
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
// If the canonicalized request-host does not domain-match the
|
|
310
|
+
// domain-attribute:
|
|
311
|
+
// Ignore the cookie entirely and abort these steps.
|
|
312
|
+
//
|
|
313
|
+
// RFC 6265 §5.3 + §5.1.3:
|
|
314
|
+
// https://datatracker.ietf.org/doc/html/rfc6265#section-5.3
|
|
315
|
+
// https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.3
|
|
316
|
+
let domain = if let Some(domain) = cookie.domain() {
|
|
317
|
+
let domain = normalize_domain(domain);
|
|
318
|
+
if domain.is_empty() || !domain_match(normalize_domain(host), domain) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
domain
|
|
322
|
+
} else {
|
|
323
|
+
normalize_domain(host)
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
// If the request-uri contains no path component or if the first character of the
|
|
327
|
+
// path component of the request-uri is not a %x2F ("/") OR if the cookie's path-
|
|
328
|
+
// attribute is missing or does not start with a %x2F ("/"):
|
|
329
|
+
// Let cookie-path be the default-path of the request-uri.
|
|
330
|
+
// Otherwise:
|
|
331
|
+
// Let cookie-path be the substring of the request-uri's path from the first
|
|
332
|
+
// character up to, not including, the right-most %x2F ("/").
|
|
333
|
+
//
|
|
334
|
+
// RFC 6265 §5.2.4 + §5.1.4:
|
|
335
|
+
// https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.4
|
|
336
|
+
// https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.4
|
|
337
|
+
let path = cookie
|
|
338
|
+
.path()
|
|
339
|
+
.filter(|path| path.starts_with(DEFAULT_PATH))
|
|
340
|
+
.unwrap_or_else(|| normalize_path(uri.path()));
|
|
341
|
+
|
|
342
|
+
let mut inner = self.0.write();
|
|
361
343
|
let name_map = inner
|
|
362
344
|
.entry(domain.to_owned())
|
|
363
|
-
.
|
|
345
|
+
.or_default()
|
|
364
346
|
.entry(path.to_owned())
|
|
365
347
|
.or_default();
|
|
366
348
|
|
|
@@ -368,11 +350,12 @@ impl Jar {
|
|
|
368
350
|
let expired = cookie
|
|
369
351
|
.expires_datetime()
|
|
370
352
|
.is_some_and(|dt| dt <= SystemTime::now())
|
|
371
|
-
|| cookie.max_age().is_some_and(
|
|
353
|
+
|| cookie.max_age().is_some_and(Duration::is_zero);
|
|
372
354
|
|
|
373
355
|
if expired {
|
|
374
356
|
name_map.remove(cookie);
|
|
375
357
|
} else {
|
|
358
|
+
cookie.set_path(path.to_owned());
|
|
376
359
|
name_map.add(cookie);
|
|
377
360
|
}
|
|
378
361
|
}
|
|
@@ -387,7 +370,7 @@ impl Jar {
|
|
|
387
370
|
/// ```
|
|
388
371
|
/// use wreq::cookie::Jar;
|
|
389
372
|
/// let jar = Jar::default();
|
|
390
|
-
/// jar.
|
|
373
|
+
/// jar.add("foo=bar; Path=/foo; Domain=example.com", "http://example.com/foo");
|
|
391
374
|
/// assert!(jar.get("foo", "http://example.com/foo").is_some());
|
|
392
375
|
/// jar.remove("foo", "http://example.com/foo");
|
|
393
376
|
/// assert!(jar.get("foo", "http://example.com/foo").is_none());
|
|
@@ -399,7 +382,8 @@ impl Jar {
|
|
|
399
382
|
{
|
|
400
383
|
let uri = into_uri!(uri);
|
|
401
384
|
if let Some(host) = uri.host() {
|
|
402
|
-
let
|
|
385
|
+
let host = normalize_domain(host);
|
|
386
|
+
let mut inner = self.0.write();
|
|
403
387
|
if let Some(path_map) = inner.get_mut(host) {
|
|
404
388
|
if let Some(name_map) = path_map.get_mut(uri.path()) {
|
|
405
389
|
name_map.remove(cookie.into());
|
|
@@ -416,13 +400,13 @@ impl Jar {
|
|
|
416
400
|
/// ```
|
|
417
401
|
/// use wreq::cookie::Jar;
|
|
418
402
|
/// let jar = Jar::default();
|
|
419
|
-
/// jar.
|
|
403
|
+
/// jar.add("foo=bar; Domain=example.com", "http://example.com");
|
|
420
404
|
/// assert_eq!(jar.get_all().count(), 1);
|
|
421
405
|
/// jar.clear();
|
|
422
406
|
/// assert_eq!(jar.get_all().count(), 0);
|
|
423
407
|
/// ```
|
|
424
408
|
pub fn clear(&self) {
|
|
425
|
-
self.
|
|
409
|
+
self.0.write().clear();
|
|
426
410
|
}
|
|
427
411
|
}
|
|
428
412
|
|
|
@@ -438,13 +422,13 @@ impl CookieStore for Jar {
|
|
|
438
422
|
}
|
|
439
423
|
}
|
|
440
424
|
|
|
441
|
-
fn cookies(&self, uri: &Uri) -> Cookies {
|
|
425
|
+
fn cookies(&self, uri: &Uri, version: Version) -> Cookies {
|
|
442
426
|
let host = match uri.host() {
|
|
443
|
-
Some(h) => h,
|
|
427
|
+
Some(h) => normalize_domain(h),
|
|
444
428
|
None => return Cookies::Empty,
|
|
445
429
|
};
|
|
446
430
|
|
|
447
|
-
let store = self.
|
|
431
|
+
let store = self.0.read();
|
|
448
432
|
let iter = store
|
|
449
433
|
.iter()
|
|
450
434
|
.filter(|(domain, _)| domain_match(host, domain))
|
|
@@ -470,7 +454,24 @@ impl CookieStore for Jar {
|
|
|
470
454
|
})
|
|
471
455
|
});
|
|
472
456
|
|
|
473
|
-
if
|
|
457
|
+
if matches!(version, Version::HTTP_2 | Version::HTTP_3) {
|
|
458
|
+
let cookies = iter
|
|
459
|
+
.map(|cookie| {
|
|
460
|
+
let name = cookie.name();
|
|
461
|
+
let value = cookie.value();
|
|
462
|
+
|
|
463
|
+
let mut cookie_str = String::with_capacity(name.len() + 1 + value.len());
|
|
464
|
+
cookie_str.push_str(name);
|
|
465
|
+
cookie_str.push('=');
|
|
466
|
+
cookie_str.push_str(value);
|
|
467
|
+
|
|
468
|
+
HeaderValue::from_maybe_shared(Bytes::from(cookie_str))
|
|
469
|
+
})
|
|
470
|
+
.filter_map(Result::ok)
|
|
471
|
+
.collect();
|
|
472
|
+
|
|
473
|
+
Cookies::Uncompressed(cookies)
|
|
474
|
+
} else {
|
|
474
475
|
let cookies = iter.fold(String::new(), |mut cookies, cookie| {
|
|
475
476
|
if !cookies.is_empty() {
|
|
476
477
|
cookies.push_str("; ");
|
|
@@ -488,33 +489,10 @@ impl CookieStore for Jar {
|
|
|
488
489
|
HeaderValue::from_maybe_shared(Bytes::from(cookies))
|
|
489
490
|
.map(Cookies::Compressed)
|
|
490
491
|
.unwrap_or(Cookies::Empty)
|
|
491
|
-
} else {
|
|
492
|
-
let cookies = iter
|
|
493
|
-
.map(|cookie| {
|
|
494
|
-
let name = cookie.name();
|
|
495
|
-
let value = cookie.value();
|
|
496
|
-
|
|
497
|
-
let mut cookie_str = String::with_capacity(name.len() + 1 + value.len());
|
|
498
|
-
cookie_str.push_str(name);
|
|
499
|
-
cookie_str.push('=');
|
|
500
|
-
cookie_str.push_str(value);
|
|
501
|
-
|
|
502
|
-
HeaderValue::from_maybe_shared(Bytes::from(cookie_str))
|
|
503
|
-
})
|
|
504
|
-
.filter_map(Result::ok)
|
|
505
|
-
.collect();
|
|
506
|
-
|
|
507
|
-
Cookies::Uncompressed(cookies)
|
|
508
492
|
}
|
|
509
493
|
}
|
|
510
494
|
}
|
|
511
495
|
|
|
512
|
-
impl Default for Jar {
|
|
513
|
-
fn default() -> Self {
|
|
514
|
-
Self::new(true)
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
|
|
518
496
|
const DEFAULT_PATH: &str = "/";
|
|
519
497
|
|
|
520
498
|
/// Determines if the given `host` matches the cookie `domain` according to
|
|
@@ -556,7 +534,11 @@ fn path_match(req_path: &str, cookie_path: &str) -> bool {
|
|
|
556
534
|
/// the domain attribute of a cookie must not include a port. If a port is present (non-standard),
|
|
557
535
|
/// it will be ignored for domain matching purposes.
|
|
558
536
|
fn normalize_domain(domain: &str) -> &str {
|
|
559
|
-
domain.split(':').next().unwrap_or(domain)
|
|
537
|
+
let host_without_port = domain.split(':').next().unwrap_or(domain);
|
|
538
|
+
let without_leading = host_without_port
|
|
539
|
+
.strip_prefix(".")
|
|
540
|
+
.unwrap_or(host_without_port);
|
|
541
|
+
without_leading.strip_suffix(".").unwrap_or(without_leading)
|
|
560
542
|
}
|
|
561
543
|
|
|
562
544
|
/// Computes the normalized default path for a cookie as specified in
|
|
@@ -564,8 +546,7 @@ fn normalize_domain(domain: &str) -> &str {
|
|
|
564
546
|
///
|
|
565
547
|
/// This function normalizes the path for a cookie, ensuring it matches
|
|
566
548
|
/// browser and server expectations for default cookie scope.
|
|
567
|
-
fn normalize_path(
|
|
568
|
-
let path = uri.path();
|
|
549
|
+
fn normalize_path(path: &str) -> &str {
|
|
569
550
|
if !path.starts_with(DEFAULT_PATH) {
|
|
570
551
|
return DEFAULT_PATH;
|
|
571
552
|
}
|
|
@@ -577,3 +558,182 @@ fn normalize_path(uri: &Uri) -> &str {
|
|
|
577
558
|
}
|
|
578
559
|
DEFAULT_PATH
|
|
579
560
|
}
|
|
561
|
+
|
|
562
|
+
#[cfg(test)]
|
|
563
|
+
mod tests {
|
|
564
|
+
use http::{Uri, Version};
|
|
565
|
+
|
|
566
|
+
use super::{CookieStore, Cookies, Jar};
|
|
567
|
+
|
|
568
|
+
#[test]
|
|
569
|
+
fn jar_get_all_backfills_domain_and_path() {
|
|
570
|
+
let jar = Jar::default();
|
|
571
|
+
jar.add("session=abc", "http://example.com/foo/bar");
|
|
572
|
+
|
|
573
|
+
let cookies = jar.get_all().collect::<Vec<_>>();
|
|
574
|
+
assert_eq!(cookies.len(), 1);
|
|
575
|
+
|
|
576
|
+
let cookie = &cookies[0];
|
|
577
|
+
assert_eq!(cookie.name(), "session");
|
|
578
|
+
assert_eq!(cookie.value(), "abc");
|
|
579
|
+
assert_eq!(cookie.domain(), Some("example.com"));
|
|
580
|
+
assert_eq!(cookie.path(), Some("/foo"));
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
#[test]
|
|
584
|
+
fn jar_get_all_keeps_existing_domain_and_path() {
|
|
585
|
+
let jar = Jar::default();
|
|
586
|
+
jar.add(
|
|
587
|
+
"session=abc; Domain=example.com; Path=/custom",
|
|
588
|
+
"http://example.com/foo/bar",
|
|
589
|
+
);
|
|
590
|
+
|
|
591
|
+
let cookies = jar.get_all().collect::<Vec<_>>();
|
|
592
|
+
assert_eq!(cookies.len(), 1);
|
|
593
|
+
|
|
594
|
+
let cookie = &cookies[0];
|
|
595
|
+
assert_eq!(cookie.name(), "session");
|
|
596
|
+
assert_eq!(cookie.value(), "abc");
|
|
597
|
+
assert_eq!(cookie.domain(), Some("example.com"));
|
|
598
|
+
assert_eq!(cookie.path(), Some("/custom"));
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
#[test]
|
|
602
|
+
fn jar_get_all_backfills_only_missing_field() {
|
|
603
|
+
let jar = Jar::default();
|
|
604
|
+
jar.add("a=1; Domain=example.com", "http://example.com/foo/bar");
|
|
605
|
+
jar.add("b=2; Path=/fixed", "http://example.com/foo/bar");
|
|
606
|
+
|
|
607
|
+
let mut cookies = jar.get_all().collect::<Vec<_>>();
|
|
608
|
+
cookies.sort_by(|left, right| left.name().cmp(right.name()));
|
|
609
|
+
|
|
610
|
+
let a = &cookies[0];
|
|
611
|
+
assert_eq!(a.name(), "a");
|
|
612
|
+
assert_eq!(a.domain(), Some("example.com"));
|
|
613
|
+
assert_eq!(a.path(), Some("/foo"));
|
|
614
|
+
|
|
615
|
+
let b = &cookies[1];
|
|
616
|
+
assert_eq!(b.name(), "b");
|
|
617
|
+
assert_eq!(b.domain(), Some("example.com"));
|
|
618
|
+
assert_eq!(b.path(), Some("/fixed"));
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
#[test]
|
|
622
|
+
fn jar_add_rejects_mismatched_domain() {
|
|
623
|
+
let jar = Jar::default();
|
|
624
|
+
jar.add("session=abc; Domain=other.com", "http://example.com/foo");
|
|
625
|
+
|
|
626
|
+
assert_eq!(jar.get_all().count(), 0);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
#[test]
|
|
630
|
+
fn jar_add_accepts_matching_parent_domain() {
|
|
631
|
+
let jar = Jar::default();
|
|
632
|
+
jar.add(
|
|
633
|
+
"session=abc; Domain=example.com",
|
|
634
|
+
"http://api.example.com/foo",
|
|
635
|
+
);
|
|
636
|
+
|
|
637
|
+
let cookies = jar.get_all().collect::<Vec<_>>();
|
|
638
|
+
assert_eq!(cookies.len(), 1);
|
|
639
|
+
assert_eq!(cookies[0].domain(), Some("example.com"));
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
#[test]
|
|
643
|
+
fn jar_get_all_export_import_keeps_effective_path() {
|
|
644
|
+
let source = Jar::default();
|
|
645
|
+
source.add("session=abc", "http://example.com/foo/bar");
|
|
646
|
+
|
|
647
|
+
let exported = source.get_all().collect::<Vec<_>>();
|
|
648
|
+
assert_eq!(exported.len(), 1);
|
|
649
|
+
assert_eq!(exported[0].path(), Some("/foo"));
|
|
650
|
+
|
|
651
|
+
let target = Jar::default();
|
|
652
|
+
for cookie in exported {
|
|
653
|
+
target.add(cookie, "http://example.com/another/deeper");
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
let imported = target.get_all().collect::<Vec<_>>();
|
|
657
|
+
assert_eq!(imported.len(), 1);
|
|
658
|
+
assert_eq!(imported[0].path(), Some("/foo"));
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
#[test]
|
|
662
|
+
fn cookie_store_invalid_explicit_path_falls_back_to_default_path() {
|
|
663
|
+
let jar = Jar::default();
|
|
664
|
+
jar.add("key=val; Path=noslash", "http://example.com/foo/bar");
|
|
665
|
+
|
|
666
|
+
assert!(jar.get("key", "http://example.com/foo").is_some());
|
|
667
|
+
assert!(jar.get("key", "http://example.com/noslash").is_none());
|
|
668
|
+
|
|
669
|
+
let cookies = jar.get_all().collect::<Vec<_>>();
|
|
670
|
+
assert_eq!(cookies.len(), 1);
|
|
671
|
+
assert_eq!(cookies[0].path(), Some("/foo"));
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
#[test]
|
|
675
|
+
fn jar_sends_parent_domain_cookie_to_subdomain() {
|
|
676
|
+
let jar = Jar::default();
|
|
677
|
+
jar.add(
|
|
678
|
+
"session=abc; Domain=example.com; Path=/",
|
|
679
|
+
"http://example.com/login",
|
|
680
|
+
);
|
|
681
|
+
|
|
682
|
+
let should_receive = [
|
|
683
|
+
"http://example.com/dashboard",
|
|
684
|
+
"http://api.example.com/dashboard",
|
|
685
|
+
"http://sub.api.example.com/dashboard",
|
|
686
|
+
];
|
|
687
|
+
for uri_str in &should_receive {
|
|
688
|
+
let uri = Uri::from_static(uri_str);
|
|
689
|
+
match jar.cookies(&uri, Version::HTTP_11) {
|
|
690
|
+
Cookies::Compressed(v) => assert_eq!(
|
|
691
|
+
v.to_str().unwrap(),
|
|
692
|
+
"session=abc",
|
|
693
|
+
"expected cookie to be sent to {uri_str}"
|
|
694
|
+
),
|
|
695
|
+
other => panic!("expected Compressed cookie for {uri_str}, got {other:?}"),
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
let should_not_receive = [
|
|
700
|
+
"http://notexample.com/dashboard",
|
|
701
|
+
"http://fakeexample.com/dashboard",
|
|
702
|
+
];
|
|
703
|
+
for uri_str in &should_not_receive {
|
|
704
|
+
let uri = Uri::from_static(uri_str);
|
|
705
|
+
assert!(
|
|
706
|
+
matches!(jar.cookies(&uri, Version::HTTP_11), Cookies::Empty),
|
|
707
|
+
"cookie must NOT be sent to {uri_str}"
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
#[test]
|
|
713
|
+
fn jar_subdomain_cookie_does_not_leak_to_parent_or_sibling() {
|
|
714
|
+
let jar = Jar::default();
|
|
715
|
+
jar.add(
|
|
716
|
+
"token=xyz; Domain=api.example.com; Path=/",
|
|
717
|
+
"http://api.example.com/",
|
|
718
|
+
);
|
|
719
|
+
|
|
720
|
+
let uri = Uri::from_static("http://api.example.com/");
|
|
721
|
+
assert!(
|
|
722
|
+
matches!(jar.cookies(&uri, Version::HTTP_11), Cookies::Compressed(_)),
|
|
723
|
+
"cookie must be sent to api.example.com"
|
|
724
|
+
);
|
|
725
|
+
|
|
726
|
+
let must_not_receive = [
|
|
727
|
+
"http://example.com/",
|
|
728
|
+
"http://other.example.com/",
|
|
729
|
+
"http://notapi.example.com/",
|
|
730
|
+
];
|
|
731
|
+
for uri_str in &must_not_receive {
|
|
732
|
+
let uri = Uri::from_static(uri_str);
|
|
733
|
+
assert!(
|
|
734
|
+
matches!(jar.cookies(&uri, Version::HTTP_11), Cookies::Empty),
|
|
735
|
+
"cookie must NOT leak to {uri_str}"
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// This module contains the `GaiResolver` implementation for the `compio` runtime.
|
|
2
|
+
|
|
3
|
+
use std::{
|
|
4
|
+
future::Future,
|
|
5
|
+
io,
|
|
6
|
+
net::ToSocketAddrs,
|
|
7
|
+
pin::Pin,
|
|
8
|
+
task::{self, Poll},
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
use tower::Service;
|
|
12
|
+
|
|
13
|
+
use super::{GaiAddrs, GaiResolver};
|
|
14
|
+
use crate::dns::{Addrs, Name, Resolve, Resolving, SocketAddrs};
|
|
15
|
+
|
|
16
|
+
/// A future to resolve a name returned by `GaiResolver`.
|
|
17
|
+
pub struct GaiFuture {
|
|
18
|
+
inner: compio::runtime::JoinHandle<Result<SocketAddrs, io::Error>>,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ==== impl GaiResolver ====
|
|
22
|
+
|
|
23
|
+
impl GaiResolver {
|
|
24
|
+
/// Creates a new [`GaiResolver`].
|
|
25
|
+
pub fn new() -> Self {
|
|
26
|
+
GaiResolver { _priv: () }
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
impl Service<Name> for GaiResolver {
|
|
31
|
+
type Response = GaiAddrs;
|
|
32
|
+
type Error = io::Error;
|
|
33
|
+
type Future = GaiFuture;
|
|
34
|
+
|
|
35
|
+
#[inline]
|
|
36
|
+
fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<Result<(), io::Error>> {
|
|
37
|
+
Poll::Ready(Ok(()))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
fn call(&mut self, name: Name) -> Self::Future {
|
|
41
|
+
let blocking = compio::runtime::spawn_blocking(move || {
|
|
42
|
+
debug!("resolving {}", name);
|
|
43
|
+
(name.as_str(), 0)
|
|
44
|
+
.to_socket_addrs()
|
|
45
|
+
.map(|i| SocketAddrs::new(i.collect()))
|
|
46
|
+
});
|
|
47
|
+
GaiFuture { inner: blocking }
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
impl Resolve for GaiResolver {
|
|
52
|
+
fn resolve(&self, name: Name) -> Resolving {
|
|
53
|
+
let mut this = self.clone();
|
|
54
|
+
Box::pin(async move {
|
|
55
|
+
this.call(name)
|
|
56
|
+
.await
|
|
57
|
+
.map(|addrs| Box::new(addrs) as Addrs)
|
|
58
|
+
.map_err(Into::into)
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ==== impl GaiFuture ====
|
|
64
|
+
|
|
65
|
+
impl Future for GaiFuture {
|
|
66
|
+
type Output = Result<GaiAddrs, io::Error>;
|
|
67
|
+
|
|
68
|
+
fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
|
|
69
|
+
Pin::new(&mut self.inner).poll(cx).map(|res| match res {
|
|
70
|
+
Ok(Ok(addrs)) => Ok(GaiAddrs { inner: addrs }),
|
|
71
|
+
Ok(Err(err)) => Err(err),
|
|
72
|
+
Err(join_err) => Err(io::Error::other(format!(
|
|
73
|
+
"DNS resolution blocked task panicked: {join_err}"
|
|
74
|
+
))),
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
}
|