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.
- checksums.yaml +7 -0
- data/Cargo.lock +2688 -0
- data/Cargo.toml +6 -0
- data/README.md +179 -0
- data/ext/wreq_rb/Cargo.toml +39 -0
- data/ext/wreq_rb/extconf.rb +22 -0
- data/ext/wreq_rb/src/client.rs +565 -0
- data/ext/wreq_rb/src/error.rs +25 -0
- data/ext/wreq_rb/src/lib.rs +20 -0
- data/ext/wreq_rb/src/response.rs +132 -0
- data/lib/wreq-rb/version.rb +5 -0
- data/lib/wreq-rb.rb +17 -0
- data/patches/0001-add-transfer-size-tracking.patch +292 -0
- data/vendor/wreq/Cargo.toml +306 -0
- data/vendor/wreq/LICENSE +202 -0
- data/vendor/wreq/README.md +122 -0
- data/vendor/wreq/examples/cert_store.rs +77 -0
- data/vendor/wreq/examples/connect_via_lower_priority_tokio_runtime.rs +258 -0
- data/vendor/wreq/examples/emulation.rs +118 -0
- data/vendor/wreq/examples/form.rs +14 -0
- data/vendor/wreq/examples/http1_websocket.rs +37 -0
- data/vendor/wreq/examples/http2_websocket.rs +45 -0
- data/vendor/wreq/examples/json_dynamic.rs +41 -0
- data/vendor/wreq/examples/json_typed.rs +47 -0
- data/vendor/wreq/examples/keylog.rs +16 -0
- data/vendor/wreq/examples/request_with_emulation.rs +115 -0
- data/vendor/wreq/examples/request_with_interface.rs +37 -0
- data/vendor/wreq/examples/request_with_local_address.rs +16 -0
- data/vendor/wreq/examples/request_with_proxy.rs +13 -0
- data/vendor/wreq/examples/request_with_redirect.rs +22 -0
- data/vendor/wreq/examples/request_with_version.rs +15 -0
- data/vendor/wreq/examples/tor_socks.rs +24 -0
- data/vendor/wreq/examples/unix_socket.rs +33 -0
- data/vendor/wreq/src/client/body.rs +304 -0
- data/vendor/wreq/src/client/conn/conn.rs +231 -0
- data/vendor/wreq/src/client/conn/connector.rs +549 -0
- data/vendor/wreq/src/client/conn/http.rs +1023 -0
- data/vendor/wreq/src/client/conn/proxy/socks.rs +233 -0
- data/vendor/wreq/src/client/conn/proxy/tunnel.rs +260 -0
- data/vendor/wreq/src/client/conn/proxy.rs +39 -0
- data/vendor/wreq/src/client/conn/tls_info.rs +98 -0
- data/vendor/wreq/src/client/conn/uds.rs +44 -0
- data/vendor/wreq/src/client/conn/verbose.rs +149 -0
- data/vendor/wreq/src/client/conn.rs +323 -0
- data/vendor/wreq/src/client/core/body/incoming.rs +485 -0
- data/vendor/wreq/src/client/core/body/length.rs +118 -0
- data/vendor/wreq/src/client/core/body.rs +34 -0
- data/vendor/wreq/src/client/core/common/buf.rs +149 -0
- data/vendor/wreq/src/client/core/common/rewind.rs +141 -0
- data/vendor/wreq/src/client/core/common/watch.rs +76 -0
- data/vendor/wreq/src/client/core/common.rs +3 -0
- data/vendor/wreq/src/client/core/conn/http1.rs +342 -0
- data/vendor/wreq/src/client/core/conn/http2.rs +307 -0
- data/vendor/wreq/src/client/core/conn.rs +11 -0
- data/vendor/wreq/src/client/core/dispatch.rs +299 -0
- data/vendor/wreq/src/client/core/error.rs +435 -0
- data/vendor/wreq/src/client/core/ext.rs +201 -0
- data/vendor/wreq/src/client/core/http1.rs +178 -0
- data/vendor/wreq/src/client/core/http2.rs +483 -0
- data/vendor/wreq/src/client/core/proto/h1/conn.rs +988 -0
- data/vendor/wreq/src/client/core/proto/h1/decode.rs +1170 -0
- data/vendor/wreq/src/client/core/proto/h1/dispatch.rs +684 -0
- data/vendor/wreq/src/client/core/proto/h1/encode.rs +580 -0
- data/vendor/wreq/src/client/core/proto/h1/io.rs +879 -0
- data/vendor/wreq/src/client/core/proto/h1/role.rs +694 -0
- data/vendor/wreq/src/client/core/proto/h1.rs +104 -0
- data/vendor/wreq/src/client/core/proto/h2/client.rs +650 -0
- data/vendor/wreq/src/client/core/proto/h2/ping.rs +539 -0
- data/vendor/wreq/src/client/core/proto/h2.rs +379 -0
- data/vendor/wreq/src/client/core/proto/headers.rs +138 -0
- data/vendor/wreq/src/client/core/proto.rs +58 -0
- data/vendor/wreq/src/client/core/rt/bounds.rs +57 -0
- data/vendor/wreq/src/client/core/rt/timer.rs +150 -0
- data/vendor/wreq/src/client/core/rt/tokio.rs +99 -0
- data/vendor/wreq/src/client/core/rt.rs +25 -0
- data/vendor/wreq/src/client/core/upgrade.rs +267 -0
- data/vendor/wreq/src/client/core.rs +16 -0
- data/vendor/wreq/src/client/emulation.rs +161 -0
- data/vendor/wreq/src/client/http/client/error.rs +142 -0
- data/vendor/wreq/src/client/http/client/exec.rs +29 -0
- data/vendor/wreq/src/client/http/client/extra.rs +77 -0
- data/vendor/wreq/src/client/http/client/lazy.rs +79 -0
- data/vendor/wreq/src/client/http/client/pool.rs +1105 -0
- data/vendor/wreq/src/client/http/client/util.rs +104 -0
- data/vendor/wreq/src/client/http/client.rs +1003 -0
- data/vendor/wreq/src/client/http/future.rs +99 -0
- data/vendor/wreq/src/client/http.rs +1629 -0
- data/vendor/wreq/src/client/layer/config/options.rs +156 -0
- data/vendor/wreq/src/client/layer/config.rs +116 -0
- data/vendor/wreq/src/client/layer/cookie.rs +161 -0
- data/vendor/wreq/src/client/layer/decoder.rs +139 -0
- data/vendor/wreq/src/client/layer/redirect/future.rs +270 -0
- data/vendor/wreq/src/client/layer/redirect/policy.rs +63 -0
- data/vendor/wreq/src/client/layer/redirect.rs +145 -0
- data/vendor/wreq/src/client/layer/retry/classify.rs +105 -0
- data/vendor/wreq/src/client/layer/retry/scope.rs +51 -0
- data/vendor/wreq/src/client/layer/retry.rs +151 -0
- data/vendor/wreq/src/client/layer/timeout/body.rs +233 -0
- data/vendor/wreq/src/client/layer/timeout/future.rs +90 -0
- data/vendor/wreq/src/client/layer/timeout.rs +177 -0
- data/vendor/wreq/src/client/layer.rs +15 -0
- data/vendor/wreq/src/client/multipart.rs +717 -0
- data/vendor/wreq/src/client/request.rs +818 -0
- data/vendor/wreq/src/client/response.rs +534 -0
- data/vendor/wreq/src/client/ws/json.rs +99 -0
- data/vendor/wreq/src/client/ws/message.rs +453 -0
- data/vendor/wreq/src/client/ws.rs +714 -0
- data/vendor/wreq/src/client.rs +27 -0
- data/vendor/wreq/src/config.rs +140 -0
- data/vendor/wreq/src/cookie.rs +579 -0
- data/vendor/wreq/src/dns/gai.rs +249 -0
- data/vendor/wreq/src/dns/hickory.rs +78 -0
- data/vendor/wreq/src/dns/resolve.rs +180 -0
- data/vendor/wreq/src/dns.rs +69 -0
- data/vendor/wreq/src/error.rs +502 -0
- data/vendor/wreq/src/ext.rs +398 -0
- data/vendor/wreq/src/hash.rs +143 -0
- data/vendor/wreq/src/header.rs +506 -0
- data/vendor/wreq/src/into_uri.rs +187 -0
- data/vendor/wreq/src/lib.rs +586 -0
- data/vendor/wreq/src/proxy/mac.rs +82 -0
- data/vendor/wreq/src/proxy/matcher.rs +806 -0
- data/vendor/wreq/src/proxy/uds.rs +66 -0
- data/vendor/wreq/src/proxy/win.rs +31 -0
- data/vendor/wreq/src/proxy.rs +569 -0
- data/vendor/wreq/src/redirect.rs +575 -0
- data/vendor/wreq/src/retry.rs +198 -0
- data/vendor/wreq/src/sync.rs +129 -0
- data/vendor/wreq/src/tls/conn/cache.rs +123 -0
- data/vendor/wreq/src/tls/conn/cert_compression.rs +125 -0
- data/vendor/wreq/src/tls/conn/ext.rs +82 -0
- data/vendor/wreq/src/tls/conn/macros.rs +34 -0
- data/vendor/wreq/src/tls/conn/service.rs +138 -0
- data/vendor/wreq/src/tls/conn.rs +681 -0
- data/vendor/wreq/src/tls/keylog/handle.rs +64 -0
- data/vendor/wreq/src/tls/keylog.rs +99 -0
- data/vendor/wreq/src/tls/options.rs +464 -0
- data/vendor/wreq/src/tls/x509/identity.rs +122 -0
- data/vendor/wreq/src/tls/x509/parser.rs +71 -0
- data/vendor/wreq/src/tls/x509/store.rs +228 -0
- data/vendor/wreq/src/tls/x509.rs +68 -0
- data/vendor/wreq/src/tls.rs +154 -0
- data/vendor/wreq/src/trace.rs +55 -0
- data/vendor/wreq/src/util.rs +122 -0
- data/vendor/wreq/tests/badssl.rs +228 -0
- data/vendor/wreq/tests/brotli.rs +350 -0
- data/vendor/wreq/tests/client.rs +1098 -0
- data/vendor/wreq/tests/connector_layers.rs +227 -0
- data/vendor/wreq/tests/cookie.rs +306 -0
- data/vendor/wreq/tests/deflate.rs +347 -0
- data/vendor/wreq/tests/emulation.rs +260 -0
- data/vendor/wreq/tests/gzip.rs +347 -0
- data/vendor/wreq/tests/layers.rs +261 -0
- data/vendor/wreq/tests/multipart.rs +165 -0
- data/vendor/wreq/tests/proxy.rs +438 -0
- data/vendor/wreq/tests/redirect.rs +629 -0
- data/vendor/wreq/tests/retry.rs +135 -0
- data/vendor/wreq/tests/support/delay_server.rs +117 -0
- data/vendor/wreq/tests/support/error.rs +16 -0
- data/vendor/wreq/tests/support/layer.rs +183 -0
- data/vendor/wreq/tests/support/mod.rs +9 -0
- data/vendor/wreq/tests/support/server.rs +232 -0
- data/vendor/wreq/tests/timeouts.rs +281 -0
- data/vendor/wreq/tests/unix_socket.rs +135 -0
- data/vendor/wreq/tests/upgrade.rs +98 -0
- data/vendor/wreq/tests/zstd.rs +559 -0
- 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
|
+
}
|