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,629 @@
1
+ mod support;
2
+ use http::StatusCode;
3
+ use http_body_util::BodyExt;
4
+ use support::server;
5
+ use wreq::{
6
+ Body, Client,
7
+ redirect::{History, Policy},
8
+ };
9
+
10
+ #[tokio::test]
11
+ async fn test_redirect_301_and_302_and_303_changes_post_to_get() {
12
+ let codes = [301u16, 302, 303];
13
+
14
+ for &code in &codes {
15
+ let redirect = server::http(move |req| async move {
16
+ if req.method() == "POST" {
17
+ assert_eq!(req.uri(), &*format!("/{code}"));
18
+ http::Response::builder()
19
+ .status(code)
20
+ .header("location", "/dst")
21
+ .header("server", "test-redirect")
22
+ .body(Body::default())
23
+ .unwrap()
24
+ } else {
25
+ assert_eq!(req.method(), "GET");
26
+
27
+ http::Response::builder()
28
+ .header("server", "test-dst")
29
+ .body(Body::default())
30
+ .unwrap()
31
+ }
32
+ });
33
+
34
+ let url = format!("http://{}/{}", redirect.addr(), code);
35
+ let dst = format!("http://{}/{}", redirect.addr(), "dst");
36
+ let res = wreq::post(&url)
37
+ .redirect(Policy::default())
38
+ .send()
39
+ .await
40
+ .unwrap();
41
+ assert_eq!(res.uri(), dst.as_str());
42
+ assert_eq!(res.status(), wreq::StatusCode::OK);
43
+ assert_eq!(
44
+ res.headers().get(wreq::header::SERVER).unwrap(),
45
+ &"test-dst"
46
+ );
47
+ }
48
+ }
49
+
50
+ #[tokio::test]
51
+ async fn test_redirect_307_and_308_tries_to_get_again() {
52
+ let codes = [307u16, 308];
53
+ for &code in &codes {
54
+ let redirect = server::http(move |req| async move {
55
+ assert_eq!(req.method(), "GET");
56
+ if req.uri() == &*format!("/{code}") {
57
+ http::Response::builder()
58
+ .status(code)
59
+ .header("location", "/dst")
60
+ .header("server", "test-redirect")
61
+ .body(Body::default())
62
+ .unwrap()
63
+ } else {
64
+ assert_eq!(req.uri(), "/dst");
65
+
66
+ http::Response::builder()
67
+ .header("server", "test-dst")
68
+ .body(Body::default())
69
+ .unwrap()
70
+ }
71
+ });
72
+
73
+ let url = format!("http://{}/{}", redirect.addr(), code);
74
+ let dst = format!("http://{}/{}", redirect.addr(), "dst");
75
+ let res = wreq::get(&url)
76
+ .redirect(Policy::default())
77
+ .send()
78
+ .await
79
+ .unwrap();
80
+ assert_eq!(res.uri(), dst.as_str());
81
+ assert_eq!(res.status(), wreq::StatusCode::OK);
82
+ assert_eq!(
83
+ res.headers().get(wreq::header::SERVER).unwrap(),
84
+ &"test-dst"
85
+ );
86
+ }
87
+ }
88
+
89
+ #[tokio::test]
90
+ async fn test_redirect_307_and_308_tries_to_post_again() {
91
+ let _ = pretty_env_logger::env_logger::try_init();
92
+
93
+ let codes = [307u16, 308];
94
+ for &code in &codes {
95
+ let redirect = server::http(move |mut req| async move {
96
+ assert_eq!(req.method(), "POST");
97
+ assert_eq!(req.headers()["content-length"], "5");
98
+
99
+ let data = req
100
+ .body_mut()
101
+ .frame()
102
+ .await
103
+ .unwrap()
104
+ .unwrap()
105
+ .into_data()
106
+ .unwrap();
107
+ assert_eq!(&*data, b"Hello");
108
+
109
+ if req.uri() == &*format!("/{code}") {
110
+ http::Response::builder()
111
+ .status(code)
112
+ .header("location", "/dst")
113
+ .header("server", "test-redirect")
114
+ .body(Body::default())
115
+ .unwrap()
116
+ } else {
117
+ assert_eq!(req.uri(), "/dst");
118
+
119
+ http::Response::builder()
120
+ .header("server", "test-dst")
121
+ .body(Body::default())
122
+ .unwrap()
123
+ }
124
+ });
125
+
126
+ let url = format!("http://{}/{}", redirect.addr(), code);
127
+ let dst = format!("http://{}/{}", redirect.addr(), "dst");
128
+ let res = wreq::post(&url)
129
+ .redirect(Policy::default())
130
+ .body("Hello")
131
+ .send()
132
+ .await
133
+ .unwrap();
134
+ assert_eq!(res.uri(), dst.as_str());
135
+ assert_eq!(res.status(), wreq::StatusCode::OK);
136
+ assert_eq!(
137
+ res.headers().get(wreq::header::SERVER).unwrap(),
138
+ &"test-dst"
139
+ );
140
+ }
141
+ }
142
+
143
+ #[tokio::test]
144
+ async fn test_redirect_removes_sensitive_headers() {
145
+ use tokio::sync::watch;
146
+
147
+ let (tx, rx) = watch::channel::<Option<std::net::SocketAddr>>(None);
148
+
149
+ let end_server = server::http(move |req| {
150
+ let mut rx = rx.clone();
151
+ async move {
152
+ assert_eq!(req.headers().get("cookie"), None);
153
+
154
+ rx.changed().await.unwrap();
155
+ let mid_addr = rx.borrow().unwrap();
156
+ assert_eq!(
157
+ req.headers()["referer"],
158
+ format!("http://{mid_addr}/sensitive")
159
+ );
160
+ http::Response::default()
161
+ }
162
+ });
163
+
164
+ let end_addr = end_server.addr();
165
+
166
+ let mid_server = server::http(move |req| async move {
167
+ assert_eq!(req.headers()["cookie"], "foo=bar");
168
+ http::Response::builder()
169
+ .status(302)
170
+ .header("location", format!("http://{end_addr}/end"))
171
+ .body(Body::default())
172
+ .unwrap()
173
+ });
174
+
175
+ tx.send(Some(mid_server.addr())).unwrap();
176
+
177
+ Client::builder()
178
+ .redirect(Policy::default())
179
+ .build()
180
+ .unwrap()
181
+ .get(format!("http://{}/sensitive", mid_server.addr()))
182
+ .header(
183
+ wreq::header::COOKIE,
184
+ wreq::header::HeaderValue::from_static("foo=bar"),
185
+ )
186
+ .send()
187
+ .await
188
+ .unwrap();
189
+ }
190
+
191
+ #[tokio::test]
192
+ async fn test_redirect_policy_can_return_errors() {
193
+ let server = server::http(move |req| async move {
194
+ assert_eq!(req.uri(), "/loop");
195
+ http::Response::builder()
196
+ .status(302)
197
+ .header("location", "/loop")
198
+ .body(Body::default())
199
+ .unwrap()
200
+ });
201
+
202
+ let url = format!("http://{}/loop", server.addr());
203
+ let err = wreq::get(&url)
204
+ .redirect(Policy::default())
205
+ .send()
206
+ .await
207
+ .unwrap_err();
208
+ assert!(err.is_redirect());
209
+ }
210
+
211
+ #[tokio::test]
212
+ async fn test_redirect_policy_can_stop_redirects_without_an_error() {
213
+ let server = server::http(move |req| async move {
214
+ assert_eq!(req.uri(), "/no-redirect");
215
+ http::Response::builder()
216
+ .status(302)
217
+ .header("location", "/dont")
218
+ .body(Body::default())
219
+ .unwrap()
220
+ });
221
+
222
+ let url = format!("http://{}/no-redirect", server.addr());
223
+
224
+ let res = Client::builder()
225
+ .redirect(Policy::none())
226
+ .build()
227
+ .unwrap()
228
+ .get(&url)
229
+ .send()
230
+ .await
231
+ .unwrap();
232
+
233
+ assert_eq!(res.uri(), url.as_str());
234
+ assert_eq!(res.status(), wreq::StatusCode::FOUND);
235
+ }
236
+
237
+ #[tokio::test]
238
+ async fn test_referer_is_not_set_if_disabled() {
239
+ let server = server::http(move |req| async move {
240
+ if req.uri() == "/no-refer" {
241
+ http::Response::builder()
242
+ .status(302)
243
+ .header("location", "/dst")
244
+ .body(Body::default())
245
+ .unwrap()
246
+ } else {
247
+ assert_eq!(req.uri(), "/dst");
248
+ assert_eq!(req.headers().get("referer"), None);
249
+
250
+ http::Response::default()
251
+ }
252
+ });
253
+
254
+ Client::builder()
255
+ .referer(false)
256
+ .build()
257
+ .unwrap()
258
+ .get(format!("http://{}/no-refer", server.addr()))
259
+ .send()
260
+ .await
261
+ .unwrap();
262
+ }
263
+
264
+ #[tokio::test]
265
+ async fn test_invalid_location_stops_redirect_gh484() {
266
+ let server = server::http(move |_req| async move {
267
+ http::Response::builder()
268
+ .status(302)
269
+ .header("location", "http://www.yikes{KABOOM}")
270
+ .body(Body::default())
271
+ .unwrap()
272
+ });
273
+
274
+ let url = format!("http://{}/yikes", server.addr());
275
+
276
+ let res = wreq::get(&url).send().await.unwrap();
277
+
278
+ assert_eq!(res.uri(), url.as_str());
279
+ assert_eq!(res.status(), wreq::StatusCode::FOUND);
280
+ }
281
+
282
+ #[tokio::test]
283
+ async fn test_invalid_scheme_is_rejected() {
284
+ let server = server::http(move |_req| async move {
285
+ http::Response::builder()
286
+ .status(302)
287
+ .header("location", "htt://www.yikes.com/")
288
+ .body(Body::default())
289
+ .unwrap()
290
+ });
291
+
292
+ let url = format!("http://{}/yikes", server.addr());
293
+
294
+ let err = wreq::get(&url)
295
+ .redirect(Policy::default())
296
+ .send()
297
+ .await
298
+ .unwrap_err();
299
+ assert!(err.is_builder());
300
+ }
301
+
302
+ #[cfg(feature = "cookies")]
303
+ #[tokio::test]
304
+ async fn test_redirect_302_with_set_cookies() {
305
+ let code = 302;
306
+ let server = server::http(move |req| async move {
307
+ if req.uri() == "/302" {
308
+ http::Response::builder()
309
+ .status(302)
310
+ .header("location", "/dst")
311
+ .header("set-cookie", "key=value")
312
+ .body(Body::default())
313
+ .unwrap()
314
+ } else {
315
+ assert_eq!(req.uri(), "/dst");
316
+ assert_eq!(req.headers()["cookie"], "key=value");
317
+ http::Response::default()
318
+ }
319
+ });
320
+
321
+ let url = format!("http://{}/{}", server.addr(), code);
322
+ let dst = format!("http://{}/{}", server.addr(), "dst");
323
+
324
+ let client = Client::builder()
325
+ .cookie_store(true)
326
+ .redirect(Policy::default())
327
+ .build()
328
+ .unwrap();
329
+ let res = client.get(&url).send().await.unwrap();
330
+
331
+ assert_eq!(res.uri(), dst.as_str());
332
+ assert_eq!(res.status(), wreq::StatusCode::OK);
333
+ }
334
+
335
+ #[tokio::test]
336
+ async fn test_redirect_limit_to_1() {
337
+ let server = server::http(move |req| async move {
338
+ let i: i32 = req
339
+ .uri()
340
+ .path()
341
+ .rsplit('/')
342
+ .next()
343
+ .unwrap()
344
+ .parse::<i32>()
345
+ .unwrap();
346
+ assert!(req.uri().path().ends_with(&format!("/redirect/{i}")));
347
+ http::Response::builder()
348
+ .status(302)
349
+ .header("location", format!("/redirect/{}", i + 1))
350
+ .body(Body::default())
351
+ .unwrap()
352
+ });
353
+ // The number at the end of the uri indicates the total number of redirections
354
+ let url = format!("http://{}/redirect/0", server.addr());
355
+
356
+ let client = Client::builder()
357
+ .redirect(Policy::limited(1))
358
+ .build()
359
+ .unwrap();
360
+ let res = client.get(&url).send().await.unwrap_err();
361
+ // If the maximum limit is 1, then the final uri should be /redirect/1
362
+ assert_eq!(
363
+ res.uri().unwrap().to_string(),
364
+ format!("http://{}/redirect/1", server.addr()).as_str()
365
+ );
366
+ assert!(res.is_redirect());
367
+ }
368
+
369
+ #[tokio::test]
370
+ async fn test_scheme_only_check_after_policy_return_follow() {
371
+ let server = server::http(move |_| async move {
372
+ http::Response::builder()
373
+ .status(302)
374
+ .header("location", "htt://www.yikes.com/")
375
+ .body(Body::default())
376
+ .unwrap()
377
+ });
378
+
379
+ let url = format!("http://{}/yikes", server.addr());
380
+ let res = Client::builder()
381
+ .redirect(Policy::custom(|attempt| attempt.stop()))
382
+ .build()
383
+ .unwrap()
384
+ .get(&url)
385
+ .send()
386
+ .await;
387
+
388
+ assert!(res.is_ok());
389
+ assert_eq!(res.unwrap().status(), wreq::StatusCode::FOUND);
390
+
391
+ let res = Client::builder()
392
+ .redirect(Policy::custom(|attempt| attempt.follow()))
393
+ .build()
394
+ .unwrap()
395
+ .get(&url)
396
+ .send()
397
+ .await;
398
+
399
+ assert!(res.is_err());
400
+ assert!(res.unwrap_err().is_builder());
401
+ }
402
+
403
+ #[tokio::test]
404
+ async fn test_redirect_301_302_303_empty_payload_headers() {
405
+ let codes = [301u16, 302, 303];
406
+ for &code in &codes {
407
+ let redirect = server::http(move |mut req| async move {
408
+ if req.method() == "POST" {
409
+ let data = req
410
+ .body_mut()
411
+ .frame()
412
+ .await
413
+ .unwrap()
414
+ .unwrap()
415
+ .into_data()
416
+ .unwrap();
417
+
418
+ assert_eq!(&*data, b"Hello");
419
+ if req.headers().get(wreq::header::CONTENT_LENGTH).is_some() {
420
+ assert_eq!(req.headers()[wreq::header::CONTENT_LENGTH], "5");
421
+ }
422
+ assert_eq!(req.uri(), &*format!("/{code}"));
423
+
424
+ http::Response::builder()
425
+ .header("location", "/dst")
426
+ .header("server", "test-dst")
427
+ .status(code)
428
+ .body(Body::default())
429
+ .unwrap()
430
+ } else {
431
+ assert_eq!(req.method(), "GET");
432
+ assert!(req.headers().get(wreq::header::CONTENT_TYPE).is_none());
433
+ assert!(req.headers().get(wreq::header::CONTENT_LENGTH).is_none());
434
+ assert!(req.headers().get(wreq::header::CONTENT_ENCODING).is_none());
435
+ http::Response::builder()
436
+ .header("server", "test-dst")
437
+ .body(Body::default())
438
+ .unwrap()
439
+ }
440
+ });
441
+
442
+ let url = format!("http://{}/{}", redirect.addr(), code);
443
+ let dst = format!("http://{}/{}", redirect.addr(), "dst");
444
+ let res = wreq::post(&url)
445
+ .redirect(Policy::default())
446
+ .body("Hello")
447
+ .header(wreq::header::CONTENT_TYPE, "text/plain")
448
+ .header(wreq::header::CONTENT_LENGTH, "5")
449
+ .header(wreq::header::CONTENT_ENCODING, "identity")
450
+ .send()
451
+ .await
452
+ .unwrap();
453
+ assert_eq!(res.uri(), dst.as_str());
454
+ assert_eq!(res.status(), 200);
455
+ assert_eq!(
456
+ res.headers().get(wreq::header::SERVER).unwrap(),
457
+ &"test-dst"
458
+ );
459
+ }
460
+ }
461
+
462
+ #[tokio::test]
463
+ async fn test_redirect_history() {
464
+ let redirect = server::http(move |req| async move {
465
+ if req.uri() == "/first" {
466
+ http::Response::builder()
467
+ .status(302)
468
+ .header("location", "/second")
469
+ .body(Body::default())
470
+ .unwrap()
471
+ } else if req.uri() == "/second" {
472
+ http::Response::builder()
473
+ .status(302)
474
+ .header("location", "/dst")
475
+ .body(Body::default())
476
+ .unwrap()
477
+ } else {
478
+ assert_eq!(req.uri(), "/dst");
479
+
480
+ http::Response::builder()
481
+ .header("server", "test-dst")
482
+ .body(Body::default())
483
+ .unwrap()
484
+ }
485
+ });
486
+
487
+ let url = format!("http://{}/first", redirect.addr());
488
+ let dst = format!("http://{}/{}", redirect.addr(), "dst");
489
+
490
+ let client = Client::builder()
491
+ .redirect(Policy::default())
492
+ .build()
493
+ .unwrap();
494
+
495
+ let res = client.get(&url).send().await.unwrap();
496
+ assert_eq!(res.uri(), dst.as_str());
497
+ assert_eq!(res.status(), wreq::StatusCode::OK);
498
+ assert_eq!(
499
+ res.headers().get(wreq::header::SERVER).unwrap(),
500
+ &"test-dst"
501
+ );
502
+
503
+ let mut history = res.extensions().get::<History>().unwrap().into_iter();
504
+
505
+ let next1 = history.next().unwrap();
506
+ assert_eq!(next1.status, 302);
507
+ assert_eq!(next1.previous.path(), "/first");
508
+ assert_eq!(next1.uri.path(), "/second");
509
+ assert_eq!(next1.headers["location"], "/second");
510
+
511
+ let next2 = history.next().unwrap();
512
+ assert_eq!(next2.status, 302);
513
+ assert_eq!(next2.previous.path(), "/second");
514
+ assert_eq!(next2.uri.path(), "/dst");
515
+ assert_eq!(next2.headers["location"], "/dst");
516
+
517
+ assert!(history.next().is_none());
518
+ }
519
+
520
+ #[cfg(feature = "cookies")]
521
+ #[tokio::test]
522
+ async fn test_redirect_applies_set_cookie_from_redirect() {
523
+ let server = server::http(move |req| async move {
524
+ match req.uri().path() {
525
+ "/start" => http::Response::builder()
526
+ .status(302)
527
+ .header("location", "/dst")
528
+ .header("set-cookie", "session=abc; Path=/")
529
+ .body(Body::default())
530
+ .unwrap(),
531
+ "/dst" => {
532
+ assert_eq!(req.headers()["cookie"], "session=abc");
533
+ http::Response::builder()
534
+ .status(200)
535
+ .body(Body::default())
536
+ .unwrap()
537
+ }
538
+ _ => http::Response::builder()
539
+ .status(404)
540
+ .body(Body::default())
541
+ .unwrap(),
542
+ }
543
+ });
544
+
545
+ let start = format!("http://{}/start", server.addr());
546
+ let dst = format!("http://{}/dst", server.addr());
547
+
548
+ let client = Client::builder()
549
+ .cookie_store(true)
550
+ .redirect(Policy::default())
551
+ .build()
552
+ .unwrap();
553
+
554
+ let res = client.get(&start).send().await.unwrap();
555
+ assert_eq!(res.uri(), dst.as_str());
556
+ assert_eq!(res.status(), wreq::StatusCode::OK);
557
+ }
558
+
559
+ #[tokio::test]
560
+ async fn test_redirect_async_pending_follow() {
561
+ let server = server::http(move |req| async move {
562
+ if req.uri() == "/async-redirect" {
563
+ http::Response::builder()
564
+ .status(302)
565
+ .header("location", "/dst")
566
+ .body(Body::default())
567
+ .unwrap()
568
+ } else {
569
+ assert_eq!(req.uri(), "/dst");
570
+ http::Response::builder()
571
+ .header("server", "test-dst")
572
+ .body(Body::default())
573
+ .unwrap()
574
+ }
575
+ });
576
+
577
+ let url = format!("http://{}/async-redirect", server.addr());
578
+ let dst = format!("http://{}/dst", server.addr());
579
+
580
+ let client = Client::builder()
581
+ .redirect(Policy::custom(|attempt| {
582
+ attempt.pending(|attempt| async move {
583
+ // Simulate async decision-making
584
+ tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
585
+ attempt.follow()
586
+ })
587
+ }))
588
+ .build()
589
+ .unwrap();
590
+
591
+ let res = client.get(&url).send().await.unwrap();
592
+ assert_eq!(res.uri(), dst.as_str());
593
+ assert_eq!(res.status(), wreq::StatusCode::OK);
594
+ assert_eq!(
595
+ res.headers().get(wreq::header::SERVER).unwrap(),
596
+ &"test-dst"
597
+ );
598
+ }
599
+
600
+ #[tokio::test]
601
+ async fn test_redirect_location_is_encoded() {
602
+ let server = server::http(move |req| async move {
603
+ if req.uri() == "/start" {
604
+ http::Response::builder()
605
+ .status(302)
606
+ .header("location", "/dst path")
607
+ .body(wreq::Body::default())
608
+ .unwrap()
609
+ } else {
610
+ assert_eq!(req.uri().path(), "/dst%20path");
611
+ http::Response::builder()
612
+ .status(StatusCode::OK)
613
+ .body(wreq::Body::default())
614
+ .unwrap()
615
+ }
616
+ });
617
+
618
+ let url = format!("http://{}/start", server.addr());
619
+ let dst = format!("http://{}/dst%20path", server.addr());
620
+
621
+ let client = Client::builder()
622
+ .redirect(Policy::default())
623
+ .build()
624
+ .unwrap();
625
+
626
+ let res = client.get(&url).send().await.unwrap();
627
+ assert_eq!(res.uri(), dst.as_str());
628
+ assert_eq!(res.status(), StatusCode::OK);
629
+ }