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,559 @@
1
+ mod support;
2
+ use support::server;
3
+ use tokio::io::AsyncWriteExt;
4
+ use wreq::Client;
5
+
6
+ #[tokio::test]
7
+ async fn zstd_response() {
8
+ zstd_case(10_000, 4096).await;
9
+ }
10
+
11
+ #[tokio::test]
12
+ async fn zstd_single_byte_chunks() {
13
+ zstd_case(10, 1).await;
14
+ }
15
+
16
+ #[tokio::test]
17
+ async fn test_zstd_empty_body() {
18
+ let server = server::http(move |req| async move {
19
+ assert_eq!(req.method(), "HEAD");
20
+
21
+ http::Response::builder()
22
+ .header("content-encoding", "zstd")
23
+ .body(Default::default())
24
+ .unwrap()
25
+ });
26
+
27
+ let res = wreq::head(format!("http://{}/zstd", server.addr()))
28
+ .send()
29
+ .await
30
+ .unwrap();
31
+
32
+ let body = res.text().await.unwrap();
33
+
34
+ assert_eq!(body, "");
35
+ }
36
+
37
+ #[tokio::test]
38
+ async fn test_accept_header_is_not_changed_if_set() {
39
+ let server = server::http(move |req| async move {
40
+ assert_eq!(req.headers()["accept"], "application/json");
41
+ assert!(
42
+ req.headers()["accept-encoding"]
43
+ .to_str()
44
+ .unwrap()
45
+ .contains("zstd")
46
+ );
47
+ http::Response::default()
48
+ });
49
+
50
+ let res = wreq::get(format!("http://{}/accept", server.addr()))
51
+ .header(
52
+ wreq::header::ACCEPT,
53
+ wreq::header::HeaderValue::from_static("application/json"),
54
+ )
55
+ .send()
56
+ .await
57
+ .unwrap();
58
+
59
+ assert_eq!(res.status(), wreq::StatusCode::OK);
60
+ }
61
+
62
+ #[tokio::test]
63
+ async fn test_accept_encoding_header_is_not_changed_if_set() {
64
+ let server = server::http(move |req| async move {
65
+ assert_eq!(req.headers()["accept"], "*/*");
66
+ assert_eq!(req.headers()["accept-encoding"], "identity");
67
+ http::Response::default()
68
+ });
69
+
70
+ let res = wreq::get(format!("http://{}/accept-encoding", server.addr()))
71
+ .header(wreq::header::ACCEPT, "*/*")
72
+ .header(
73
+ wreq::header::ACCEPT_ENCODING,
74
+ wreq::header::HeaderValue::from_static("identity"),
75
+ )
76
+ .send()
77
+ .await
78
+ .unwrap();
79
+
80
+ assert_eq!(res.status(), wreq::StatusCode::OK);
81
+ }
82
+
83
+ async fn zstd_case(response_size: usize, chunk_size: usize) {
84
+ use futures_util::stream::StreamExt;
85
+
86
+ let content: String = (0..response_size).fold(String::new(), |mut acc, i| {
87
+ acc.push_str(&format!("test {i}"));
88
+ acc
89
+ });
90
+
91
+ let zstded_content = zstd::encode_all(content.as_bytes(), 3).unwrap();
92
+
93
+ let mut response = format!(
94
+ "\
95
+ HTTP/1.1 200 OK\r\n\
96
+ Server: test-accept\r\n\
97
+ Content-Encoding: zstd\r\n\
98
+ Content-Length: {}\r\n\
99
+ \r\n",
100
+ &zstded_content.len()
101
+ )
102
+ .into_bytes();
103
+ response.extend(&zstded_content);
104
+
105
+ let server = server::http(move |req| {
106
+ assert!(
107
+ req.headers()["accept-encoding"]
108
+ .to_str()
109
+ .unwrap()
110
+ .contains("zstd")
111
+ );
112
+
113
+ let zstded = zstded_content.clone();
114
+ async move {
115
+ let len = zstded.len();
116
+ let stream =
117
+ futures_util::stream::unfold((zstded, 0), move |(zstded, pos)| async move {
118
+ let chunk = zstded.chunks(chunk_size).nth(pos)?.to_vec();
119
+
120
+ Some((chunk, (zstded, pos + 1)))
121
+ });
122
+
123
+ let body = wreq::Body::wrap_stream(stream.map(Ok::<_, std::convert::Infallible>));
124
+
125
+ http::Response::builder()
126
+ .header("content-encoding", "zstd")
127
+ .header("content-length", len)
128
+ .body(body)
129
+ .unwrap()
130
+ }
131
+ });
132
+
133
+ let res = wreq::get(format!("http://{}/zstd", server.addr()))
134
+ .send()
135
+ .await
136
+ .expect("response");
137
+
138
+ let body = res.text().await.expect("text");
139
+ assert_eq!(body, content);
140
+ }
141
+
142
+ const COMPRESSED_RESPONSE_HEADERS: &[u8] = b"HTTP/1.1 200 OK\x0d\x0a\
143
+ Content-Type: text/plain\x0d\x0a\
144
+ Connection: keep-alive\x0d\x0a\
145
+ Content-Encoding: zstd\x0d\x0a";
146
+
147
+ const RESPONSE_CONTENT: &str = "some message here";
148
+
149
+ fn zstd_compress(input: &[u8]) -> Vec<u8> {
150
+ zstd::encode_all(input, 3).unwrap()
151
+ }
152
+
153
+ #[tokio::test]
154
+ async fn test_non_chunked_non_fragmented_response() {
155
+ let server = server::low_level_with_response(|_raw_request, client_socket| {
156
+ Box::new(async move {
157
+ let zstded_content = zstd_compress(RESPONSE_CONTENT.as_bytes());
158
+ let content_length_header =
159
+ format!("Content-Length: {}\r\n\r\n", zstded_content.len()).into_bytes();
160
+ let response = [
161
+ COMPRESSED_RESPONSE_HEADERS,
162
+ &content_length_header,
163
+ &zstded_content,
164
+ ]
165
+ .concat();
166
+
167
+ client_socket
168
+ .write_all(response.as_slice())
169
+ .await
170
+ .expect("response write_all failed");
171
+ client_socket.flush().await.expect("response flush failed");
172
+ })
173
+ });
174
+
175
+ let res = wreq::get(format!("http://{}/", server.addr()))
176
+ .send()
177
+ .await
178
+ .expect("response");
179
+
180
+ assert_eq!(res.text().await.expect("text"), RESPONSE_CONTENT);
181
+ }
182
+
183
+ #[tokio::test]
184
+ async fn test_chunked_fragmented_response_1() {
185
+ const DELAY_BETWEEN_RESPONSE_PARTS: tokio::time::Duration =
186
+ tokio::time::Duration::from_millis(1000);
187
+ const DELAY_MARGIN: tokio::time::Duration = tokio::time::Duration::from_millis(50);
188
+
189
+ let server = server::low_level_with_response(|_raw_request, client_socket| {
190
+ Box::new(async move {
191
+ let zstded_content = zstd_compress(RESPONSE_CONTENT.as_bytes());
192
+ let response_first_part = [
193
+ COMPRESSED_RESPONSE_HEADERS,
194
+ format!(
195
+ "Transfer-Encoding: chunked\r\n\r\n{:x}\r\n",
196
+ zstded_content.len()
197
+ )
198
+ .as_bytes(),
199
+ &zstded_content,
200
+ ]
201
+ .concat();
202
+ let response_second_part = b"\r\n0\r\n\r\n";
203
+
204
+ client_socket
205
+ .write_all(response_first_part.as_slice())
206
+ .await
207
+ .expect("response_first_part write_all failed");
208
+ client_socket
209
+ .flush()
210
+ .await
211
+ .expect("response_first_part flush failed");
212
+
213
+ tokio::time::sleep(DELAY_BETWEEN_RESPONSE_PARTS).await;
214
+
215
+ client_socket
216
+ .write_all(response_second_part)
217
+ .await
218
+ .expect("response_second_part write_all failed");
219
+ client_socket
220
+ .flush()
221
+ .await
222
+ .expect("response_second_part flush failed");
223
+ })
224
+ });
225
+
226
+ let start = tokio::time::Instant::now();
227
+ let res = wreq::get(format!("http://{}/", server.addr()))
228
+ .send()
229
+ .await
230
+ .expect("response");
231
+
232
+ assert_eq!(res.text().await.expect("text"), RESPONSE_CONTENT);
233
+ assert!(start.elapsed() >= DELAY_BETWEEN_RESPONSE_PARTS - DELAY_MARGIN);
234
+ }
235
+
236
+ #[tokio::test]
237
+ async fn test_chunked_fragmented_response_2() {
238
+ const DELAY_BETWEEN_RESPONSE_PARTS: tokio::time::Duration =
239
+ tokio::time::Duration::from_millis(1000);
240
+ const DELAY_MARGIN: tokio::time::Duration = tokio::time::Duration::from_millis(50);
241
+
242
+ let server = server::low_level_with_response(|_raw_request, client_socket| {
243
+ Box::new(async move {
244
+ let zstded_content = zstd_compress(RESPONSE_CONTENT.as_bytes());
245
+ let response_first_part = [
246
+ COMPRESSED_RESPONSE_HEADERS,
247
+ format!(
248
+ "Transfer-Encoding: chunked\r\n\r\n{:x}\r\n",
249
+ zstded_content.len()
250
+ )
251
+ .as_bytes(),
252
+ &zstded_content,
253
+ b"\r\n",
254
+ ]
255
+ .concat();
256
+ let response_second_part = b"0\r\n\r\n";
257
+
258
+ client_socket
259
+ .write_all(response_first_part.as_slice())
260
+ .await
261
+ .expect("response_first_part write_all failed");
262
+ client_socket
263
+ .flush()
264
+ .await
265
+ .expect("response_first_part flush failed");
266
+
267
+ tokio::time::sleep(DELAY_BETWEEN_RESPONSE_PARTS).await;
268
+
269
+ client_socket
270
+ .write_all(response_second_part)
271
+ .await
272
+ .expect("response_second_part write_all failed");
273
+ client_socket
274
+ .flush()
275
+ .await
276
+ .expect("response_second_part flush failed");
277
+ })
278
+ });
279
+
280
+ let start = tokio::time::Instant::now();
281
+ let res = wreq::get(format!("http://{}/", server.addr()))
282
+ .send()
283
+ .await
284
+ .expect("response");
285
+
286
+ assert_eq!(res.text().await.expect("text"), RESPONSE_CONTENT);
287
+ assert!(start.elapsed() >= DELAY_BETWEEN_RESPONSE_PARTS - DELAY_MARGIN);
288
+ }
289
+
290
+ #[tokio::test]
291
+ async fn test_chunked_fragmented_response_with_extra_bytes() {
292
+ const DELAY_BETWEEN_RESPONSE_PARTS: tokio::time::Duration =
293
+ tokio::time::Duration::from_millis(1000);
294
+ const DELAY_MARGIN: tokio::time::Duration = tokio::time::Duration::from_millis(50);
295
+
296
+ let server = server::low_level_with_response(|_raw_request, client_socket| {
297
+ Box::new(async move {
298
+ let zstded_content = zstd_compress(RESPONSE_CONTENT.as_bytes());
299
+ let response_first_part = [
300
+ COMPRESSED_RESPONSE_HEADERS,
301
+ format!(
302
+ "Transfer-Encoding: chunked\r\n\r\n{:x}\r\n",
303
+ zstded_content.len()
304
+ )
305
+ .as_bytes(),
306
+ &zstded_content,
307
+ ]
308
+ .concat();
309
+ let response_second_part = b"\r\n2ab\r\n0\r\n\r\n";
310
+
311
+ client_socket
312
+ .write_all(response_first_part.as_slice())
313
+ .await
314
+ .expect("response_first_part write_all failed");
315
+ client_socket
316
+ .flush()
317
+ .await
318
+ .expect("response_first_part flush failed");
319
+
320
+ tokio::time::sleep(DELAY_BETWEEN_RESPONSE_PARTS).await;
321
+
322
+ client_socket
323
+ .write_all(response_second_part)
324
+ .await
325
+ .expect("response_second_part write_all failed");
326
+ client_socket
327
+ .flush()
328
+ .await
329
+ .expect("response_second_part flush failed");
330
+ })
331
+ });
332
+
333
+ let start = tokio::time::Instant::now();
334
+ let res = wreq::get(format!("http://{}/", server.addr()))
335
+ .send()
336
+ .await
337
+ .expect("response");
338
+
339
+ let err = res.text().await.expect_err("there must be an error");
340
+ assert!(err.is_decode());
341
+ assert!(start.elapsed() >= DELAY_BETWEEN_RESPONSE_PARTS - DELAY_MARGIN);
342
+ }
343
+
344
+ // Big response can have multiple ZSTD frames in it
345
+ #[tokio::test]
346
+ async fn test_non_chunked_non_fragmented_multiple_frames_response() {
347
+ let server = server::low_level_with_response(|_raw_request, client_socket| {
348
+ Box::new(async move {
349
+ // Split the content into two parts
350
+ let content_bytes = RESPONSE_CONTENT.as_bytes();
351
+ let mid = content_bytes.len() / 2;
352
+ // Compress each part separately to create multiple ZSTD frames
353
+ let compressed_part1 = zstd::encode_all(&content_bytes[0..mid], 3).unwrap();
354
+ let compressed_part2 = zstd::encode_all(&content_bytes[mid..], 3).unwrap();
355
+ // Concatenate the compressed frames
356
+ let mut zstded_content = compressed_part1;
357
+ zstded_content.extend_from_slice(&compressed_part2);
358
+ // Set Content-Length to the total length of the concatenated frames
359
+ let content_length_header =
360
+ format!("Content-Length: {}\r\n\r\n", zstded_content.len()).into_bytes();
361
+ let response = [
362
+ COMPRESSED_RESPONSE_HEADERS,
363
+ &content_length_header,
364
+ &zstded_content,
365
+ ]
366
+ .concat();
367
+
368
+ client_socket
369
+ .write_all(response.as_slice())
370
+ .await
371
+ .expect("response write_all failed");
372
+ client_socket.flush().await.expect("response flush failed");
373
+ })
374
+ });
375
+
376
+ let res = wreq::get(format!("http://{}/", server.addr()))
377
+ .send()
378
+ .await
379
+ .expect("response");
380
+
381
+ assert_eq!(res.text().await.expect("text"), RESPONSE_CONTENT);
382
+ }
383
+
384
+ #[tokio::test]
385
+ async fn test_chunked_fragmented_multiple_frames_in_one_chunk() {
386
+ // Define constants for delay and timing margin
387
+ const DELAY_BETWEEN_RESPONSE_PARTS: tokio::time::Duration =
388
+ tokio::time::Duration::from_millis(1000); // 1-second delay
389
+ const DELAY_MARGIN: tokio::time::Duration = tokio::time::Duration::from_millis(50); // Margin for timing assertions
390
+
391
+ // Set up a low-level server
392
+ let server = server::low_level_with_response(|_raw_request, client_socket| {
393
+ Box::new(async move {
394
+ // Split RESPONSE_CONTENT into two parts
395
+ let mid = RESPONSE_CONTENT.len() / 2;
396
+ let part1 = &RESPONSE_CONTENT[0..mid];
397
+ let part2 = &RESPONSE_CONTENT[mid..];
398
+
399
+ // Compress each part separately to create two ZSTD frames
400
+ let compressed_part1 = zstd_compress(part1.as_bytes());
401
+ let compressed_part2 = zstd_compress(part2.as_bytes());
402
+
403
+ // Concatenate the frames into a single chunk's data
404
+ let chunk_data = [compressed_part1.as_slice(), compressed_part2.as_slice()].concat();
405
+
406
+ // Calculate the chunk size in bytes
407
+ let chunk_size = chunk_data.len();
408
+
409
+ // Prepare the initial response part: headers + chunk size
410
+ let headers = [
411
+ COMPRESSED_RESPONSE_HEADERS, /* e.g., "HTTP/1.1 200 OK\r\nContent-Encoding:
412
+ * zstd\r\n" */
413
+ b"Transfer-Encoding: chunked\r\n\r\n", // Indicate chunked encoding
414
+ format!("{chunk_size:x}\r\n").as_bytes(), // Chunk size in hex
415
+ ]
416
+ .concat();
417
+
418
+ // Send headers + chunk size + chunk data
419
+ client_socket
420
+ .write_all([headers.as_slice(), &chunk_data].concat().as_slice())
421
+ .await
422
+ .expect("write_all failed");
423
+ client_socket.flush().await.expect("flush failed");
424
+
425
+ // Introduce a delay to simulate fragmentation
426
+ tokio::time::sleep(DELAY_BETWEEN_RESPONSE_PARTS).await;
427
+
428
+ // Send chunk terminator + final chunk
429
+ client_socket
430
+ .write_all(b"\r\n0\r\n\r\n")
431
+ .await
432
+ .expect("write_all failed");
433
+ client_socket.flush().await.expect("flush failed");
434
+ })
435
+ });
436
+
437
+ // Record the start time for delay verification
438
+ let start = tokio::time::Instant::now();
439
+
440
+ let res = wreq::get(format!("http://{}/", server.addr()))
441
+ .send()
442
+ .await
443
+ .expect("Failed to get response");
444
+
445
+ // Verify the decompressed response matches the original content
446
+ assert_eq!(
447
+ res.text().await.expect("Failed to read text"),
448
+ RESPONSE_CONTENT
449
+ );
450
+ assert!(start.elapsed() >= DELAY_BETWEEN_RESPONSE_PARTS - DELAY_MARGIN);
451
+ }
452
+
453
+ #[tokio::test]
454
+ async fn test_connection_reuse_with_chunked_fragmented_multiple_frames_in_one_chunk() {
455
+ // Define constants for delay and timing margin
456
+ const DELAY_BETWEEN_RESPONSE_PARTS: tokio::time::Duration =
457
+ tokio::time::Duration::from_millis(1000); // 1-second delay
458
+ const DELAY_MARGIN: tokio::time::Duration = tokio::time::Duration::from_millis(50); // Margin for timing assertions
459
+
460
+ // We will record the peer addresses of each client request here
461
+ let peer_addrs = std::sync::Arc::new(std::sync::Mutex::new(Vec::<std::net::SocketAddr>::new()));
462
+ let peer_addrs_clone = peer_addrs.clone();
463
+
464
+ // Set up a low-level server (it will reuse existing client connection, executing callback for
465
+ // each client request)
466
+ let server = server::low_level_with_response(move |_raw_request, client_socket| {
467
+ let peer_addrs = peer_addrs_clone.clone();
468
+ Box::new(async move {
469
+ // Split RESPONSE_CONTENT into two parts
470
+ let mid = RESPONSE_CONTENT.len() / 2;
471
+ let part1 = &RESPONSE_CONTENT[0..mid];
472
+ let part2 = &RESPONSE_CONTENT[mid..];
473
+
474
+ // Compress each part separately to create two ZSTD frames
475
+ let compressed_part1 = zstd_compress(part1.as_bytes());
476
+ let compressed_part2 = zstd_compress(part2.as_bytes());
477
+
478
+ // Concatenate the frames into a single chunk's data
479
+ let chunk_data = [compressed_part1.as_slice(), compressed_part2.as_slice()].concat();
480
+
481
+ // Calculate the chunk size in bytes
482
+ let chunk_size = chunk_data.len();
483
+
484
+ // Prepare the initial response part: headers + chunk size
485
+ let headers = [
486
+ COMPRESSED_RESPONSE_HEADERS, /* e.g., "HTTP/1.1 200 OK\r\nContent-Encoding:
487
+ * zstd\r\n" */
488
+ b"Transfer-Encoding: chunked\r\n\r\n", // Indicate chunked encoding
489
+ format!("{chunk_size:x}\r\n").as_bytes(), // Chunk size in hex
490
+ ]
491
+ .concat();
492
+
493
+ // Send headers + chunk size + chunk data
494
+ client_socket
495
+ .write_all([headers.as_slice(), &chunk_data].concat().as_slice())
496
+ .await
497
+ .expect("write_all failed");
498
+ client_socket.flush().await.expect("flush failed");
499
+
500
+ // Introduce a delay to simulate fragmentation
501
+ tokio::time::sleep(DELAY_BETWEEN_RESPONSE_PARTS).await;
502
+
503
+ peer_addrs
504
+ .lock()
505
+ .unwrap()
506
+ .push(client_socket.peer_addr().unwrap());
507
+
508
+ // Send chunk terminator + final chunk
509
+ client_socket
510
+ .write_all(b"\r\n0\r\n\r\n")
511
+ .await
512
+ .expect("write_all failed");
513
+ client_socket.flush().await.expect("flush failed");
514
+ })
515
+ });
516
+
517
+ let client = Client::builder()
518
+ .pool_idle_timeout(std::time::Duration::from_secs(30))
519
+ .pool_max_idle_per_host(1)
520
+ .build()
521
+ .unwrap();
522
+
523
+ const NUMBER_OF_REQUESTS: usize = 5;
524
+
525
+ for _ in 0..NUMBER_OF_REQUESTS {
526
+ // Record the start time for delay verification
527
+ let start = tokio::time::Instant::now();
528
+
529
+ let res = client
530
+ .get(format!("http://{}/", server.addr()))
531
+ .send()
532
+ .await
533
+ .expect("Failed to get response");
534
+
535
+ // Verify the decompressed response matches the original content
536
+ assert_eq!(
537
+ res.text().await.expect("Failed to read text"),
538
+ RESPONSE_CONTENT
539
+ );
540
+ assert!(start.elapsed() >= DELAY_BETWEEN_RESPONSE_PARTS - DELAY_MARGIN);
541
+ }
542
+
543
+ drop(client);
544
+
545
+ // Check that all peer addresses are the same
546
+ let peer_addrs = peer_addrs.lock().unwrap();
547
+ assert_eq!(
548
+ peer_addrs.len(),
549
+ NUMBER_OF_REQUESTS,
550
+ "Expected {} peer addresses, but got {}",
551
+ NUMBER_OF_REQUESTS,
552
+ peer_addrs.len()
553
+ );
554
+ let first_addr = peer_addrs[0];
555
+ assert!(
556
+ peer_addrs.iter().all(|addr| addr == &first_addr),
557
+ "All peer addresses should be the same, but found differences: {peer_addrs:?}"
558
+ );
559
+ }