wreq-rb 0.6.0 → 0.6.2

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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +257 -102
  3. data/ext/wreq_rb/Cargo.toml +7 -4
  4. data/ext/wreq_rb/src/client.rs +105 -15
  5. data/ext/wreq_rb/src/response.rs +28 -3
  6. data/lib/wreq-rb/version.rb +1 -1
  7. data/patches/0001-add-transfer-size-tracking.patch +11 -15
  8. data/vendor/wreq/Cargo.toml +9 -8
  9. data/vendor/wreq/README.md +5 -5
  10. data/vendor/wreq/bench/support/bench.rs +6 -2
  11. data/vendor/wreq/bench/support/client.rs +88 -1
  12. data/vendor/wreq/bench/support/exec.rs +0 -0
  13. data/vendor/wreq/bench/support/rt.rs +34 -0
  14. data/vendor/wreq/bench/support/server.rs +1 -1
  15. data/vendor/wreq/bench/support.rs +1 -15
  16. data/vendor/wreq/examples/cert_store.rs +13 -13
  17. data/vendor/wreq/examples/request_with_emulate.rs +1 -1
  18. data/vendor/wreq/examples/tcp_linger.rs +22 -0
  19. data/vendor/wreq/src/client/layer/client/pool.rs +17 -17
  20. data/vendor/wreq/src/client/layer/client.rs +2 -0
  21. data/vendor/wreq/src/client/layer/decoder.rs +71 -17
  22. data/vendor/wreq/src/client/layer/redirect/future.rs +49 -63
  23. data/vendor/wreq/src/client/layer/redirect/policy.rs +2 -26
  24. data/vendor/wreq/src/client/layer/redirect.rs +48 -60
  25. data/vendor/wreq/src/client/layer/retry.rs +12 -15
  26. data/vendor/wreq/src/client/layer/timeout/body.rs +27 -21
  27. data/vendor/wreq/src/client/layer/timeout/future.rs +33 -58
  28. data/vendor/wreq/src/client/layer/timeout.rs +8 -14
  29. data/vendor/wreq/src/client/request.rs +4 -0
  30. data/vendor/wreq/src/client.rs +53 -31
  31. data/vendor/wreq/src/conn/connector.rs +99 -129
  32. data/vendor/wreq/src/conn/http.rs +25 -18
  33. data/vendor/wreq/src/conn/net/tcp.rs +601 -107
  34. data/vendor/wreq/src/conn/proxy/socks.rs +6 -6
  35. data/vendor/wreq/src/conn/timeout.rs +166 -0
  36. data/vendor/wreq/src/conn.rs +5 -4
  37. data/vendor/wreq/src/cookie/jar.rs +1225 -0
  38. data/vendor/wreq/src/cookie/store.rs +321 -0
  39. data/vendor/wreq/src/cookie.rs +108 -612
  40. data/vendor/wreq/src/dns/resolve.rs +8 -2
  41. data/vendor/wreq/src/dns.rs +4 -4
  42. data/vendor/wreq/src/error.rs +53 -20
  43. data/vendor/wreq/src/lib.rs +1 -0
  44. data/vendor/wreq/src/proxy/matcher.rs +26 -12
  45. data/vendor/wreq/src/proxy/win.rs +39 -9
  46. data/vendor/wreq/src/redirect.rs +515 -100
  47. data/vendor/wreq/src/tls/conn.rs +3 -11
  48. data/vendor/wreq/src/tls/session.rs +7 -8
  49. data/vendor/wreq/src/tls/trust/store.rs +4 -4
  50. data/vendor/wreq/src/util.rs +23 -0
  51. data/vendor/wreq/tests/badssl.rs +72 -7
  52. data/vendor/wreq/tests/brotli.rs +1 -1
  53. data/vendor/wreq/tests/client.rs +24 -0
  54. data/vendor/wreq/tests/connector_layers.rs +8 -4
  55. data/vendor/wreq/tests/cookie.rs +59 -0
  56. data/vendor/wreq/tests/deflate.rs +1 -1
  57. data/vendor/wreq/tests/gzip.rs +53 -1
  58. data/vendor/wreq/tests/layers.rs +8 -4
  59. data/vendor/wreq/tests/redirect.rs +180 -97
  60. data/vendor/wreq/tests/timeouts.rs +47 -12
  61. data/vendor/wreq/tests/zstd.rs +1 -1
  62. metadata +8 -2
