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.
Files changed (167) hide show
  1. checksums.yaml +7 -0
  2. data/Cargo.lock +2688 -0
  3. data/Cargo.toml +6 -0
  4. data/README.md +179 -0
  5. data/ext/wreq_rb/Cargo.toml +39 -0
  6. data/ext/wreq_rb/extconf.rb +22 -0
  7. data/ext/wreq_rb/src/client.rs +565 -0
  8. data/ext/wreq_rb/src/error.rs +25 -0
  9. data/ext/wreq_rb/src/lib.rs +20 -0
  10. data/ext/wreq_rb/src/response.rs +132 -0
  11. data/lib/wreq-rb/version.rb +5 -0
  12. data/lib/wreq-rb.rb +17 -0
  13. data/patches/0001-add-transfer-size-tracking.patch +292 -0
  14. data/vendor/wreq/Cargo.toml +306 -0
  15. data/vendor/wreq/LICENSE +202 -0
  16. data/vendor/wreq/README.md +122 -0
  17. data/vendor/wreq/examples/cert_store.rs +77 -0
  18. data/vendor/wreq/examples/connect_via_lower_priority_tokio_runtime.rs +258 -0
  19. data/vendor/wreq/examples/emulation.rs +118 -0
  20. data/vendor/wreq/examples/form.rs +14 -0
  21. data/vendor/wreq/examples/http1_websocket.rs +37 -0
  22. data/vendor/wreq/examples/http2_websocket.rs +45 -0
  23. data/vendor/wreq/examples/json_dynamic.rs +41 -0
  24. data/vendor/wreq/examples/json_typed.rs +47 -0
  25. data/vendor/wreq/examples/keylog.rs +16 -0
  26. data/vendor/wreq/examples/request_with_emulation.rs +115 -0
  27. data/vendor/wreq/examples/request_with_interface.rs +37 -0
  28. data/vendor/wreq/examples/request_with_local_address.rs +16 -0
  29. data/vendor/wreq/examples/request_with_proxy.rs +13 -0
  30. data/vendor/wreq/examples/request_with_redirect.rs +22 -0
  31. data/vendor/wreq/examples/request_with_version.rs +15 -0
  32. data/vendor/wreq/examples/tor_socks.rs +24 -0
  33. data/vendor/wreq/examples/unix_socket.rs +33 -0
  34. data/vendor/wreq/src/client/body.rs +304 -0
  35. data/vendor/wreq/src/client/conn/conn.rs +231 -0
  36. data/vendor/wreq/src/client/conn/connector.rs +549 -0
  37. data/vendor/wreq/src/client/conn/http.rs +1023 -0
  38. data/vendor/wreq/src/client/conn/proxy/socks.rs +233 -0
  39. data/vendor/wreq/src/client/conn/proxy/tunnel.rs +260 -0
  40. data/vendor/wreq/src/client/conn/proxy.rs +39 -0
  41. data/vendor/wreq/src/client/conn/tls_info.rs +98 -0
  42. data/vendor/wreq/src/client/conn/uds.rs +44 -0
  43. data/vendor/wreq/src/client/conn/verbose.rs +149 -0
  44. data/vendor/wreq/src/client/conn.rs +323 -0
  45. data/vendor/wreq/src/client/core/body/incoming.rs +485 -0
  46. data/vendor/wreq/src/client/core/body/length.rs +118 -0
  47. data/vendor/wreq/src/client/core/body.rs +34 -0
  48. data/vendor/wreq/src/client/core/common/buf.rs +149 -0
  49. data/vendor/wreq/src/client/core/common/rewind.rs +141 -0
  50. data/vendor/wreq/src/client/core/common/watch.rs +76 -0
  51. data/vendor/wreq/src/client/core/common.rs +3 -0
  52. data/vendor/wreq/src/client/core/conn/http1.rs +342 -0
  53. data/vendor/wreq/src/client/core/conn/http2.rs +307 -0
  54. data/vendor/wreq/src/client/core/conn.rs +11 -0
  55. data/vendor/wreq/src/client/core/dispatch.rs +299 -0
  56. data/vendor/wreq/src/client/core/error.rs +435 -0
  57. data/vendor/wreq/src/client/core/ext.rs +201 -0
  58. data/vendor/wreq/src/client/core/http1.rs +178 -0
  59. data/vendor/wreq/src/client/core/http2.rs +483 -0
  60. data/vendor/wreq/src/client/core/proto/h1/conn.rs +988 -0
  61. data/vendor/wreq/src/client/core/proto/h1/decode.rs +1170 -0
  62. data/vendor/wreq/src/client/core/proto/h1/dispatch.rs +684 -0
  63. data/vendor/wreq/src/client/core/proto/h1/encode.rs +580 -0
  64. data/vendor/wreq/src/client/core/proto/h1/io.rs +879 -0
  65. data/vendor/wreq/src/client/core/proto/h1/role.rs +694 -0
  66. data/vendor/wreq/src/client/core/proto/h1.rs +104 -0
  67. data/vendor/wreq/src/client/core/proto/h2/client.rs +650 -0
  68. data/vendor/wreq/src/client/core/proto/h2/ping.rs +539 -0
  69. data/vendor/wreq/src/client/core/proto/h2.rs +379 -0
  70. data/vendor/wreq/src/client/core/proto/headers.rs +138 -0
  71. data/vendor/wreq/src/client/core/proto.rs +58 -0
  72. data/vendor/wreq/src/client/core/rt/bounds.rs +57 -0
  73. data/vendor/wreq/src/client/core/rt/timer.rs +150 -0
  74. data/vendor/wreq/src/client/core/rt/tokio.rs +99 -0
  75. data/vendor/wreq/src/client/core/rt.rs +25 -0
  76. data/vendor/wreq/src/client/core/upgrade.rs +267 -0
  77. data/vendor/wreq/src/client/core.rs +16 -0
  78. data/vendor/wreq/src/client/emulation.rs +161 -0
  79. data/vendor/wreq/src/client/http/client/error.rs +142 -0
  80. data/vendor/wreq/src/client/http/client/exec.rs +29 -0
  81. data/vendor/wreq/src/client/http/client/extra.rs +77 -0
  82. data/vendor/wreq/src/client/http/client/lazy.rs +79 -0
  83. data/vendor/wreq/src/client/http/client/pool.rs +1105 -0
  84. data/vendor/wreq/src/client/http/client/util.rs +104 -0
  85. data/vendor/wreq/src/client/http/client.rs +1003 -0
  86. data/vendor/wreq/src/client/http/future.rs +99 -0
  87. data/vendor/wreq/src/client/http.rs +1629 -0
  88. data/vendor/wreq/src/client/layer/config/options.rs +156 -0
  89. data/vendor/wreq/src/client/layer/config.rs +116 -0
  90. data/vendor/wreq/src/client/layer/cookie.rs +161 -0
  91. data/vendor/wreq/src/client/layer/decoder.rs +139 -0
  92. data/vendor/wreq/src/client/layer/redirect/future.rs +270 -0
  93. data/vendor/wreq/src/client/layer/redirect/policy.rs +63 -0
  94. data/vendor/wreq/src/client/layer/redirect.rs +145 -0
  95. data/vendor/wreq/src/client/layer/retry/classify.rs +105 -0
  96. data/vendor/wreq/src/client/layer/retry/scope.rs +51 -0
  97. data/vendor/wreq/src/client/layer/retry.rs +151 -0
  98. data/vendor/wreq/src/client/layer/timeout/body.rs +233 -0
  99. data/vendor/wreq/src/client/layer/timeout/future.rs +90 -0
  100. data/vendor/wreq/src/client/layer/timeout.rs +177 -0
  101. data/vendor/wreq/src/client/layer.rs +15 -0
  102. data/vendor/wreq/src/client/multipart.rs +717 -0
  103. data/vendor/wreq/src/client/request.rs +818 -0
  104. data/vendor/wreq/src/client/response.rs +534 -0
  105. data/vendor/wreq/src/client/ws/json.rs +99 -0
  106. data/vendor/wreq/src/client/ws/message.rs +453 -0
  107. data/vendor/wreq/src/client/ws.rs +714 -0
  108. data/vendor/wreq/src/client.rs +27 -0
  109. data/vendor/wreq/src/config.rs +140 -0
  110. data/vendor/wreq/src/cookie.rs +579 -0
  111. data/vendor/wreq/src/dns/gai.rs +249 -0
  112. data/vendor/wreq/src/dns/hickory.rs +78 -0
  113. data/vendor/wreq/src/dns/resolve.rs +180 -0
  114. data/vendor/wreq/src/dns.rs +69 -0
  115. data/vendor/wreq/src/error.rs +502 -0
  116. data/vendor/wreq/src/ext.rs +398 -0
  117. data/vendor/wreq/src/hash.rs +143 -0
  118. data/vendor/wreq/src/header.rs +506 -0
  119. data/vendor/wreq/src/into_uri.rs +187 -0
  120. data/vendor/wreq/src/lib.rs +586 -0
  121. data/vendor/wreq/src/proxy/mac.rs +82 -0
  122. data/vendor/wreq/src/proxy/matcher.rs +806 -0
  123. data/vendor/wreq/src/proxy/uds.rs +66 -0
  124. data/vendor/wreq/src/proxy/win.rs +31 -0
  125. data/vendor/wreq/src/proxy.rs +569 -0
  126. data/vendor/wreq/src/redirect.rs +575 -0
  127. data/vendor/wreq/src/retry.rs +198 -0
  128. data/vendor/wreq/src/sync.rs +129 -0
  129. data/vendor/wreq/src/tls/conn/cache.rs +123 -0
  130. data/vendor/wreq/src/tls/conn/cert_compression.rs +125 -0
  131. data/vendor/wreq/src/tls/conn/ext.rs +82 -0
  132. data/vendor/wreq/src/tls/conn/macros.rs +34 -0
  133. data/vendor/wreq/src/tls/conn/service.rs +138 -0
  134. data/vendor/wreq/src/tls/conn.rs +681 -0
  135. data/vendor/wreq/src/tls/keylog/handle.rs +64 -0
  136. data/vendor/wreq/src/tls/keylog.rs +99 -0
  137. data/vendor/wreq/src/tls/options.rs +464 -0
  138. data/vendor/wreq/src/tls/x509/identity.rs +122 -0
  139. data/vendor/wreq/src/tls/x509/parser.rs +71 -0
  140. data/vendor/wreq/src/tls/x509/store.rs +228 -0
  141. data/vendor/wreq/src/tls/x509.rs +68 -0
  142. data/vendor/wreq/src/tls.rs +154 -0
  143. data/vendor/wreq/src/trace.rs +55 -0
  144. data/vendor/wreq/src/util.rs +122 -0
  145. data/vendor/wreq/tests/badssl.rs +228 -0
  146. data/vendor/wreq/tests/brotli.rs +350 -0
  147. data/vendor/wreq/tests/client.rs +1098 -0
  148. data/vendor/wreq/tests/connector_layers.rs +227 -0
  149. data/vendor/wreq/tests/cookie.rs +306 -0
  150. data/vendor/wreq/tests/deflate.rs +347 -0
  151. data/vendor/wreq/tests/emulation.rs +260 -0
  152. data/vendor/wreq/tests/gzip.rs +347 -0
  153. data/vendor/wreq/tests/layers.rs +261 -0
  154. data/vendor/wreq/tests/multipart.rs +165 -0
  155. data/vendor/wreq/tests/proxy.rs +438 -0
  156. data/vendor/wreq/tests/redirect.rs +629 -0
  157. data/vendor/wreq/tests/retry.rs +135 -0
  158. data/vendor/wreq/tests/support/delay_server.rs +117 -0
  159. data/vendor/wreq/tests/support/error.rs +16 -0
  160. data/vendor/wreq/tests/support/layer.rs +183 -0
  161. data/vendor/wreq/tests/support/mod.rs +9 -0
  162. data/vendor/wreq/tests/support/server.rs +232 -0
  163. data/vendor/wreq/tests/timeouts.rs +281 -0
  164. data/vendor/wreq/tests/unix_socket.rs +135 -0
  165. data/vendor/wreq/tests/upgrade.rs +98 -0
  166. data/vendor/wreq/tests/zstd.rs +559 -0
  167. 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
+ }