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,1098 @@
1
+ mod support;
2
+
3
+ #[cfg(feature = "json")]
4
+ use std::collections::HashMap;
5
+
6
+ use bytes::Bytes;
7
+ use http::{
8
+ HeaderMap, HeaderValue, StatusCode, Version,
9
+ header::{
10
+ self, ACCEPT, AUTHORIZATION, CACHE_CONTROL, CONTENT_LENGTH, CONTENT_TYPE, COOKIE, REFERER,
11
+ TRANSFER_ENCODING, USER_AGENT,
12
+ },
13
+ };
14
+ use http_body_util::{BodyExt, Full};
15
+ use pretty_env_logger::env_logger;
16
+ use support::server;
17
+ use tokio::io::AsyncWriteExt;
18
+ use wreq::{Client, header::OrigHeaderMap, tls::TlsInfo};
19
+
20
+ #[tokio::test]
21
+ async fn auto_headers() {
22
+ let server = server::http(move |req| async move {
23
+ assert_eq!(req.method(), "GET");
24
+
25
+ assert_eq!(req.headers()["accept"], "*/*");
26
+ assert_eq!(req.headers().get("user-agent"), None);
27
+ if cfg!(feature = "gzip") {
28
+ assert!(
29
+ req.headers()["accept-encoding"]
30
+ .to_str()
31
+ .unwrap()
32
+ .contains("gzip")
33
+ );
34
+ }
35
+ if cfg!(feature = "brotli") {
36
+ assert!(
37
+ req.headers()["accept-encoding"]
38
+ .to_str()
39
+ .unwrap()
40
+ .contains("br")
41
+ );
42
+ }
43
+ if cfg!(feature = "zstd") {
44
+ assert!(
45
+ req.headers()["accept-encoding"]
46
+ .to_str()
47
+ .unwrap()
48
+ .contains("zstd")
49
+ );
50
+ }
51
+ if cfg!(feature = "deflate") {
52
+ assert!(
53
+ req.headers()["accept-encoding"]
54
+ .to_str()
55
+ .unwrap()
56
+ .contains("deflate")
57
+ );
58
+ }
59
+
60
+ http::Response::default()
61
+ });
62
+
63
+ let url = format!("http://{}/1", server.addr());
64
+ let res = Client::builder()
65
+ .no_proxy()
66
+ .build()
67
+ .unwrap()
68
+ .get(&url)
69
+ .header(wreq::header::ACCEPT, "*/*")
70
+ .send()
71
+ .await
72
+ .unwrap();
73
+
74
+ assert_eq!(res.uri(), url.as_str());
75
+ assert_eq!(res.status(), wreq::StatusCode::OK);
76
+ assert_eq!(res.remote_addr(), Some(server.addr()));
77
+ }
78
+
79
+ #[tokio::test]
80
+ async fn test_headers_order_with_client() {
81
+ use http::HeaderValue;
82
+ use wreq::{
83
+ Client,
84
+ header::{ACCEPT, CONTENT_TYPE, USER_AGENT},
85
+ };
86
+
87
+ let server = server::http(move |req| async move {
88
+ assert_eq!(req.method(), "POST");
89
+
90
+ let expected_headers = [
91
+ ("cookie", "cookie1=cookie1-value"),
92
+ ("cookie", "cookie2=cookie2-value"),
93
+ ("user-agent", "my-test-client"),
94
+ ("accept", "*/*"),
95
+ ("content-type", "application/json"),
96
+ ("authorization", "Bearer test-token"),
97
+ ("referer", "https://example.com"),
98
+ ("cache-control", "no-cache"),
99
+ ];
100
+
101
+ for (i, (expected_key, expected_value)) in expected_headers.iter().enumerate() {
102
+ let (key, value) = req.headers().iter().nth(i).unwrap();
103
+ assert_eq!(key.as_str(), *expected_key);
104
+ assert_eq!(value.as_bytes(), expected_value.as_bytes());
105
+ }
106
+
107
+ let full: Vec<u8> = req
108
+ .into_body()
109
+ .collect()
110
+ .await
111
+ .expect("must succeed")
112
+ .to_bytes()
113
+ .to_vec();
114
+
115
+ assert_eq!(full, br#"{"message":"hello"}"#);
116
+
117
+ http::Response::default()
118
+ });
119
+
120
+ let url = format!("http://{}/test", server.addr());
121
+
122
+ let client = Client::builder()
123
+ .no_proxy()
124
+ .default_headers({
125
+ let mut headers = HeaderMap::new();
126
+ headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
127
+ headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
128
+ headers.insert(USER_AGENT, HeaderValue::from_static("my-test-client"));
129
+ headers.insert(AUTHORIZATION, HeaderValue::from_static("Bearer test-token"));
130
+ headers.insert(REFERER, HeaderValue::from_static("https://example.com"));
131
+ headers.append("cookie", HeaderValue::from_static("cookie1=cookie1-value"));
132
+ headers.append("cookie", HeaderValue::from_static("cookie2=cookie2-value"));
133
+ headers.insert(CACHE_CONTROL, HeaderValue::from_static("no-cache"));
134
+ headers
135
+ })
136
+ .orig_headers({
137
+ let mut orig_headers = OrigHeaderMap::new();
138
+ orig_headers.insert("cookie");
139
+ orig_headers.insert("user-agent");
140
+ orig_headers.insert("accept");
141
+ orig_headers.insert("content-type");
142
+ orig_headers.insert("authorization");
143
+ orig_headers.insert("referer");
144
+ orig_headers.insert("cache-control");
145
+ orig_headers
146
+ })
147
+ .build()
148
+ .unwrap();
149
+
150
+ let res = client
151
+ .post(&url)
152
+ .body(r#"{"message":"hello"}"#)
153
+ .send()
154
+ .await
155
+ .unwrap();
156
+
157
+ assert_eq!(res.status(), wreq::StatusCode::OK);
158
+ }
159
+
160
+ #[tokio::test]
161
+ async fn test_headers_order_with_request() {
162
+ use http::HeaderValue;
163
+ use wreq::{
164
+ Client,
165
+ header::{ACCEPT, CONTENT_TYPE, USER_AGENT},
166
+ };
167
+
168
+ let server = server::http(move |req| async move {
169
+ assert_eq!(req.method(), "POST");
170
+
171
+ let expected_headers = [
172
+ ("user-agent", "my-test-client"),
173
+ ("accept", "*/*"),
174
+ ("content-type", "application/json"),
175
+ ("authorization", "Bearer test-token"),
176
+ ("referer", "https://example.com"),
177
+ ("cookie", "cookie1=cookie1"),
178
+ ("cookie", "cookie2=cookie2"),
179
+ ("cache-control", "no-cache"),
180
+ ];
181
+
182
+ for (i, (expected_key, expected_value)) in expected_headers.iter().enumerate() {
183
+ let (key, value) = req.headers().iter().nth(i).unwrap();
184
+ assert_eq!(key.as_str(), *expected_key);
185
+ assert_eq!(value.as_bytes(), expected_value.as_bytes());
186
+ }
187
+
188
+ let full: Vec<u8> = req
189
+ .into_body()
190
+ .collect()
191
+ .await
192
+ .expect("must succeed")
193
+ .to_bytes()
194
+ .to_vec();
195
+
196
+ assert_eq!(full, br#"{"message":"hello"}"#);
197
+
198
+ http::Response::default()
199
+ });
200
+
201
+ let url = format!("http://{}/test", server.addr());
202
+
203
+ let client = Client::builder().no_proxy().build().unwrap();
204
+
205
+ let res = client
206
+ .post(&url)
207
+ .headers({
208
+ let mut headers = HeaderMap::new();
209
+ headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
210
+ headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
211
+ headers.insert(USER_AGENT, HeaderValue::from_static("my-test-client"));
212
+ headers.insert(AUTHORIZATION, HeaderValue::from_static("Bearer test-token"));
213
+ headers.insert(REFERER, HeaderValue::from_static("https://example.com"));
214
+ headers.append("cookie", HeaderValue::from_static("cookie1=cookie1"));
215
+ headers.append("cookie", HeaderValue::from_static("cookie2=cookie2"));
216
+ headers.insert(CACHE_CONTROL, HeaderValue::from_static("no-cache"));
217
+ headers
218
+ })
219
+ .orig_headers({
220
+ let mut orig_headers = OrigHeaderMap::new();
221
+ orig_headers.insert("user-agent");
222
+ orig_headers.insert("accept");
223
+ orig_headers.insert("content-type");
224
+ orig_headers.insert("authorization");
225
+ orig_headers.insert("referer");
226
+ orig_headers.insert("cookie");
227
+ orig_headers.insert("cache-control");
228
+ orig_headers
229
+ })
230
+ .body(r#"{"message":"hello"}"#)
231
+ .send()
232
+ .await
233
+ .unwrap();
234
+
235
+ assert_eq!(res.status(), wreq::StatusCode::OK);
236
+ }
237
+
238
+ #[tokio::test]
239
+ async fn test_overwrite_headers() {
240
+ let server = server::http(move |req| async move {
241
+ let path = req.uri().path();
242
+ match path {
243
+ "/1" => {
244
+ assert_eq!(req.method(), "GET");
245
+ assert_eq!(req.headers()[USER_AGENT], "my-custom-agent");
246
+ let mut cookies = req.headers().get_all(COOKIE).iter();
247
+ assert_eq!(cookies.next().unwrap(), "a=b");
248
+ assert_eq!(cookies.next().unwrap(), "c=d");
249
+ assert_eq!(cookies.next(), None);
250
+ }
251
+ "/2" => {
252
+ assert_eq!(req.method(), "GET");
253
+ assert_eq!(req.headers()[USER_AGENT], "my-custom-agent");
254
+ let mut cookies = req.headers().get_all(COOKIE).iter();
255
+ assert_eq!(cookies.next().unwrap(), "e=f");
256
+ assert_eq!(cookies.next().unwrap(), "g=h");
257
+ assert_eq!(cookies.next(), None);
258
+ }
259
+ "/3" => {
260
+ assert_eq!(req.method(), "GET");
261
+ assert_eq!(req.headers()[USER_AGENT], "default-agent");
262
+ let mut cookies = req.headers().get_all(COOKIE).iter();
263
+ assert_eq!(cookies.next().unwrap(), "a=b");
264
+ assert_eq!(cookies.next().unwrap(), "c=d");
265
+ assert_eq!(cookies.next(), None);
266
+ }
267
+ "/4" => {
268
+ assert_eq!(req.method(), "GET");
269
+ assert_eq!(req.headers()[USER_AGENT], "default-agent");
270
+ let mut cookies = req.headers().get_all(COOKIE).iter();
271
+ assert_eq!(cookies.next().unwrap(), "e=f");
272
+ assert_eq!(cookies.next().unwrap(), "g=h");
273
+ assert_eq!(cookies.next(), None);
274
+ }
275
+ _ => {
276
+ unreachable!("Unexpected request path: {}", path);
277
+ }
278
+ }
279
+
280
+ http::Response::default()
281
+ });
282
+
283
+ let mut default_headers = header::HeaderMap::new();
284
+ default_headers.insert(
285
+ USER_AGENT,
286
+ header::HeaderValue::from_static("default-agent"),
287
+ );
288
+ default_headers.insert(COOKIE, header::HeaderValue::from_static("a=b"));
289
+ default_headers.append(COOKIE, header::HeaderValue::from_static("c=d"));
290
+
291
+ let client = Client::builder()
292
+ .no_proxy()
293
+ .default_headers(default_headers)
294
+ .build()
295
+ .unwrap();
296
+
297
+ let url = format!("http://{}/1", server.addr());
298
+ let res = client
299
+ .get(&url)
300
+ .header(USER_AGENT, "my-custom-agent")
301
+ .send()
302
+ .await
303
+ .unwrap();
304
+ assert_eq!(res.uri(), url.as_str());
305
+ assert_eq!(res.status(), wreq::StatusCode::OK);
306
+
307
+ let url = format!("http://{}/2", server.addr());
308
+ let res = client
309
+ .get(&url)
310
+ .header(USER_AGENT, "my-custom-agent")
311
+ .header(COOKIE, "e=f")
312
+ .header(COOKIE, "g=h")
313
+ .send()
314
+ .await
315
+ .unwrap();
316
+ assert_eq!(res.uri(), url.as_str());
317
+ assert_eq!(res.status(), wreq::StatusCode::OK);
318
+
319
+ let url = format!("http://{}/3", server.addr());
320
+ let res = client.get(&url).send().await.unwrap();
321
+ assert_eq!(res.uri(), url.as_str());
322
+ assert_eq!(res.status(), wreq::StatusCode::OK);
323
+
324
+ let url = format!("http://{}/4", server.addr());
325
+ let res = client
326
+ .get(&url)
327
+ .header(COOKIE, "e=f")
328
+ .header(COOKIE, "g=h")
329
+ .send()
330
+ .await
331
+ .unwrap();
332
+ assert_eq!(res.uri(), url.as_str());
333
+ assert_eq!(res.status(), wreq::StatusCode::OK);
334
+ }
335
+
336
+ #[tokio::test]
337
+ async fn donot_set_content_length_0_if_have_no_body() {
338
+ let server = server::http(move |req| async move {
339
+ let headers = req.headers();
340
+ assert_eq!(headers.get(CONTENT_LENGTH), None);
341
+ assert!(headers.get(CONTENT_TYPE).is_none());
342
+ assert!(headers.get(TRANSFER_ENCODING).is_none());
343
+ http::Response::default()
344
+ });
345
+
346
+ let url = format!("http://{}/content-length", server.addr());
347
+ let res = Client::builder()
348
+ .no_proxy()
349
+ .build()
350
+ .expect("client builder")
351
+ .get(&url)
352
+ .send()
353
+ .await
354
+ .expect("request");
355
+
356
+ assert_eq!(res.status(), wreq::StatusCode::OK);
357
+ }
358
+
359
+ #[tokio::test]
360
+ async fn user_agent() {
361
+ let server = server::http(move |req| async move {
362
+ assert_eq!(req.headers()["user-agent"], "wreq-test-agent");
363
+ http::Response::default()
364
+ });
365
+
366
+ let url = format!("http://{}/ua", server.addr());
367
+ let res = Client::builder()
368
+ .user_agent("wreq-test-agent")
369
+ .build()
370
+ .expect("client builder")
371
+ .get(&url)
372
+ .send()
373
+ .await
374
+ .expect("request");
375
+
376
+ assert_eq!(res.status(), wreq::StatusCode::OK);
377
+ }
378
+
379
+ #[tokio::test]
380
+ async fn response_text() {
381
+ let _ = env_logger::try_init();
382
+
383
+ let server = server::http(move |_req| async { http::Response::new("Hello".into()) });
384
+
385
+ let client = Client::new();
386
+
387
+ let res = client
388
+ .get(format!("http://{}/text", server.addr()))
389
+ .send()
390
+ .await
391
+ .expect("Failed to get");
392
+ assert_eq!(res.content_length(), Some(5));
393
+ let text = res.text().await.expect("Failed to get text");
394
+ assert_eq!("Hello", text);
395
+ }
396
+
397
+ #[tokio::test]
398
+ async fn response_bytes() {
399
+ let _ = env_logger::try_init();
400
+
401
+ let server = server::http(move |_req| async { http::Response::new("Hello".into()) });
402
+
403
+ let client = Client::new();
404
+
405
+ let res = client
406
+ .get(format!("http://{}/bytes", server.addr()))
407
+ .send()
408
+ .await
409
+ .expect("Failed to get");
410
+ assert_eq!(res.content_length(), Some(5));
411
+ let bytes = res.bytes().await.expect("res.bytes()");
412
+ assert_eq!("Hello", bytes);
413
+ }
414
+
415
+ #[tokio::test]
416
+ #[cfg(feature = "json")]
417
+ async fn response_json() {
418
+ let _ = env_logger::try_init();
419
+
420
+ let server = server::http(move |_req| async { http::Response::new("\"Hello\"".into()) });
421
+
422
+ let client = Client::new();
423
+
424
+ let res = client
425
+ .get(format!("http://{}/json", server.addr()))
426
+ .send()
427
+ .await
428
+ .expect("Failed to get");
429
+ let text = res.json::<String>().await.expect("Failed to get json");
430
+ assert_eq!("Hello", text);
431
+ }
432
+
433
+ #[tokio::test]
434
+ async fn body_pipe_response() {
435
+ let _ = env_logger::try_init();
436
+
437
+ let server = server::http(move |req| async move {
438
+ if req.uri() == "/get" {
439
+ http::Response::new("pipe me".into())
440
+ } else {
441
+ assert_eq!(req.uri(), "/pipe");
442
+ assert_eq!(req.headers()["content-length"], "7");
443
+
444
+ let full: Vec<u8> = req
445
+ .into_body()
446
+ .collect()
447
+ .await
448
+ .expect("must succeed")
449
+ .to_bytes()
450
+ .to_vec();
451
+
452
+ assert_eq!(full, b"pipe me");
453
+
454
+ http::Response::default()
455
+ }
456
+ });
457
+
458
+ let client = Client::new();
459
+
460
+ let res1 = client
461
+ .get(format!("http://{}/get", server.addr()))
462
+ .send()
463
+ .await
464
+ .expect("get1");
465
+
466
+ assert_eq!(res1.status(), wreq::StatusCode::OK);
467
+ assert_eq!(res1.content_length(), Some(7));
468
+
469
+ // and now ensure we can "pipe" the response to another request
470
+ let res2 = client
471
+ .post(format!("http://{}/pipe", server.addr()))
472
+ .body(res1)
473
+ .send()
474
+ .await
475
+ .expect("res2");
476
+
477
+ assert_eq!(res2.status(), wreq::StatusCode::OK);
478
+ }
479
+
480
+ #[tokio::test]
481
+ async fn overridden_dns_resolution_with_gai() {
482
+ let _ = env_logger::builder().is_test(true).try_init();
483
+ let server = server::http(move |_req| async { http::Response::new("Hello".into()) });
484
+
485
+ let overridden_domain = "rust-lang.org";
486
+ let url = format!(
487
+ "http://{overridden_domain}:{}/domain_override",
488
+ server.addr().port()
489
+ );
490
+ let client = Client::builder()
491
+ .no_proxy()
492
+ .resolve(overridden_domain, server.addr())
493
+ .build()
494
+ .expect("client builder");
495
+ let req = client.get(&url);
496
+ let res = req.send().await.expect("request");
497
+
498
+ assert_eq!(res.status(), wreq::StatusCode::OK);
499
+ let text = res.text().await.expect("Failed to get text");
500
+ assert_eq!("Hello", text);
501
+ }
502
+
503
+ #[tokio::test]
504
+ async fn overridden_dns_resolution_with_gai_multiple() {
505
+ let _ = env_logger::builder().is_test(true).try_init();
506
+ let server = server::http(move |_req| async { http::Response::new("Hello".into()) });
507
+
508
+ let overridden_domain = "rust-lang.org";
509
+ let url = format!(
510
+ "http://{overridden_domain}:{}/domain_override",
511
+ server.addr().port()
512
+ );
513
+ // the server runs on IPv4 localhost, so provide both IPv4 and IPv6 and let the happy eyeballs
514
+ // algorithm decide which address to use.
515
+ let client = Client::builder()
516
+ .no_proxy()
517
+ .resolve_to_addrs(
518
+ overridden_domain,
519
+ [
520
+ std::net::SocketAddr::new(
521
+ std::net::IpAddr::V6(std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)),
522
+ server.addr().port(),
523
+ ),
524
+ server.addr(),
525
+ ],
526
+ )
527
+ .build()
528
+ .expect("client builder");
529
+ let req = client.get(&url);
530
+ let res = req.send().await.expect("request");
531
+
532
+ assert_eq!(res.status(), wreq::StatusCode::OK);
533
+ let text = res.text().await.expect("Failed to get text");
534
+ assert_eq!("Hello", text);
535
+ }
536
+
537
+ #[cfg(feature = "hickory-dns")]
538
+ #[tokio::test]
539
+ async fn overridden_dns_resolution_with_hickory_dns() {
540
+ let _ = env_logger::builder().is_test(true).try_init();
541
+ let server = server::http(move |_req| async { http::Response::new("Hello".into()) });
542
+
543
+ let overridden_domain = "rust-lang.org";
544
+ let url = format!(
545
+ "http://{overridden_domain}:{}/domain_override",
546
+ server.addr().port()
547
+ );
548
+ let client = Client::builder()
549
+ .no_proxy()
550
+ .resolve(overridden_domain, server.addr())
551
+ .build()
552
+ .expect("client builder");
553
+ let req = client.get(&url);
554
+ let res = req.send().await.expect("request");
555
+
556
+ assert_eq!(res.status(), wreq::StatusCode::OK);
557
+ let text = res.text().await.expect("Failed to get text");
558
+ assert_eq!("Hello", text);
559
+ }
560
+
561
+ #[cfg(feature = "hickory-dns")]
562
+ #[tokio::test]
563
+ async fn overridden_dns_resolution_with_hickory_dns_multiple() {
564
+ let _ = env_logger::builder().is_test(true).try_init();
565
+ let server = server::http(move |_req| async { http::Response::new("Hello".into()) });
566
+
567
+ let overridden_domain = "rust-lang.org";
568
+ let url = format!(
569
+ "http://{overridden_domain}:{}/domain_override",
570
+ server.addr().port()
571
+ );
572
+ // the server runs on IPv4 localhost, so provide both IPv4 and IPv6 and let the happy eyeballs
573
+ // algorithm decide which address to use.
574
+ let client = Client::builder()
575
+ .no_proxy()
576
+ .resolve_to_addrs(
577
+ overridden_domain,
578
+ [
579
+ std::net::SocketAddr::new(
580
+ std::net::IpAddr::V6(std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)),
581
+ server.addr().port(),
582
+ ),
583
+ server.addr(),
584
+ ],
585
+ )
586
+ .build()
587
+ .expect("client builder");
588
+ let req = client.get(&url);
589
+ let res = req.send().await.expect("request");
590
+
591
+ assert_eq!(res.status(), wreq::StatusCode::OK);
592
+ let text = res.text().await.expect("Failed to get text");
593
+ assert_eq!("Hello", text);
594
+ }
595
+
596
+ #[test]
597
+ #[cfg(feature = "json")]
598
+ fn add_json_default_content_type_if_not_set_manually() {
599
+ let mut map = HashMap::new();
600
+ map.insert("body", "json");
601
+ let content_type = http::HeaderValue::from_static("application/vnd.api+json");
602
+ let req = Client::new()
603
+ .post("https://google.com/")
604
+ .header(CONTENT_TYPE, &content_type)
605
+ .json(&map)
606
+ .build()
607
+ .expect("request is not valid");
608
+
609
+ assert_eq!(content_type, req.headers().get(CONTENT_TYPE).unwrap());
610
+ }
611
+
612
+ #[test]
613
+ #[cfg(feature = "json")]
614
+ fn update_json_content_type_if_set_manually() {
615
+ let mut map = HashMap::new();
616
+ map.insert("body", "json");
617
+ let req = Client::new()
618
+ .post("https://google.com/")
619
+ .json(&map)
620
+ .build()
621
+ .expect("request is not valid");
622
+
623
+ assert_eq!("application/json", req.headers().get(CONTENT_TYPE).unwrap());
624
+ }
625
+
626
+ #[tokio::test]
627
+ async fn test_tls_info() {
628
+ let resp = Client::builder()
629
+ .tls_info(true)
630
+ .build()
631
+ .expect("client builder")
632
+ .get("https://google.com")
633
+ .send()
634
+ .await
635
+ .expect("response");
636
+ let tls_info = resp.extensions().get::<TlsInfo>().unwrap();
637
+ let peer_certificate = tls_info.peer_certificate();
638
+ assert!(peer_certificate.is_some());
639
+ let der = peer_certificate.unwrap();
640
+ assert_eq!(der[0], 0x30); // ASN.1 SEQUENCE
641
+
642
+ let resp = Client::builder()
643
+ .build()
644
+ .expect("client builder")
645
+ .get("https://google.com")
646
+ .send()
647
+ .await
648
+ .expect("response");
649
+ let tls_info = resp.extensions().get::<TlsInfo>();
650
+ assert!(tls_info.is_none());
651
+ }
652
+
653
+ #[tokio::test]
654
+ async fn close_connection_after_idle_timeout() {
655
+ let mut server = server::http(move |_| async move { http::Response::default() });
656
+
657
+ let client = Client::builder()
658
+ .pool_idle_timeout(std::time::Duration::from_secs(1))
659
+ .build()
660
+ .unwrap();
661
+
662
+ let url = format!("http://{}", server.addr());
663
+
664
+ client.get(&url).send().await.unwrap();
665
+
666
+ tokio::time::sleep(std::time::Duration::from_secs(2)).await;
667
+
668
+ assert!(
669
+ server
670
+ .events()
671
+ .iter()
672
+ .any(|e| matches!(e, server::Event::ConnectionClosed))
673
+ );
674
+ }
675
+
676
+ #[tokio::test]
677
+ async fn http1_reason_phrase() {
678
+ let server = server::low_level_with_response(|_raw_request, client_socket| {
679
+ Box::new(async move {
680
+ client_socket
681
+ .write_all(b"HTTP/1.1 418 I'm not a teapot\r\nContent-Length: 0\r\n\r\n")
682
+ .await
683
+ .expect("response write_all failed");
684
+ })
685
+ });
686
+
687
+ let client = Client::new();
688
+
689
+ let res = client
690
+ .get(format!("http://{}", server.addr()))
691
+ .send()
692
+ .await
693
+ .expect("Failed to get");
694
+
695
+ assert_eq!(
696
+ res.error_for_status().unwrap_err().to_string(),
697
+ format!(
698
+ "HTTP status client error (418 I'm not a teapot) for uri (http://{}/)",
699
+ server.addr()
700
+ )
701
+ );
702
+ }
703
+
704
+ #[tokio::test]
705
+ async fn error_has_url() {
706
+ let u = "http://does.not.exist.local/ever";
707
+ let err = wreq::get(u).send().await.unwrap_err();
708
+ assert_eq!(
709
+ err.uri().map(ToString::to_string).as_deref(),
710
+ Some(u),
711
+ "{err:?}"
712
+ );
713
+ }
714
+
715
+ #[tokio::test]
716
+ async fn http1_only() {
717
+ let server = server::http(move |_| async move { http::Response::default() });
718
+
719
+ let resp = Client::builder()
720
+ .http1_only()
721
+ .build()
722
+ .unwrap()
723
+ .get(format!("http://{}", server.addr()))
724
+ .send()
725
+ .await
726
+ .unwrap();
727
+
728
+ assert_eq!(resp.version(), wreq::Version::HTTP_11);
729
+
730
+ let resp = Client::builder()
731
+ .build()
732
+ .unwrap()
733
+ .get(format!("http://{}", server.addr()))
734
+ .version(Version::HTTP_11)
735
+ .send()
736
+ .await
737
+ .unwrap();
738
+
739
+ assert_eq!(resp.version(), wreq::Version::HTTP_11);
740
+ }
741
+
742
+ #[tokio::test]
743
+ async fn http2_only() {
744
+ let server = server::http(move |_| async move { http::Response::default() });
745
+
746
+ let resp = Client::builder()
747
+ .http2_only()
748
+ .build()
749
+ .unwrap()
750
+ .get(format!("http://{}", server.addr()))
751
+ .send()
752
+ .await
753
+ .unwrap();
754
+
755
+ assert_eq!(resp.version(), wreq::Version::HTTP_2);
756
+
757
+ let resp = Client::builder()
758
+ .build()
759
+ .unwrap()
760
+ .get(format!("http://{}", server.addr()))
761
+ .version(Version::HTTP_2)
762
+ .send()
763
+ .await
764
+ .unwrap();
765
+
766
+ assert_eq!(resp.version(), wreq::Version::HTTP_2);
767
+ }
768
+
769
+ #[tokio::test]
770
+ async fn connection_pool_cache() {
771
+ let client = Client::default();
772
+ let url = "https://hyper.rs";
773
+
774
+ let resp = client
775
+ .get(url)
776
+ .version(http::Version::HTTP_2)
777
+ .send()
778
+ .await
779
+ .unwrap();
780
+
781
+ assert_eq!(resp.status(), wreq::StatusCode::OK);
782
+ assert_eq!(resp.version(), http::Version::HTTP_2);
783
+
784
+ let resp = client
785
+ .get(url)
786
+ .version(http::Version::HTTP_11)
787
+ .send()
788
+ .await
789
+ .unwrap();
790
+
791
+ assert_eq!(resp.status(), wreq::StatusCode::OK);
792
+ assert_eq!(resp.version(), http::Version::HTTP_11);
793
+
794
+ let resp = client
795
+ .get(url)
796
+ .version(http::Version::HTTP_2)
797
+ .send()
798
+ .await
799
+ .unwrap();
800
+
801
+ assert_eq!(resp.status(), wreq::StatusCode::OK);
802
+ assert_eq!(resp.version(), http::Version::HTTP_2);
803
+ }
804
+
805
+ #[tokio::test]
806
+ async fn http1_send_case_sensitive_headers() {
807
+ // Create a request with a case-sensitive header
808
+ let mut orig_headers = OrigHeaderMap::new();
809
+ orig_headers.insert("X-custom-header");
810
+ orig_headers.insert("Host");
811
+
812
+ let resp = wreq::get("https://tls.peet.ws/api/all")
813
+ .header("X-Custom-Header", "value")
814
+ .orig_headers(orig_headers)
815
+ .version(Version::HTTP_11)
816
+ .send()
817
+ .await
818
+ .unwrap()
819
+ .text()
820
+ .await
821
+ .unwrap();
822
+
823
+ assert!(resp.contains("X-custom-header"));
824
+ assert!(resp.contains("Host"));
825
+ }
826
+
827
+ #[tokio::test]
828
+ async fn tunnel_includes_proxy_auth_with_multiple_proxies() {
829
+ let url = "http://hyper.rs.local/prox";
830
+ let server1 = server::http(move |req| {
831
+ assert_eq!(req.method(), "GET");
832
+ assert_eq!(req.uri(), url);
833
+ assert_eq!(req.headers()["host"], "hyper.rs.local");
834
+ assert_eq!(
835
+ req.headers()["proxy-authorization"],
836
+ "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
837
+ );
838
+ assert_eq!(req.headers()["proxy-header"], "proxy2");
839
+ async { http::Response::default() }
840
+ });
841
+
842
+ let proxy_url = format!("http://Aladdin:open%20sesame@{}", server1.addr());
843
+
844
+ let mut headers1 = wreq::header::HeaderMap::new();
845
+ headers1.insert("proxy-header", "proxy1".parse().unwrap());
846
+
847
+ let mut headers2 = wreq::header::HeaderMap::new();
848
+ headers2.insert("proxy-header", "proxy2".parse().unwrap());
849
+
850
+ let client = Client::builder()
851
+ // When processing proxy headers, the first one is iterated,
852
+ // and if the current URL does not match, the proxy is skipped
853
+ .proxy(
854
+ wreq::Proxy::https(&proxy_url)
855
+ .unwrap()
856
+ .custom_http_headers(headers1.clone()),
857
+ )
858
+ // When processing proxy headers, the second one is iterated,
859
+ // and for the current URL matching, the proxy will be used
860
+ .proxy(
861
+ wreq::Proxy::http(&proxy_url)
862
+ .unwrap()
863
+ .custom_http_headers(headers2.clone()),
864
+ )
865
+ .build()
866
+ .unwrap();
867
+
868
+ let res = client.get(url).send().await.unwrap();
869
+
870
+ assert_eq!(res.uri(), url);
871
+ assert_eq!(res.status(), wreq::StatusCode::OK);
872
+
873
+ let client = Client::builder()
874
+ // When processing proxy headers, the first one is iterated,
875
+ // and for the current URL matching, the proxy will be used
876
+ .proxy(
877
+ wreq::Proxy::http(&proxy_url)
878
+ .unwrap()
879
+ .custom_http_headers(headers2),
880
+ )
881
+ // When processing proxy headers, the second one is iterated,
882
+ // and if the current URL does not match, the proxy is skipped
883
+ .proxy(
884
+ wreq::Proxy::https(&proxy_url)
885
+ .unwrap()
886
+ .custom_http_headers(headers1),
887
+ )
888
+ .build()
889
+ .unwrap();
890
+
891
+ let res = client.get(url).send().await.unwrap();
892
+
893
+ assert_eq!(res.uri(), url);
894
+ assert_eq!(res.status(), wreq::StatusCode::OK);
895
+ }
896
+
897
+ #[tokio::test]
898
+ async fn skip_default_headers() {
899
+ let server = server::http(move |req| async move {
900
+ let path = req.uri().path();
901
+ match path {
902
+ "/skip" => {
903
+ assert_eq!(req.method(), "GET");
904
+ assert_eq!(req.headers().get(USER_AGENT), None);
905
+ assert_eq!(req.headers().get(ACCEPT), None);
906
+ }
907
+ "/no_skip" => {
908
+ assert_eq!(req.method(), "GET");
909
+ assert_eq!(req.headers()[USER_AGENT], "test-agent");
910
+ assert_eq!(req.headers()[ACCEPT], "*/*");
911
+ }
912
+ _ => unreachable!("Unexpected request path: {path}"),
913
+ }
914
+
915
+ http::Response::default()
916
+ });
917
+
918
+ let client = Client::builder()
919
+ .default_headers({
920
+ let mut headers = wreq::header::HeaderMap::new();
921
+ headers.insert(USER_AGENT, "test-agent".parse().unwrap());
922
+ headers.insert(ACCEPT, "*/*".parse().unwrap());
923
+ headers
924
+ })
925
+ .no_proxy()
926
+ .build()
927
+ .unwrap();
928
+
929
+ let url = format!("http://{}/skip", server.addr());
930
+ let res = client
931
+ .get(&url)
932
+ .default_headers(false)
933
+ .send()
934
+ .await
935
+ .unwrap();
936
+ assert_eq!(res.uri(), url.as_str());
937
+ assert_eq!(res.status(), wreq::StatusCode::OK);
938
+
939
+ let url = format!("http://{}/no_skip", server.addr());
940
+ let res = client.get(&url).send().await.unwrap();
941
+ assert_eq!(res.uri(), url.as_str());
942
+ assert_eq!(res.status(), wreq::StatusCode::OK);
943
+ }
944
+
945
+ #[tokio::test]
946
+ async fn test_client_same_header_values_append() {
947
+ let server = server::http(move |req| async move {
948
+ let path = req.uri().path();
949
+ match path {
950
+ "/duplicate-cookies" => {
951
+ let cookie_values: Vec<_> = req.headers().get_all(header::COOKIE).iter().collect();
952
+ assert_eq!(cookie_values.len(), 1);
953
+ assert_eq!(cookie_values[0], "duplicate=same_value");
954
+ }
955
+ "/no-duplicate-cookies" => {
956
+ let cookie_values: Vec<_> = req.headers().get_all(header::COOKIE).iter().collect();
957
+ assert_eq!(cookie_values.len(), 3);
958
+ assert_eq!(cookie_values[0], "duplicate=same_value");
959
+ assert_eq!(cookie_values[1], "unique1=value1");
960
+ assert_eq!(cookie_values[2], "unique2=value2");
961
+ }
962
+ _ => unreachable!("Unexpected request path: {}", path),
963
+ }
964
+
965
+ http::Response::default()
966
+ });
967
+
968
+ let client = Client::builder()
969
+ .no_proxy()
970
+ .default_headers({
971
+ let mut headers = HeaderMap::new();
972
+ headers.insert(
973
+ header::COOKIE,
974
+ HeaderValue::from_static("duplicate=same_value"),
975
+ );
976
+ headers.append(header::COOKIE, HeaderValue::from_static("unique1=value1"));
977
+ headers.append(header::COOKIE, HeaderValue::from_static("unique2=value2"));
978
+ headers
979
+ })
980
+ .build()
981
+ .unwrap();
982
+
983
+ let res = client
984
+ .get(format!("http://{}/duplicate-cookies", server.addr()))
985
+ .header(header::COOKIE, "duplicate=same_value")
986
+ .send()
987
+ .await
988
+ .unwrap();
989
+ assert_eq!(res.status(), wreq::StatusCode::OK);
990
+
991
+ let res = client
992
+ .get(format!("http://{}/no-duplicate-cookies", server.addr()))
993
+ .send()
994
+ .await
995
+ .unwrap();
996
+ assert_eq!(res.status(), wreq::StatusCode::OK);
997
+ }
998
+
999
+ #[cfg(all(
1000
+ feature = "gzip",
1001
+ feature = "brotli",
1002
+ feature = "deflate",
1003
+ feature = "zstd"
1004
+ ))]
1005
+ #[tokio::test]
1006
+ async fn test_client_default_accept_encoding() {
1007
+ let server = server::http(move |req| async move {
1008
+ let accept_encoding = req.headers().get(header::ACCEPT_ENCODING).unwrap();
1009
+ if req.uri() == "/default" {
1010
+ assert_eq!(accept_encoding, "zstd");
1011
+ }
1012
+
1013
+ if req.uri() == "/custom" {
1014
+ assert_eq!(accept_encoding, "gzip");
1015
+ }
1016
+
1017
+ http::Response::default()
1018
+ });
1019
+
1020
+ let client = Client::builder()
1021
+ .default_headers({
1022
+ let mut headers = HeaderMap::new();
1023
+ headers.insert(header::ACCEPT_ENCODING, HeaderValue::from_static("zstd"));
1024
+ headers
1025
+ })
1026
+ .no_proxy()
1027
+ .build()
1028
+ .unwrap();
1029
+
1030
+ let _ = client
1031
+ .get(format!("http://{}/default", server.addr()))
1032
+ .send()
1033
+ .await
1034
+ .unwrap();
1035
+
1036
+ let _ = client
1037
+ .get(format!("http://{}/custom", server.addr()))
1038
+ .header(header::ACCEPT_ENCODING, "gzip")
1039
+ .send()
1040
+ .await
1041
+ .unwrap();
1042
+ }
1043
+
1044
+ #[tokio::test]
1045
+ async fn response_trailers() {
1046
+ let server = server::http(move |req| async move {
1047
+ assert_eq!(req.uri().path(), "/trailers");
1048
+
1049
+ let body = Full::new(Bytes::from("HelloWorld!")).with_trailers(async move {
1050
+ let mut trailers = http::HeaderMap::new();
1051
+ trailers.insert("chunky-trailer1", HeaderValue::from_static("value1"));
1052
+ trailers.insert("chunky-trailer2", HeaderValue::from_static("value2"));
1053
+ Some(Ok(trailers))
1054
+ });
1055
+ let mut resp = http::Response::new(wreq::Body::wrap(body));
1056
+ resp.headers_mut().insert(
1057
+ header::TRAILER,
1058
+ header::HeaderValue::from_static("chunky-trailer1, chunky-trailer2"),
1059
+ );
1060
+ resp.headers_mut().insert(
1061
+ header::TRANSFER_ENCODING,
1062
+ header::HeaderValue::from_static("chunked"),
1063
+ );
1064
+
1065
+ resp
1066
+ });
1067
+
1068
+ let mut res = wreq::get(format!("http://{}/trailers", server.addr()))
1069
+ .header(header::TE, "trailers")
1070
+ .send()
1071
+ .await
1072
+ .expect("Failed to get response");
1073
+
1074
+ assert_eq!(res.status(), StatusCode::OK);
1075
+
1076
+ let mut body_content = Vec::new();
1077
+ let mut trailers = HeaderMap::default();
1078
+ while let Some(chunk) = res.frame().await {
1079
+ match chunk
1080
+ .unwrap()
1081
+ .into_data()
1082
+ .map_err(|frame| frame.into_trailers())
1083
+ {
1084
+ Ok(res) => {
1085
+ body_content.extend_from_slice(&res);
1086
+ }
1087
+ Err(Ok(res)) => {
1088
+ trailers.extend(res);
1089
+ }
1090
+ _ => (),
1091
+ }
1092
+ }
1093
+
1094
+ let body = String::from_utf8(body_content).expect("Invalid UTF-8");
1095
+ assert_eq!(body, "HelloWorld!");
1096
+ assert_eq!(trailers["chunky-trailer1"], "value1");
1097
+ assert_eq!(trailers["chunky-trailer2"], "value2");
1098
+ }