@@ -3,7 +3,7 @@ use http::StatusCode;
3
3
  use http_body_util::BodyExt;
4
4
  use support::server;
5
5
  use wreq::{
6
- Body, Client,
6
+ Body, Client, header,
7
7
  redirect::{History, Policy},
8
8
  };
9
9
 
@@ -17,15 +17,15 @@ async fn test_redirect_301_and_302_and_303_changes_post_to_get() {
17
17
  assert_eq!(req.uri(), &*format!("/{code}"));
18
18
  http::Response::builder()
19
19
  .status(code)
20
- .header("location", "/dst")
21
- .header("server", "test-redirect")
20
+ .header(header::LOCATION, "/dst")
21
+ .header(header::SERVER, "test-redirect")
22
22
  .body(Body::default())
23
23
  .unwrap()
24
24
  } else {
25
25
  assert_eq!(req.method(), "GET");
26
26
 
27
27
  http::Response::builder()
28
- .header("server", "test-dst")
28
+ .header(header::SERVER, "test-dst")
29
29
  .body(Body::default())
30
30
  .unwrap()
31
31
  }
@@ -40,10 +40,7 @@ async fn test_redirect_301_and_302_and_303_changes_post_to_get() {
40
40
  .unwrap();
41
41
  assert_eq!(res.uri(), dst.as_str());
42
42
  assert_eq!(res.status(), wreq::StatusCode::OK);
43
- assert_eq!(
44
- res.headers().get(wreq::header::SERVER).unwrap(),
45
- &"test-dst"
46
- );
43
+ assert_eq!(res.headers().get(header::SERVER).unwrap(), &"test-dst");
47
44
  }
48
45
  }
49
46
 
@@ -56,15 +53,15 @@ async fn test_redirect_307_and_308_tries_to_get_again() {
56
53
  if req.uri() == &*format!("/{code}") {
57
54
  http::Response::builder()
58
55
  .status(code)
59
- .header("location", "/dst")
60
- .header("server", "test-redirect")
56
+ .header(header::LOCATION, "/dst")
57
+ .header(header::SERVER, "test-redirect")
61
58
  .body(Body::default())
62
59
  .unwrap()
63
60
  } else {
64
61
  assert_eq!(req.uri(), "/dst");
65
62
 
66
63
  http::Response::builder()
67
- .header("server", "test-dst")
64
+ .header(header::SERVER, "test-dst")
68
65
  .body(Body::default())
69
66
  .unwrap()
70
67
  }
@@ -79,10 +76,7 @@ async fn test_redirect_307_and_308_tries_to_get_again() {
79
76
  .unwrap();
80
77
  assert_eq!(res.uri(), dst.as_str());
81
78
  assert_eq!(res.status(), wreq::StatusCode::OK);
82
- assert_eq!(
83
- res.headers().get(wreq::header::SERVER).unwrap(),
84
- &"test-dst"
85
- );
79
+ assert_eq!(res.headers().get(header::SERVER).unwrap(), &"test-dst");
86
80
  }
87
81
  }
88
82
 
