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,115 @@
|
|
|
1
|
+
use wreq::{
|
|
2
|
+
Emulation,
|
|
3
|
+
header::{self, HeaderMap, HeaderValue, OrigHeaderMap},
|
|
4
|
+
http2::{Http2Options, PseudoId, PseudoOrder},
|
|
5
|
+
tls::{AlpnProtocol, TlsOptions, TlsVersion},
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
macro_rules! join {
|
|
9
|
+
($sep:expr, $first:expr $(, $rest:expr)*) => {
|
|
10
|
+
concat!($first $(, $sep, $rest)*)
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
#[tokio::main]
|
|
15
|
+
async fn main() -> wreq::Result<()> {
|
|
16
|
+
tracing_subscriber::fmt()
|
|
17
|
+
.with_max_level(tracing::Level::TRACE)
|
|
18
|
+
.init();
|
|
19
|
+
|
|
20
|
+
// TLS options config
|
|
21
|
+
let tls = TlsOptions::builder()
|
|
22
|
+
.enable_ocsp_stapling(true)
|
|
23
|
+
.curves_list(join!(":", "X25519", "P-256", "P-384"))
|
|
24
|
+
.cipher_list(join!(
|
|
25
|
+
":",
|
|
26
|
+
"TLS_AES_128_GCM_SHA256",
|
|
27
|
+
"TLS_AES_256_GCM_SHA384",
|
|
28
|
+
"TLS_CHACHA20_POLY1305_SHA256",
|
|
29
|
+
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
|
30
|
+
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
|
31
|
+
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
|
|
32
|
+
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
|
33
|
+
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
|
34
|
+
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"
|
|
35
|
+
))
|
|
36
|
+
.sigalgs_list(join!(
|
|
37
|
+
":",
|
|
38
|
+
"ecdsa_secp256r1_sha256",
|
|
39
|
+
"rsa_pss_rsae_sha256",
|
|
40
|
+
"rsa_pkcs1_sha256",
|
|
41
|
+
"ecdsa_secp384r1_sha384",
|
|
42
|
+
"rsa_pss_rsae_sha384",
|
|
43
|
+
"rsa_pkcs1_sha384",
|
|
44
|
+
"rsa_pss_rsae_sha512",
|
|
45
|
+
"rsa_pkcs1_sha512",
|
|
46
|
+
"rsa_pkcs1_sha1"
|
|
47
|
+
))
|
|
48
|
+
.alpn_protocols([AlpnProtocol::HTTP2, AlpnProtocol::HTTP1])
|
|
49
|
+
.min_tls_version(TlsVersion::TLS_1_2)
|
|
50
|
+
.max_tls_version(TlsVersion::TLS_1_3)
|
|
51
|
+
.build();
|
|
52
|
+
|
|
53
|
+
// HTTP/2 options config
|
|
54
|
+
let http2 = Http2Options::builder()
|
|
55
|
+
.initial_stream_id(3)
|
|
56
|
+
.initial_window_size(16777216)
|
|
57
|
+
.initial_connection_window_size(16711681 + 65535)
|
|
58
|
+
.headers_pseudo_order(
|
|
59
|
+
PseudoOrder::builder()
|
|
60
|
+
.extend([
|
|
61
|
+
PseudoId::Method,
|
|
62
|
+
PseudoId::Path,
|
|
63
|
+
PseudoId::Authority,
|
|
64
|
+
PseudoId::Scheme,
|
|
65
|
+
])
|
|
66
|
+
.build(),
|
|
67
|
+
)
|
|
68
|
+
.build();
|
|
69
|
+
|
|
70
|
+
// Default headers
|
|
71
|
+
let headers = {
|
|
72
|
+
let mut headers = HeaderMap::new();
|
|
73
|
+
headers.insert(header::USER_AGENT, HeaderValue::from_static("TwitterAndroid/10.89.0-release.0 (310890000-r-0) G011A/9 (google;G011A;google;G011A;0;;1;2016)"));
|
|
74
|
+
headers.insert(header::ACCEPT_LANGUAGE, HeaderValue::from_static("en-US"));
|
|
75
|
+
headers.insert(
|
|
76
|
+
header::ACCEPT_ENCODING,
|
|
77
|
+
HeaderValue::from_static("br, gzip, deflate"),
|
|
78
|
+
);
|
|
79
|
+
headers.insert(header::ACCEPT, HeaderValue::from_static("application/json"));
|
|
80
|
+
headers.insert(header::CACHE_CONTROL, HeaderValue::from_static("no-store"));
|
|
81
|
+
headers.insert(
|
|
82
|
+
header::COOKIE,
|
|
83
|
+
HeaderValue::from_static("ct0=YOUR_CT0_VALUE;"),
|
|
84
|
+
);
|
|
85
|
+
headers
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// The headers keep the original case and order
|
|
89
|
+
let orig_headers = {
|
|
90
|
+
let mut orig_headers = OrigHeaderMap::new();
|
|
91
|
+
orig_headers.insert("cookie");
|
|
92
|
+
orig_headers.insert("content-length");
|
|
93
|
+
orig_headers.insert("User-Agent");
|
|
94
|
+
orig_headers.insert("Accept-Language");
|
|
95
|
+
orig_headers.insert("Accept-Encoding");
|
|
96
|
+
orig_headers
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// This provider encapsulates TLS, HTTP/1, HTTP/2, default headers, and original headers
|
|
100
|
+
let emulation = Emulation::builder()
|
|
101
|
+
.tls_options(tls)
|
|
102
|
+
.http2_options(http2)
|
|
103
|
+
.headers(headers)
|
|
104
|
+
.orig_headers(orig_headers)
|
|
105
|
+
.build();
|
|
106
|
+
|
|
107
|
+
// Use the API you're already familiar with
|
|
108
|
+
let resp = wreq::get("https://tls.peet.ws/api/all")
|
|
109
|
+
.emulation(emulation)
|
|
110
|
+
.send()
|
|
111
|
+
.await?;
|
|
112
|
+
println!("{}", resp.text().await?);
|
|
113
|
+
|
|
114
|
+
Ok(())
|
|
115
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#[cfg(any(
|
|
2
|
+
target_os = "android",
|
|
3
|
+
target_os = "fuchsia",
|
|
4
|
+
target_os = "illumos",
|
|
5
|
+
target_os = "ios",
|
|
6
|
+
target_os = "linux",
|
|
7
|
+
target_os = "macos",
|
|
8
|
+
target_os = "solaris",
|
|
9
|
+
target_os = "tvos",
|
|
10
|
+
target_os = "visionos",
|
|
11
|
+
target_os = "watchos",
|
|
12
|
+
))]
|
|
13
|
+
#[tokio::main]
|
|
14
|
+
async fn main() -> wreq::Result<()> {
|
|
15
|
+
// Use the API you're already familiar with
|
|
16
|
+
let resp = wreq::get("https://api.ip.sb/ip")
|
|
17
|
+
.interface("utun4")
|
|
18
|
+
.send()
|
|
19
|
+
.await?;
|
|
20
|
+
println!("{}", resp.text().await?);
|
|
21
|
+
|
|
22
|
+
Ok(())
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
#[cfg(not(any(
|
|
26
|
+
target_os = "android",
|
|
27
|
+
target_os = "fuchsia",
|
|
28
|
+
target_os = "illumos",
|
|
29
|
+
target_os = "ios",
|
|
30
|
+
target_os = "linux",
|
|
31
|
+
target_os = "macos",
|
|
32
|
+
target_os = "solaris",
|
|
33
|
+
target_os = "tvos",
|
|
34
|
+
target_os = "visionos",
|
|
35
|
+
target_os = "watchos",
|
|
36
|
+
)))]
|
|
37
|
+
fn main() {}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
use std::net::IpAddr;
|
|
2
|
+
|
|
3
|
+
use wreq::redirect::Policy;
|
|
4
|
+
|
|
5
|
+
#[tokio::main]
|
|
6
|
+
async fn main() -> wreq::Result<()> {
|
|
7
|
+
// Use the API you're already familiar with
|
|
8
|
+
let resp = wreq::get("http://www.baidu.com")
|
|
9
|
+
.redirect(Policy::default())
|
|
10
|
+
.local_address(IpAddr::from([192, 168, 1, 226]))
|
|
11
|
+
.send()
|
|
12
|
+
.await?;
|
|
13
|
+
println!("{}", resp.text().await?);
|
|
14
|
+
|
|
15
|
+
Ok(())
|
|
16
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
use wreq::Proxy;
|
|
2
|
+
|
|
3
|
+
#[tokio::main]
|
|
4
|
+
async fn main() -> wreq::Result<()> {
|
|
5
|
+
// Use the API you're already familiar with
|
|
6
|
+
let resp = wreq::get("https://api.ip.sb/ip")
|
|
7
|
+
.proxy(Proxy::all("socks5h://localhost:6153")?)
|
|
8
|
+
.send()
|
|
9
|
+
.await?;
|
|
10
|
+
println!("{}", resp.text().await?);
|
|
11
|
+
|
|
12
|
+
Ok(())
|
|
13
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
use wreq::redirect::Policy;
|
|
2
|
+
|
|
3
|
+
#[tokio::main]
|
|
4
|
+
async fn main() -> wreq::Result<()> {
|
|
5
|
+
// Use the API you're already familiar with
|
|
6
|
+
let resp = wreq::get("https://google.com/")
|
|
7
|
+
.redirect(Policy::custom(|attempt| {
|
|
8
|
+
// we can inspect the redirect attempt
|
|
9
|
+
println!(
|
|
10
|
+
"Redirecting (status: {}) to {:?} and headers: {:#?}",
|
|
11
|
+
attempt.status, attempt.uri, attempt.headers
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
// we can follow redirects as normal
|
|
15
|
+
attempt.follow()
|
|
16
|
+
}))
|
|
17
|
+
.send()
|
|
18
|
+
.await?;
|
|
19
|
+
println!("{}", resp.text().await?);
|
|
20
|
+
|
|
21
|
+
Ok(())
|
|
22
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
use http::Version;
|
|
2
|
+
|
|
3
|
+
#[tokio::main]
|
|
4
|
+
async fn main() -> wreq::Result<()> {
|
|
5
|
+
// Use the API you're already familiar with
|
|
6
|
+
let resp = wreq::get("https://www.google.com")
|
|
7
|
+
.version(Version::HTTP_11)
|
|
8
|
+
.send()
|
|
9
|
+
.await?;
|
|
10
|
+
|
|
11
|
+
assert_eq!(resp.version(), Version::HTTP_11);
|
|
12
|
+
println!("{}", resp.text().await?);
|
|
13
|
+
|
|
14
|
+
Ok(())
|
|
15
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#![deny(warnings)]
|
|
2
|
+
|
|
3
|
+
// This is using the `tokio` runtime. You'll need the following dependency:
|
|
4
|
+
//
|
|
5
|
+
// `tokio = { version = "1", features = ["full"] }`
|
|
6
|
+
#[tokio::main]
|
|
7
|
+
async fn main() -> wreq::Result<()> {
|
|
8
|
+
// Make sure you are running tor and this is your socks port
|
|
9
|
+
let proxy = wreq::Proxy::all("socks5h://127.0.0.1:9050").expect("tor proxy should be there");
|
|
10
|
+
let client = wreq::Client::builder()
|
|
11
|
+
.proxy(proxy)
|
|
12
|
+
.build()
|
|
13
|
+
.expect("should be able to build wreq client");
|
|
14
|
+
|
|
15
|
+
let res = client.get("https://check.torproject.org").send().await?;
|
|
16
|
+
println!("Status: {}", res.status());
|
|
17
|
+
|
|
18
|
+
let text = res.text().await?;
|
|
19
|
+
let is_tor = text.contains("Congratulations. This emulation is configured to use Tor.");
|
|
20
|
+
println!("Is Tor: {is_tor}");
|
|
21
|
+
assert!(is_tor);
|
|
22
|
+
|
|
23
|
+
Ok(())
|
|
24
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#[cfg(unix)]
|
|
2
|
+
#[tokio::main]
|
|
3
|
+
async fn main() -> wreq::Result<()> {
|
|
4
|
+
// Create a Unix socket proxy
|
|
5
|
+
let proxy = wreq::Proxy::unix("/var/run/docker.sock")?;
|
|
6
|
+
|
|
7
|
+
// Build a client
|
|
8
|
+
let client = wreq::Client::builder()
|
|
9
|
+
// Specify the Unix socket path
|
|
10
|
+
.proxy(proxy.clone())
|
|
11
|
+
.timeout(std::time::Duration::from_secs(10))
|
|
12
|
+
.build()?;
|
|
13
|
+
|
|
14
|
+
// Use the API you're already familiar with
|
|
15
|
+
let resp = client
|
|
16
|
+
.get("http://localhost/v1.41/containers/json")
|
|
17
|
+
.send()
|
|
18
|
+
.await?;
|
|
19
|
+
println!("{}", resp.text().await?);
|
|
20
|
+
|
|
21
|
+
// Or specify the Unix socket directly in the request
|
|
22
|
+
let resp = client
|
|
23
|
+
.get("http://localhost/v1.41/containers/json")
|
|
24
|
+
.proxy(proxy)
|
|
25
|
+
.send()
|
|
26
|
+
.await?;
|
|
27
|
+
println!("{}", resp.text().await?);
|
|
28
|
+
|
|
29
|
+
Ok(())
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
#[cfg(not(unix))]
|
|
33
|
+
fn main() {}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
use std::{
|
|
2
|
+
pin::Pin,
|
|
3
|
+
task::{Context, Poll, ready},
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
use bytes::Bytes;
|
|
7
|
+
use http_body::{Body as HttpBody, SizeHint};
|
|
8
|
+
use http_body_util::{BodyExt, Either, combinators::BoxBody};
|
|
9
|
+
use pin_project_lite::pin_project;
|
|
10
|
+
#[cfg(feature = "stream")]
|
|
11
|
+
use {tokio::fs::File, tokio_util::io::ReaderStream};
|
|
12
|
+
|
|
13
|
+
use crate::error::{BoxError, Error};
|
|
14
|
+
|
|
15
|
+
/// An request body.
|
|
16
|
+
#[derive(Debug)]
|
|
17
|
+
pub struct Body(Either<Bytes, BoxBody<Bytes, BoxError>>);
|
|
18
|
+
|
|
19
|
+
pin_project! {
|
|
20
|
+
/// We can't use `map_frame()` because that loses the hint data (for good reason).
|
|
21
|
+
/// But we aren't transforming the data.
|
|
22
|
+
struct IntoBytesBody<B> {
|
|
23
|
+
#[pin]
|
|
24
|
+
inner: B,
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ===== impl Body =====
|
|
29
|
+
|
|
30
|
+
impl Body {
|
|
31
|
+
/// Returns a reference to the internal data of the `Body`.
|
|
32
|
+
///
|
|
33
|
+
/// `None` is returned, if the underlying data is a stream.
|
|
34
|
+
pub fn as_bytes(&self) -> Option<&[u8]> {
|
|
35
|
+
match &self.0 {
|
|
36
|
+
Either::Left(bytes) => Some(bytes.as_ref()),
|
|
37
|
+
Either::Right(..) => None,
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// Wrap a [`HttpBody`] in a box inside `Body`.
|
|
42
|
+
///
|
|
43
|
+
/// # Example
|
|
44
|
+
///
|
|
45
|
+
/// ```
|
|
46
|
+
/// # use wreq::Body;
|
|
47
|
+
/// # use futures_util;
|
|
48
|
+
/// # fn main() {
|
|
49
|
+
/// let content = "hello,world!".to_string();
|
|
50
|
+
///
|
|
51
|
+
/// let body = Body::wrap(content);
|
|
52
|
+
/// # }
|
|
53
|
+
/// ```
|
|
54
|
+
pub fn wrap<B>(inner: B) -> Body
|
|
55
|
+
where
|
|
56
|
+
B: HttpBody + Send + Sync + 'static,
|
|
57
|
+
B::Data: Into<Bytes>,
|
|
58
|
+
B::Error: Into<BoxError>,
|
|
59
|
+
{
|
|
60
|
+
Body(Either::Right(
|
|
61
|
+
IntoBytesBody { inner }.map_err(Into::into).boxed(),
|
|
62
|
+
))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// Wrap a futures `Stream` in a box inside `Body`.
|
|
66
|
+
///
|
|
67
|
+
/// # Example
|
|
68
|
+
///
|
|
69
|
+
/// ```
|
|
70
|
+
/// # use wreq::Body;
|
|
71
|
+
/// # use futures_util;
|
|
72
|
+
/// # fn main() {
|
|
73
|
+
/// let chunks: Vec<Result<_, ::std::io::Error>> = vec![Ok("hello"), Ok(" "), Ok("world")];
|
|
74
|
+
///
|
|
75
|
+
/// let stream = futures_util::stream::iter(chunks);
|
|
76
|
+
///
|
|
77
|
+
/// let body = Body::wrap_stream(stream);
|
|
78
|
+
/// # }
|
|
79
|
+
/// ```
|
|
80
|
+
///
|
|
81
|
+
/// # Optional
|
|
82
|
+
///
|
|
83
|
+
/// This requires the `stream` feature to be enabled.
|
|
84
|
+
#[cfg(feature = "stream")]
|
|
85
|
+
#[cfg_attr(docsrs, doc(cfg(feature = "stream")))]
|
|
86
|
+
pub fn wrap_stream<S>(stream: S) -> Body
|
|
87
|
+
where
|
|
88
|
+
S: futures_util::stream::TryStream + Send + 'static,
|
|
89
|
+
S::Error: Into<BoxError>,
|
|
90
|
+
Bytes: From<S::Ok>,
|
|
91
|
+
{
|
|
92
|
+
Body::stream(stream)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
#[cfg(any(feature = "stream", feature = "multipart"))]
|
|
96
|
+
pub(crate) fn stream<S>(stream: S) -> Body
|
|
97
|
+
where
|
|
98
|
+
S: futures_util::stream::TryStream + Send + 'static,
|
|
99
|
+
S::Error: Into<BoxError>,
|
|
100
|
+
Bytes: From<S::Ok>,
|
|
101
|
+
{
|
|
102
|
+
use futures_util::TryStreamExt;
|
|
103
|
+
use http_body::Frame;
|
|
104
|
+
use http_body_util::StreamBody;
|
|
105
|
+
use sync_wrapper::SyncStream;
|
|
106
|
+
|
|
107
|
+
let body = StreamBody::new(SyncStream::new(
|
|
108
|
+
stream
|
|
109
|
+
.map_ok(Bytes::from)
|
|
110
|
+
.map_ok(Frame::data)
|
|
111
|
+
.map_err(Into::into),
|
|
112
|
+
));
|
|
113
|
+
Body(Either::Right(body.boxed()))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
#[inline]
|
|
117
|
+
pub(crate) fn empty() -> Body {
|
|
118
|
+
Body::reusable(Bytes::new())
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
#[inline]
|
|
122
|
+
pub(crate) fn reusable(chunk: Bytes) -> Body {
|
|
123
|
+
Body(Either::Left(chunk))
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
#[cfg(feature = "multipart")]
|
|
127
|
+
pub(crate) fn content_length(&self) -> Option<u64> {
|
|
128
|
+
match self.0 {
|
|
129
|
+
Either::Left(ref bytes) => Some(bytes.len() as u64),
|
|
130
|
+
Either::Right(ref body) => body.size_hint().exact(),
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
pub(crate) fn try_clone(&self) -> Option<Body> {
|
|
135
|
+
match self.0 {
|
|
136
|
+
Either::Left(ref chunk) => Some(Body::reusable(chunk.clone())),
|
|
137
|
+
Either::Right { .. } => None,
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
impl Default for Body {
|
|
143
|
+
#[inline]
|
|
144
|
+
fn default() -> Body {
|
|
145
|
+
Body::empty()
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
impl From<BoxBody<Bytes, BoxError>> for Body {
|
|
150
|
+
#[inline]
|
|
151
|
+
fn from(body: BoxBody<Bytes, BoxError>) -> Self {
|
|
152
|
+
Self(Either::Right(body))
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
impl From<Bytes> for Body {
|
|
157
|
+
#[inline]
|
|
158
|
+
fn from(bytes: Bytes) -> Body {
|
|
159
|
+
Body::reusable(bytes)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
impl From<Vec<u8>> for Body {
|
|
164
|
+
#[inline]
|
|
165
|
+
fn from(vec: Vec<u8>) -> Body {
|
|
166
|
+
Body::reusable(vec.into())
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
impl From<&'static [u8]> for Body {
|
|
171
|
+
#[inline]
|
|
172
|
+
fn from(s: &'static [u8]) -> Body {
|
|
173
|
+
Body::reusable(Bytes::from_static(s))
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
impl From<String> for Body {
|
|
178
|
+
#[inline]
|
|
179
|
+
fn from(s: String) -> Body {
|
|
180
|
+
Body::reusable(s.into())
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
impl From<&'static str> for Body {
|
|
185
|
+
#[inline]
|
|
186
|
+
fn from(s: &'static str) -> Body {
|
|
187
|
+
s.as_bytes().into()
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
#[cfg(feature = "stream")]
|
|
192
|
+
impl From<File> for Body {
|
|
193
|
+
#[inline]
|
|
194
|
+
fn from(file: File) -> Body {
|
|
195
|
+
Body::wrap_stream(ReaderStream::new(file))
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
impl HttpBody for Body {
|
|
200
|
+
type Data = Bytes;
|
|
201
|
+
type Error = Error;
|
|
202
|
+
|
|
203
|
+
fn poll_frame(
|
|
204
|
+
mut self: Pin<&mut Self>,
|
|
205
|
+
cx: &mut Context,
|
|
206
|
+
) -> Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
|
|
207
|
+
match self.0 {
|
|
208
|
+
Either::Left(ref mut bytes) => {
|
|
209
|
+
let out = bytes.split_off(0);
|
|
210
|
+
if out.is_empty() {
|
|
211
|
+
Poll::Ready(None)
|
|
212
|
+
} else {
|
|
213
|
+
Poll::Ready(Some(Ok(http_body::Frame::data(out))))
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
Either::Right(ref mut body) => {
|
|
217
|
+
Poll::Ready(ready!(Pin::new(body).poll_frame(cx)).map(|opt_chunk| {
|
|
218
|
+
opt_chunk.map_err(|err| match err.downcast::<Error>() {
|
|
219
|
+
Ok(err) => *err,
|
|
220
|
+
Err(err) => Error::body(err),
|
|
221
|
+
})
|
|
222
|
+
}))
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
#[inline]
|
|
228
|
+
fn size_hint(&self) -> SizeHint {
|
|
229
|
+
match self.0 {
|
|
230
|
+
Either::Left(ref bytes) => SizeHint::with_exact(bytes.len() as u64),
|
|
231
|
+
Either::Right(ref body) => body.size_hint(),
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
#[inline]
|
|
236
|
+
fn is_end_stream(&self) -> bool {
|
|
237
|
+
match self.0 {
|
|
238
|
+
Either::Left(ref bytes) => bytes.is_empty(),
|
|
239
|
+
Either::Right(ref body) => body.is_end_stream(),
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ===== impl IntoBytesBody =====
|
|
245
|
+
|
|
246
|
+
impl<B> HttpBody for IntoBytesBody<B>
|
|
247
|
+
where
|
|
248
|
+
B: HttpBody,
|
|
249
|
+
B::Data: Into<Bytes>,
|
|
250
|
+
{
|
|
251
|
+
type Data = Bytes;
|
|
252
|
+
type Error = B::Error;
|
|
253
|
+
|
|
254
|
+
fn poll_frame(
|
|
255
|
+
self: Pin<&mut Self>,
|
|
256
|
+
cx: &mut Context,
|
|
257
|
+
) -> Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
|
|
258
|
+
match ready!(self.project().inner.poll_frame(cx)) {
|
|
259
|
+
Some(Ok(f)) => Poll::Ready(Some(Ok(f.map_data(Into::into)))),
|
|
260
|
+
Some(Err(e)) => Poll::Ready(Some(Err(e))),
|
|
261
|
+
None => Poll::Ready(None),
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
#[inline]
|
|
266
|
+
fn size_hint(&self) -> SizeHint {
|
|
267
|
+
self.inner.size_hint()
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
#[inline]
|
|
271
|
+
fn is_end_stream(&self) -> bool {
|
|
272
|
+
self.inner.is_end_stream()
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
#[cfg(test)]
|
|
277
|
+
mod tests {
|
|
278
|
+
use http_body::Body as _;
|
|
279
|
+
|
|
280
|
+
use super::Body;
|
|
281
|
+
|
|
282
|
+
#[test]
|
|
283
|
+
fn test_as_bytes() {
|
|
284
|
+
let test_data = b"Test body";
|
|
285
|
+
let body = Body::from(&test_data[..]);
|
|
286
|
+
assert_eq!(body.as_bytes(), Some(&test_data[..]));
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
#[test]
|
|
290
|
+
fn body_exact_length() {
|
|
291
|
+
let empty_body = Body::empty();
|
|
292
|
+
assert!(empty_body.is_end_stream());
|
|
293
|
+
assert_eq!(empty_body.size_hint().exact(), Some(0));
|
|
294
|
+
|
|
295
|
+
let bytes_body = Body::reusable("abc".into());
|
|
296
|
+
assert!(!bytes_body.is_end_stream());
|
|
297
|
+
assert_eq!(bytes_body.size_hint().exact(), Some(3));
|
|
298
|
+
|
|
299
|
+
// can delegate even when wrapped
|
|
300
|
+
let stream_body = Body::wrap(empty_body);
|
|
301
|
+
assert!(stream_body.is_end_stream());
|
|
302
|
+
assert_eq!(stream_body.size_hint().exact(), Some(0));
|
|
303
|
+
}
|
|
304
|
+
}
|