@@ -94,7 +88,7 @@ async fn test_redirect_307_and_308_tries_to_post_again() {
94
88
  for &code in &codes {
95
89
  let redirect = server::http(move |mut req| async move {
96
90
  assert_eq!(req.method(), "POST");
97
- assert_eq!(req.headers()["content-length"], "5");
91
+ assert_eq!(req.headers()[header::CONTENT_LENGTH], "5");
98
92
 
99
93
  let data = req
100
94
  .body_mut()
@@ -109,15 +103,15 @@ async fn test_redirect_307_and_308_tries_to_post_again() {
109
103
  if req.uri() == &*format!("/{code}") {
110
104
  http::Response::builder()
111
105
  .status(code)
112
- .header("location", "/dst")
113
- .header("server", "test-redirect")
106
+ .header(header::LOCATION, "/dst")
107
+ .header(header::SERVER, "test-redirect")
114
108
  .body(Body::default())
115
109
  .unwrap()
116
110
  } else {
117
111
  assert_eq!(req.uri(), "/dst");
118
112
 
119
113
  http::Response::builder()
120
- .header("server", "test-dst")
114
+ .header(header::SERVER, "test-dst")
121
115
  .body(Body::default())
122
116
  .unwrap()
123
117
  }
@@ -133,59 +127,147 @@ async fn test_redirect_307_and_308_tries_to_post_again() {
133
127
  .unwrap();
134
128
  assert_eq!(res.uri(), dst.as_str());
135
129
  assert_eq!(res.status(), wreq::StatusCode::OK);
136
- assert_eq!(
137
- res.headers().get(wreq::header::SERVER).unwrap(),
138
- &"test-dst"
139
- );
130
+ assert_eq!(res.headers().get(header::SERVER).unwrap(), &"test-dst");
140
131
  }
141
132
  }
142
133
 
143
134
  #[tokio::test]
144
135
  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()
136
+ let end_server = server::http(move |req| async move {
137
+ match req.uri().path() {
138
+ "/middle" => {
139
+ assert_eq!(req.headers().get(header::COOKIE), None);
140
+ http::Response::builder()
141
+ .status(302)
142
+ .header(header::LOCATION, "/end")
143
+ .body(Body::default())
144
+ .unwrap()
145
+ }
146
+ "/end" => {
147
+ assert_eq!(req.headers().get(header::COOKIE), None);
148
+ http::Response::default()
149
+ }
150
+ path => panic!("unexpected redirect path: {path}"),
161
151
  }
162
152
  });
163
153
 
164
154
  let end_addr = end_server.addr();
165
155
 
166
- let mid_server = server::http(move |req| async move {
167
- assert_eq!(req.headers()["cookie"], "foo=bar");
156
+ let start_server = server::http(move |req| async move {
157
+ assert_eq!(req.headers()[header::COOKIE], "foo=bar");
168
158
  http::Response::builder()
169
159
  .status(302)
170
- .header("location", format!("http://{end_addr}/end"))
160
+ .header(header::LOCATION, format!("http://{end_addr}/middle"))
171
161
  .body(Body::default())
172
162
  .unwrap()
173
163
  });
174
164
 
175
- tx.send(Some(mid_server.addr())).unwrap();
176
-
177
- Client::builder()
165
+ let response = Client::builder()
178
166
  .redirect(Policy::default())
179
167
  .build()
180
168
  .unwrap()
181
- .get(format!("http://{}/sensitive", mid_server.addr()))
182
- .header(
183
- wreq::header::COOKIE,
184
- wreq::header::HeaderValue::from_static("foo=bar"),
185
- )
169
+ .get(format!("http://{}/sensitive", start_server.addr()))
170
+ .header(header::COOKIE, header::HeaderValue::from_static("foo=bar"))
186
171
  .send()
187
172
  .await
188
173
  .unwrap();
174
+
175
+ assert_eq!(response.uri().path(), "/end");
176
+ }
177
+
178
+ #[tokio::test]
179
+ async fn test_redirect_referrer_policy() {
180
+ let server = server::http(move |req| async move {
181
+ match req.uri().path() {
182
+ "/direct-start" => http::Response::builder()
183
+ .status(302)
184
+ .header(header::LOCATION, "/direct-end")
185
+ .body(Body::default())
186
+ .unwrap(),
187
+ "/direct-end" => {
188
+ assert_eq!(req.headers().get(header::REFERER), None);
189
+ http::Response::default()
190
+ }
191
+ "/no-referrer-start" => http::Response::builder()
192
+ .status(302)
193
+ .header(header::LOCATION, "/no-referrer-end")
194
+ .header(header::REFERRER_POLICY, "no-referrer")
195
+ .body(Body::default())
196
+ .unwrap(),
197
+ "/no-referrer-end" => {
198
+ assert_eq!(req.headers().get(header::REFERER), None);
199
+ http::Response::default()
200
+ }
201
+ "/origin-start" => http::Response::builder()
202
+ .status(302)
203
+ .header(header::LOCATION, "/origin-end")
204
+ .header(
205
+ header::REFERRER_POLICY,
206
+ "unknown-policy, origin-when-cross-origin",
207
+ )
208
+ .body(Body::default())
209
+ .unwrap(),
210
+ "/origin-end" => {
211
+ assert_eq!(req.headers()[header::REFERER], "http://source.example/");
212
+ http::Response::default()
213
+ }
214
+ "/clear-start" => http::Response::builder()
215
+ .status(302)
216
+ .header(header::LOCATION, "/clear-middle")
217
+ .header(header::REFERRER_POLICY, "no-referrer")
218
+ .body(Body::default())
219
+ .unwrap(),
220
+ "/clear-middle" => {
221
+ assert_eq!(req.headers().get(header::REFERER), None);
222
+ http::Response::builder()
223
+ .status(302)
224
+ .header(header::LOCATION, "/clear-end")
225
+ .header(header::REFERRER_POLICY, "unsafe-url")
226
+ .body(Body::default())
227
+ .unwrap()
228
+ }
229
+ "/clear-end" => {
230
+ assert_eq!(req.headers().get(header::REFERER), None);
231
+ http::Response::default()
232
+ }
233
+ path => panic!("unexpected redirect path: {path}"),
234
+ }
235
+ });
236
+
237
+ let client = Client::builder()
238
+ .redirect(Policy::default())
239
+ .build()
240
+ .unwrap();
241
+ let base = format!("http://{}", server.addr());
242
+
243
+ let cases = [
244
+ ("/direct-start", "/direct-end", None),
245
+ (
246
+ "/no-referrer-start",
247
+ "/no-referrer-end",
248
+ Some("https://source.example/private?q=1"),
249
+ ),
250
+ (
251
+ "/origin-start",
252
+ "/origin-end",
253
+ Some("http://source.example/private?q=1"),
254
+ ),
255
+ (
256
+ "/clear-start",
257
+ "/clear-end",
258
+ Some("http://source.example/private"),
259
+ ),
260
+ ];
261
+
262
+ for (start, end, referer) in cases {
263
+ let mut request = client.get(format!("{base}{start}"));
264
+ if let Some(referer) = referer {
265
+ request = request.header(header::REFERER, referer);
266
+ }
267
+
268
+ let response = request.send().await.unwrap();
269
+ assert_eq!(response.uri().path(), end, "case: {start}");
270
+ }
189
271
  }
190
272
 
191
273
  #[tokio::test]
@@ -194,7 +276,7 @@ async fn test_redirect_policy_can_return_errors() {
194
276
  assert_eq!(req.uri(), "/loop");
195
277
  http::Response::builder()
196
278
  .status(302)
197
- .header("location", "/loop")
279
+ .header(header::LOCATION, "/loop")
198
280
  .body(Body::default())
199
281
  .unwrap()
200
282
  });
@@ -214,7 +296,7 @@ async fn test_redirect_policy_can_stop_redirects_without_an_error() {
214
296
  assert_eq!(req.uri(), "/no-redirect");
215
297
  http::Response::builder()
216
298
  .status(302)
217
- .header("location", "/dont")
299
+ .header(header::LOCATION, "/dont")
218
300
  .body(Body::default())
219
301
  .unwrap()
220
302
  });
@@ -235,30 +317,40 @@ async fn test_redirect_policy_can_stop_redirects_without_an_error() {
235
317
  }
236
318
 
237
319
  #[tokio::test]
238
- async fn test_referer_is_not_set_if_disabled() {
320
+ async fn test_referer_is_preserved_if_disabled() {
239
321
  let server = server::http(move |req| async move {
240
322
  if req.uri() == "/no-refer" {
241
323
  http::Response::builder()
242
324
  .status(302)
243
- .header("location", "/dst")
325
+ .header(header::LOCATION, "/dst")
244
326
  .body(Body::default())
245
327
  .unwrap()
246
328
  } else {
247
329
  assert_eq!(req.uri(), "/dst");
248
- assert_eq!(req.headers().get("referer"), None);
330
+ assert_eq!(
331
+ req.headers()[header::REFERER],
332
+ "https://source.example/private?q=1"
333
+ );
249
334
 
250
335
  http::Response::default()
251
336
  }
252
337
  });
253
338
 
254
- Client::builder()
339
+ let response = Client::builder()
340
+ .redirect(Policy::default())
255
341
  .referer(false)
256
342
  .build()
257
343
  .unwrap()
258
344
  .get(format!("http://{}/no-refer", server.addr()))
345
+ .header(
346
+ header::REFERER,
347
+ header::HeaderValue::from_static("https://source.example/private?q=1"),
348
+ )
259
349
  .send()
260
350
  .await
261
351
  .unwrap();
352
+
353
+ assert_eq!(response.uri().path(), "/dst");
262
354
  }
263
355
 
264
356
  #[tokio::test]
@@ -266,7 +358,7 @@ async fn test_invalid_location_stops_redirect_gh484() {
266
358
  let server = server::http(move |_req| async move {
267
359
  http::Response::builder()
268
360
  .status(302)
269
- .header("location", "http://www.yikes{KABOOM}")
361
+ .header(header::LOCATION, "http://www.yikes{KABOOM}")
270
362
  .body(Body::default())
271
363
  .unwrap()
272
364
  });
@@ -284,7 +376,7 @@ async fn test_invalid_scheme_is_rejected() {
284
376
  let server = server::http(move |_req| async move {
285
377
  http::Response::builder()
286
378
  .status(302)
287
- .header("location", "htt://www.yikes.com/")
379
+ .header(header::LOCATION, "htt://www.yikes.com/")
288
380
  .body(Body::default())
289
381
  .unwrap()
290
382
  });
@@ -307,13 +399,13 @@ async fn test_redirect_302_with_set_cookies() {
307
399
  if req.uri() == "/302" {
308
400
  http::Response::builder()
309
401
  .status(302)
310
- .header("location", "/dst")
311
- .header("set-cookie", "key=value")
402
+ .header(header::LOCATION, "/dst")
403
+ .header(header::SET_COOKIE, "key=value")
312
404
  .body(Body::default())
313
405
  .unwrap()
314
406
  } else {
315
407
  assert_eq!(req.uri(), "/dst");
316
- assert_eq!(req.headers()["cookie"], "key=value");
408
+ assert_eq!(req.headers()[header::COOKIE], "key=value");
317
409
  http::Response::default()
318
410
  }
319
411
  });
@@ -346,7 +438,7 @@ async fn test_redirect_limit_to_1() {
346
438
  assert!(req.uri().path().ends_with(&format!("/redirect/{i}")));
347
439
  http::Response::builder()
348
440
  .status(302)
349
- .header("location", format!("/redirect/{}", i + 1))
441
+ .header(header::LOCATION, format!("/redirect/{}", i + 1))
350
442
  .body(Body::default())
351
443
  .unwrap()
352
444
  });
@@ -371,7 +463,7 @@ async fn test_scheme_only_check_after_policy_return_follow() {
371
463
  let server = server::http(move |_| async move {
372
464
  http::Response::builder()
373
465
  .status(302)
374
- .header("location", "htt://www.yikes.com/")
466
+ .header(header::LOCATION, "htt://www.yikes.com/")
375
467
  .body(Body::default())
376
468
  .unwrap()
377
469
  });
@@ -416,24 +508,24 @@ async fn test_redirect_301_302_303_empty_payload_headers() {
416
508
  .unwrap();
417
509
 
418
510
  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");
511
+ if req.headers().get(header::CONTENT_LENGTH).is_some() {
512
+ assert_eq!(req.headers()[header::CONTENT_LENGTH], "5");
421
513
  }
422
514
  assert_eq!(req.uri(), &*format!("/{code}"));
423
515
 
424
516
  http::Response::builder()
425
- .header("location", "/dst")
426
- .header("server", "test-dst")
517
+ .header(header::LOCATION, "/dst")
518
+ .header(header::SERVER, "test-dst")
427
519
  .status(code)
428
520
  .body(Body::default())
429
521
  .unwrap()
430
522
  } else {
431
523
  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());
524
+ assert!(req.headers().get(header::CONTENT_TYPE).is_none());
525
+ assert!(req.headers().get(header::CONTENT_LENGTH).is_none());
526
+ assert!(req.headers().get(header::CONTENT_ENCODING).is_none());
435
527
  http::Response::builder()
436
- .header("server", "test-dst")
528
+ .header(header::SERVER, "test-dst")
437
529
  .body(Body::default())
438
530
  .unwrap()
439
531
  }
@@ -444,18 +536,15 @@ async fn test_redirect_301_302_303_empty_payload_headers() {
444
536
  let res = wreq::post(&url)
445
537
  .redirect(Policy::default())
446
538
  .body("Hello")
447
- .header(wreq::header::CONTENT_TYPE, "text/plain")
448
- .header(wreq::header::CONTENT_LENGTH, "5")
449
- .header(wreq::header::CONTENT_ENCODING, "identity")
539
+ .header(header::CONTENT_TYPE, "text/plain")
540
+ .header(header::CONTENT_LENGTH, "5")
541
+ .header(header::CONTENT_ENCODING, "identity")
450
542
  .send()
451
543
  .await
452
544
  .unwrap();
453
545
  assert_eq!(res.uri(), dst.as_str());
454
546
  assert_eq!(res.status(), 200);
455
- assert_eq!(
456
- res.headers().get(wreq::header::SERVER).unwrap(),
457
- &"test-dst"
458
- );
547
+ assert_eq!(res.headers().get(header::SERVER).unwrap(), &"test-dst");
459
548
  }
460
549
  }
461
550
 
@@ -465,20 +554,20 @@ async fn test_redirect_history() {
465
554
  if req.uri() == "/first" {
466
555
  http::Response::builder()
467
556
  .status(302)
468
- .header("location", "/second")
557
+ .header(header::LOCATION, "/second")
469
558
  .body(Body::default())
470
559
  .unwrap()
471
560
  } else if req.uri() == "/second" {
472
561
  http::Response::builder()
473
562
  .status(302)
474
- .header("location", "/dst")
563
+ .header(header::LOCATION, "/dst")
475
564
  .body(Body::default())
476
565
  .unwrap()
477
566
  } else {
478
567
  assert_eq!(req.uri(), "/dst");
479
568
 
480
569
  http::Response::builder()
481
- .header("server", "test-dst")
570
+ .header(header::SERVER, "test-dst")
482
571
  .body(Body::default())
483
572
  .unwrap()
484
573
  }
@@ -495,10 +584,7 @@ async fn test_redirect_history() {
495
584
  let res = client.get(&url).send().await.unwrap();
496
585
  assert_eq!(res.uri(), dst.as_str());
497
586
  assert_eq!(res.status(), wreq::StatusCode::OK);
498
- assert_eq!(
499
- res.headers().get(wreq::header::SERVER).unwrap(),
500
- &"test-dst"
501
- );
587
+ assert_eq!(res.headers().get(header::SERVER).unwrap(), &"test-dst");
502
588
 
503
589
  let mut history = res.extensions().get::<History>().unwrap().into_iter();
504
590
 
@@ -506,13 +592,13 @@ async fn test_redirect_history() {
506
592
  assert_eq!(next1.status, 302);
507
593
  assert_eq!(next1.previous.path(), "/first");
508
594
  assert_eq!(next1.uri.path(), "/second");
509
- assert_eq!(next1.headers["location"], "/second");
595
+ assert_eq!(next1.headers[header::LOCATION], "/second");
510
596
 
511
597
  let next2 = history.next().unwrap();
512
598
  assert_eq!(next2.status, 302);
513
599
  assert_eq!(next2.previous.path(), "/second");
514
600
  assert_eq!(next2.uri.path(), "/dst");
515
- assert_eq!(next2.headers["location"], "/dst");
601
+ assert_eq!(next2.headers[header::LOCATION], "/dst");
516
602
 
517
603
  assert!(history.next().is_none());
518
604
  }
@@ -524,12 +610,12 @@ async fn test_redirect_applies_set_cookie_from_redirect() {
524
610
  match req.uri().path() {
525
611
  "/start" => http::Response::builder()
526
612
  .status(302)
527
- .header("location", "/dst")
528
- .header("set-cookie", "session=abc; Path=/")
613
+ .header(header::LOCATION, "/dst")
614
+ .header(header::SET_COOKIE, "session=abc; Path=/")
529
615
  .body(Body::default())
530
616
  .unwrap(),
531
617
  "/dst" => {
532
- assert_eq!(req.headers()["cookie"], "session=abc");
618
+ assert_eq!(req.headers()[header::COOKIE], "session=abc");
533
619
  http::Response::builder()
534
620
  .status(200)
535
621
  .body(Body::default())
@@ -562,13 +648,13 @@ async fn test_redirect_async_pending_follow() {
562
648
  if req.uri() == "/async-redirect" {
563
649
  http::Response::builder()
564
650
  .status(302)
565
- .header("location", "/dst")
651
+ .header(header::LOCATION, "/dst")
566
652
  .body(Body::default())
567
653
  .unwrap()
568
654
  } else {
569
655
  assert_eq!(req.uri(), "/dst");
570
656
  http::Response::builder()
571
- .header("server", "test-dst")
657
+ .header(header::SERVER, "test-dst")
572
658
  .body(Body::default())
573
659
  .unwrap()
574
660
  }
@@ -591,10 +677,7 @@ async fn test_redirect_async_pending_follow() {
591
677
  let res = client.get(&url).send().await.unwrap();
592
678
  assert_eq!(res.uri(), dst.as_str());
593
679
  assert_eq!(res.status(), wreq::StatusCode::OK);
594
- assert_eq!(
595
- res.headers().get(wreq::header::SERVER).unwrap(),
596
- &"test-dst"
597
- );
680
+ assert_eq!(res.headers().get(header::SERVER).unwrap(), &"test-dst");
598
681
  }
599
682
 
600
683
  #[tokio::test]
@@ -603,7 +686,7 @@ async fn test_redirect_location_is_encoded() {
603
686
  if req.uri() == "/start" {
604
687
  http::Response::builder()
605
688
  .status(302)
606
- .header("location", "/dst path")
689
+ .header(header::LOCATION, "/dst path")
607
690
  .body(wreq::Body::default())
608
691
  .unwrap()
609
692
  } else {
@@ -1,8 +1,11 @@
1
1
  mod support;
2
- use std::time::Duration;
2
+ use std::{
3
+ net::{Ipv4Addr, SocketAddr},
4
+ time::Duration,
5
+ };
3
6
 
4
7
  use pretty_env_logger::env_logger;
5
- use support::server;
8
+ use support::{layer::DelayLayer, server};
6
9
  use wreq::Client;
7
10
 
8
11
  #[tokio::test]
@@ -61,16 +64,19 @@ async fn request_timeout() {
61
64
  async fn connect_timeout() {
62
65
  let _ = env_logger::try_init();
63
66
 
67
+ let server = server::http(move |_req| async { http::Response::default() });
68
+
64
69
  let client = Client::builder()
70
+ .connector_layer(DelayLayer::new(Duration::from_secs(60)))
65
71
  .connect_timeout(Duration::from_millis(100))
66
72
  .no_proxy()
67
73
  .build()
68
74
  .unwrap();
69
75
 
70
- let url = "http://192.0.2.1:81/slow";
76
+ let url = format!("http://{}", server.addr());
71
77
 
72
78
  let err = client
73
- .get(url)
79
+ .get(&url)
74
80
  .timeout(Duration::from_millis(1000))
75
81
  .send()
76
82
  .await
@@ -84,13 +90,13 @@ async fn connect_many_timeout_succeeds() {
84
90
  let _ = env_logger::try_init();
85
91
 
86
92
  let server = server::http(move |_req| async { http::Response::default() });
93
+ let unavailable = std::net::TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).unwrap();
94
+ let unavailable_addr = unavailable.local_addr().unwrap();
95
+ drop(unavailable);
87
96
  let port = server.addr().port();
88
97
 
89
98
  let client = Client::builder()
90
- .resolve_to_addrs(
91
- "many_addrs",
92
- ["127.0.0.1:81".parse().unwrap(), server.addr()],
93
- )
99
+ .resolve_to_addrs("many_addrs", [unavailable_addr, server.addr()])
94
100
  .connect_timeout(Duration::from_millis(100))
95
101
  .no_proxy()
96
102
  .build()
@@ -114,10 +120,11 @@ async fn connect_many_timeout() {
114
120
  .resolve_to_addrs(
115
121
  "many_addrs",
116
122
  [
117
- "192.0.2.1:81".parse().unwrap(),
118
- "192.0.2.2:81".parse().unwrap(),
123
+ SocketAddr::from((Ipv4Addr::LOCALHOST, 81)),
124
+ SocketAddr::from((Ipv4Addr::LOCALHOST, 82)),
119
125
  ],
120
126
  )
127
+ .connector_layer(DelayLayer::new(Duration::from_secs(60)))
121
128
  .connect_timeout(Duration::from_millis(100))
122
129
  .no_proxy()
123
130
  .build()
@@ -162,7 +169,35 @@ async fn response_timeout() {
162
169
  let res = client.get(&url).send().await.expect("Failed to get");
163
170
  let err = res.text().await.unwrap_err();
164
171
 
165
- assert!(err.is_timeout());
172
+ assert!(err.is_body() && err.is_timeout());
173
+ }
174
+
175
+ #[cfg(feature = "stream")]
176
+ #[tokio::test]
177
+ async fn total_timeout_includes_response_head_and_body() {
178
+ let _ = env_logger::try_init();
179
+
180
+ // Each delay is shorter than the timeout, but together they exceed it.
181
+ let server = server::http(move |_req| async {
182
+ tokio::time::sleep(Duration::from_millis(150)).await;
183
+ let body = wreq::Body::wrap_stream(futures_util::stream::once(async {
184
+ tokio::time::sleep(Duration::from_millis(150)).await;
185
+ Ok::<_, std::convert::Infallible>("Hello")
186
+ }));
187
+ http::Response::new(body)
188
+ });
189
+
190
+ let client = Client::builder()
191
+ .timeout(Duration::from_millis(220))
192
+ .no_proxy()
193
+ .build()
194
+ .unwrap();
195
+
196
+ let url = format!("http://{}/slow", server.addr());
197
+ let res = client.get(&url).send().await.expect("Failed to get");
198
+ let err = res.text().await.unwrap_err();
199
+
200
+ assert!(err.is_body() && err.is_timeout());
166
201
  }
167
202
 
168
203
  #[tokio::test]
@@ -218,7 +253,7 @@ async fn read_timeout_applies_to_body() {
218
253
  let res = client.get(&url).send().await.expect("Failed to get");
219
254
  let err = res.text().await.unwrap_err();
220
255
 
221
- assert!(err.is_timeout());
256
+ assert!(err.is_body() && err.is_timeout());
222
257
  }
223
258
 
224
259
  #[cfg(feature = "stream")]
@@ -97,7 +97,7 @@ async fn zstd_case(response_size: usize, chunk_size: usize) {
97
97
  Content-Encoding: zstd\r\n\
98
98
  Content-Length: {}\r\n\
99
99
  \r\n",
100
- &zstded_content.len()
100
+ zstded_content.len()
101
101
  )
102
102
  .into_bytes();
103
103
  response.extend(&zstded_content);