wreq-rb 0.5.0 → 0.5.1
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 +4 -4
- data/Cargo.lock +1922 -397
- data/LICENSE +203 -0
- data/README.md +19 -15
- data/ext/wreq_rb/Cargo.toml +4 -6
- data/ext/wreq_rb/src/client.rs +41 -48
- data/lib/wreq-rb/version.rb +1 -1
- data/patches/0001-add-transfer-size-tracking.patch +76 -67
- data/vendor/wreq/Cargo.toml +119 -71
- data/vendor/wreq/README.md +25 -20
- data/vendor/wreq/bench/http1.rs +25 -0
- data/vendor/wreq/bench/http1_over_tls.rs +25 -0
- data/vendor/wreq/bench/http2.rs +25 -0
- data/vendor/wreq/bench/http2_over_tls.rs +25 -0
- data/vendor/wreq/bench/support/bench.rs +91 -0
- data/vendor/wreq/bench/support/client.rs +217 -0
- data/vendor/wreq/bench/support/server.rs +188 -0
- data/vendor/wreq/bench/support.rs +56 -0
- data/vendor/wreq/examples/cert_store.rs +4 -4
- data/vendor/wreq/examples/{emulation.rs → emulate.rs} +2 -2
- data/vendor/wreq/examples/http2_websocket.rs +2 -2
- data/vendor/wreq/examples/keylog.rs +3 -3
- data/vendor/wreq/examples/{request_with_emulation.rs → request_with_emulate.rs} +2 -2
- data/vendor/wreq/examples/rt.rs +23 -0
- data/vendor/wreq/src/client/body.rs +23 -61
- data/vendor/wreq/src/client/emulate.rs +119 -0
- data/vendor/wreq/src/client/{http/future.rs → future.rs} +11 -32
- data/vendor/wreq/src/client/{http → layer}/client/pool.rs +66 -61
- data/vendor/wreq/src/client/{http → layer}/client.rs +416 -270
- data/vendor/wreq/src/client/layer/config.rs +27 -6
- data/vendor/wreq/src/client/layer/decoder.rs +9 -4
- data/vendor/wreq/src/client/layer/redirect/future.rs +6 -3
- data/vendor/wreq/src/client/layer/redirect.rs +4 -5
- data/vendor/wreq/src/client/layer/retry.rs +8 -5
- data/vendor/wreq/src/client/layer/timeout/body.rs +15 -6
- data/vendor/wreq/src/client/layer/timeout/future.rs +23 -18
- data/vendor/wreq/src/client/layer/timeout.rs +24 -74
- data/vendor/wreq/src/client/layer.rs +1 -2
- data/vendor/wreq/src/client/multipart.rs +137 -154
- data/vendor/wreq/src/client/request.rs +202 -118
- data/vendor/wreq/src/client/response.rs +46 -45
- data/vendor/wreq/src/client/upgrade.rs +15 -0
- data/vendor/wreq/src/client/ws.rs +73 -25
- data/vendor/wreq/src/client.rs +1655 -17
- data/vendor/wreq/src/config.rs +11 -11
- data/vendor/wreq/src/{client/conn → conn}/connector.rs +139 -137
- data/vendor/wreq/src/conn/descriptor.rs +143 -0
- data/vendor/wreq/src/conn/http.rs +484 -0
- data/vendor/wreq/src/conn/net/io.rs +75 -0
- data/vendor/wreq/src/conn/net/tcp/compio.rs +71 -0
- data/vendor/wreq/src/conn/net/tcp/tokio.rs +57 -0
- data/vendor/wreq/src/conn/net/tcp.rs +561 -0
- data/vendor/wreq/src/conn/net/uds/compio.rs +60 -0
- data/vendor/wreq/src/{client/conn/uds.rs → conn/net/uds/tokio.rs} +18 -12
- data/vendor/wreq/src/conn/net/uds.rs +11 -0
- data/vendor/wreq/src/conn/net.rs +130 -0
- data/vendor/wreq/src/{client/conn → conn}/proxy/socks.rs +2 -9
- data/vendor/wreq/src/{client/conn → conn}/proxy/tunnel.rs +21 -56
- data/vendor/wreq/src/conn/tls_info.rs +47 -0
- data/vendor/wreq/src/{client/conn.rs → conn.rs} +202 -54
- data/vendor/wreq/src/cookie.rs +302 -142
- data/vendor/wreq/src/dns/gai/compio.rs +77 -0
- data/vendor/wreq/src/dns/gai/tokio.rs +90 -0
- data/vendor/wreq/src/dns/gai.rs +14 -164
- data/vendor/wreq/src/dns/hickory.rs +16 -23
- data/vendor/wreq/src/dns/resolve.rs +7 -41
- data/vendor/wreq/src/dns.rs +90 -7
- data/vendor/wreq/src/error.rs +57 -31
- data/vendor/wreq/src/ext.rs +25 -0
- data/vendor/wreq/src/group.rs +211 -0
- data/vendor/wreq/src/header.rs +100 -112
- data/vendor/wreq/src/lib.rs +124 -73
- data/vendor/wreq/src/proxy.rs +6 -20
- data/vendor/wreq/src/redirect.rs +1 -1
- data/vendor/wreq/src/rt.rs +208 -0
- data/vendor/wreq/src/sync.rs +97 -98
- data/vendor/wreq/src/tls/compress.rs +124 -0
- data/vendor/wreq/src/tls/conn/ext.rs +54 -45
- data/vendor/wreq/src/tls/conn/service.rs +14 -18
- data/vendor/wreq/src/tls/conn.rs +169 -241
- data/vendor/wreq/src/tls/keylog.rs +68 -5
- data/vendor/wreq/src/tls/session.rs +205 -0
- data/vendor/wreq/src/tls/{x509 → trust}/identity.rs +4 -21
- data/vendor/wreq/src/tls/{x509/parser.rs → trust/parse.rs} +1 -1
- data/vendor/wreq/src/tls/{x509 → trust}/store.rs +42 -81
- data/vendor/wreq/src/tls/{x509.rs → trust.rs} +8 -2
- data/vendor/wreq/src/tls.rs +489 -25
- data/vendor/wreq/src/trace.rs +0 -12
- data/vendor/wreq/src/util.rs +1 -1
- data/vendor/wreq/tests/badssl.rs +10 -10
- data/vendor/wreq/tests/client.rs +3 -9
- data/vendor/wreq/tests/cookie.rs +6 -8
- data/vendor/wreq/tests/{emulation.rs → emulate.rs} +130 -22
- data/vendor/wreq/tests/multipart.rs +43 -1
- data/vendor/wreq/tests/proxy.rs +1 -1
- data/vendor/wreq/tests/support/layer.rs +1 -0
- metadata +49 -71
- data/patches/0002-add-cancel-connections.patch +0 -181
- data/vendor/wreq/src/client/conn/conn.rs +0 -231
- data/vendor/wreq/src/client/conn/http.rs +0 -1023
- data/vendor/wreq/src/client/conn/tls_info.rs +0 -98
- data/vendor/wreq/src/client/core/body/incoming.rs +0 -485
- data/vendor/wreq/src/client/core/body/length.rs +0 -118
- data/vendor/wreq/src/client/core/body.rs +0 -34
- data/vendor/wreq/src/client/core/common/buf.rs +0 -149
- data/vendor/wreq/src/client/core/common/rewind.rs +0 -141
- data/vendor/wreq/src/client/core/common/watch.rs +0 -76
- data/vendor/wreq/src/client/core/common.rs +0 -3
- data/vendor/wreq/src/client/core/conn/http1.rs +0 -342
- data/vendor/wreq/src/client/core/conn/http2.rs +0 -307
- data/vendor/wreq/src/client/core/conn.rs +0 -11
- data/vendor/wreq/src/client/core/dispatch.rs +0 -299
- data/vendor/wreq/src/client/core/error.rs +0 -435
- data/vendor/wreq/src/client/core/ext.rs +0 -201
- data/vendor/wreq/src/client/core/http1.rs +0 -178
- data/vendor/wreq/src/client/core/http2.rs +0 -483
- data/vendor/wreq/src/client/core/proto/h1/conn.rs +0 -988
- data/vendor/wreq/src/client/core/proto/h1/decode.rs +0 -1170
- data/vendor/wreq/src/client/core/proto/h1/dispatch.rs +0 -684
- data/vendor/wreq/src/client/core/proto/h1/encode.rs +0 -580
- data/vendor/wreq/src/client/core/proto/h1/io.rs +0 -879
- data/vendor/wreq/src/client/core/proto/h1/role.rs +0 -694
- data/vendor/wreq/src/client/core/proto/h1.rs +0 -104
- data/vendor/wreq/src/client/core/proto/h2/client.rs +0 -650
- data/vendor/wreq/src/client/core/proto/h2/ping.rs +0 -539
- data/vendor/wreq/src/client/core/proto/h2.rs +0 -379
- data/vendor/wreq/src/client/core/proto/headers.rs +0 -138
- data/vendor/wreq/src/client/core/proto.rs +0 -58
- data/vendor/wreq/src/client/core/rt/bounds.rs +0 -57
- data/vendor/wreq/src/client/core/rt/timer.rs +0 -150
- data/vendor/wreq/src/client/core/rt/tokio.rs +0 -99
- data/vendor/wreq/src/client/core/rt.rs +0 -25
- data/vendor/wreq/src/client/core/upgrade.rs +0 -267
- data/vendor/wreq/src/client/core.rs +0 -16
- data/vendor/wreq/src/client/emulation.rs +0 -161
- data/vendor/wreq/src/client/http/client/error.rs +0 -142
- data/vendor/wreq/src/client/http/client/exec.rs +0 -29
- data/vendor/wreq/src/client/http/client/extra.rs +0 -77
- data/vendor/wreq/src/client/http/client/util.rs +0 -104
- data/vendor/wreq/src/client/http.rs +0 -1629
- data/vendor/wreq/src/client/layer/config/options.rs +0 -156
- data/vendor/wreq/src/client/layer/cookie.rs +0 -161
- data/vendor/wreq/src/hash.rs +0 -143
- data/vendor/wreq/src/tls/conn/cache.rs +0 -123
- data/vendor/wreq/src/tls/conn/cert_compression.rs +0 -125
- data/vendor/wreq/src/tls/keylog/handle.rs +0 -64
- data/vendor/wreq/src/tls/options.rs +0 -464
- /data/vendor/wreq/src/client/{http → layer}/client/lazy.rs +0 -0
- /data/vendor/wreq/src/{client/conn → conn}/proxy.rs +0 -0
- /data/vendor/wreq/src/{client/conn → conn}/verbose.rs +0 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//! HTTP/2 benchmark
|
|
2
|
+
|
|
3
|
+
mod support;
|
|
4
|
+
|
|
5
|
+
use std::time::Duration;
|
|
6
|
+
|
|
7
|
+
use criterion::{Criterion, criterion_group, criterion_main};
|
|
8
|
+
use support::{HttpVersion, Tls, bench};
|
|
9
|
+
|
|
10
|
+
const NUM_REQUESTS_TO_SEND: usize = 500;
|
|
11
|
+
|
|
12
|
+
#[inline]
|
|
13
|
+
fn bench(c: &mut Criterion) {
|
|
14
|
+
bench::bench(c, Tls::Disabled, HttpVersion::Http2, NUM_REQUESTS_TO_SEND)
|
|
15
|
+
.expect("Failed to run HTTP/2 benchmark server")
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
criterion_group!(
|
|
19
|
+
name = benches;
|
|
20
|
+
config = Criterion::default()
|
|
21
|
+
.sample_size(10)
|
|
22
|
+
.warm_up_time(Duration::from_secs(3));
|
|
23
|
+
targets = bench
|
|
24
|
+
);
|
|
25
|
+
criterion_main!(benches);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//! HTTP/2 over TLS benchmark
|
|
2
|
+
|
|
3
|
+
mod support;
|
|
4
|
+
|
|
5
|
+
use std::time::Duration;
|
|
6
|
+
|
|
7
|
+
use criterion::{Criterion, criterion_group, criterion_main};
|
|
8
|
+
use support::{HttpVersion, Tls, bench};
|
|
9
|
+
|
|
10
|
+
const NUM_REQUESTS_TO_SEND: usize = 500;
|
|
11
|
+
|
|
12
|
+
#[inline]
|
|
13
|
+
fn bench(c: &mut Criterion) {
|
|
14
|
+
bench::bench(c, Tls::Enabled, HttpVersion::Http2, NUM_REQUESTS_TO_SEND)
|
|
15
|
+
.expect("Failed to run HTTP/2 over TLS benchmark server")
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
criterion_group!(
|
|
19
|
+
name = benches;
|
|
20
|
+
config = Criterion::default()
|
|
21
|
+
.sample_size(10)
|
|
22
|
+
.warm_up_time(Duration::from_secs(3));
|
|
23
|
+
targets = bench
|
|
24
|
+
);
|
|
25
|
+
criterion_main!(benches);
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
use criterion::Criterion;
|
|
2
|
+
|
|
3
|
+
use crate::support::{
|
|
4
|
+
BoxError, HttpVersion, Tls, client::bench_clients, current_thread_runtime,
|
|
5
|
+
multi_thread_runtime, server::with_server,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
pub const CURRENT_THREAD_LABEL: &str = "current_thread";
|
|
9
|
+
pub const MULTI_THREAD_LABEL: &str = "multi_thread";
|
|
10
|
+
pub const CONCURRENT_CASES: &[usize] = &[10, 50, 100, 150];
|
|
11
|
+
|
|
12
|
+
/// Recommended chunk sizes for real-world network scenarios:
|
|
13
|
+
/// - 16 KB: Matches standard TCP buffers, ideal for HTTP/2 frames.
|
|
14
|
+
/// - 32 KB: For large HTTP payloads, fits modern socket buffers.
|
|
15
|
+
/// - 64 KB: Default Linux buffer size, optimized for large uploads.
|
|
16
|
+
/// - 128 KB: For high-throughput, large-scale transfers.
|
|
17
|
+
/// - 256 KB: Bulk data, maximum throughput on fast networks.
|
|
18
|
+
///
|
|
19
|
+
/// For benchmarking latency-sensitive and high-throughput transfers.
|
|
20
|
+
pub const BODY_CASES: [(&[u8], usize); 7] = [
|
|
21
|
+
(&[b'a'; 1024], 1024), // 1 KB, chunk 1 KB
|
|
22
|
+
(&[b'a'; 10 * 1024], 10 * 1024), // 10 KB, chunk 10 KB
|
|
23
|
+
(&[b'a'; 64 * 1024], 16 * 1024), // 64 KB, chunk 16 KB
|
|
24
|
+
(&[b'a'; 128 * 1024], 32 * 1024), // 128 KB, chunk 32 KB
|
|
25
|
+
(&[b'a'; 1024 * 1024], 64 * 1024), // 1 MB, chunk 64 KB
|
|
26
|
+
(&[b'a'; 2 * 1024 * 1024], 128 * 1024), // 2 MB, chunk 128 KB
|
|
27
|
+
(&[b'a'; 4 * 1024 * 1024], 256 * 1024), // 4 MB, chunk 256 KB
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
pub fn bench(
|
|
31
|
+
c: &mut Criterion,
|
|
32
|
+
tls: Tls,
|
|
33
|
+
http_version: HttpVersion,
|
|
34
|
+
num_requests: usize,
|
|
35
|
+
) -> Result<(), BoxError> {
|
|
36
|
+
const OS: &str = std::env::consts::OS;
|
|
37
|
+
const ARCH: &str = std::env::consts::ARCH;
|
|
38
|
+
|
|
39
|
+
let system = sysinfo::System::new_all();
|
|
40
|
+
let cpu_model = system
|
|
41
|
+
.cpus()
|
|
42
|
+
.first()
|
|
43
|
+
.map_or("n/a", |cpu| cpu.brand().trim_start().trim_end());
|
|
44
|
+
|
|
45
|
+
for &concurrent_limit in CONCURRENT_CASES {
|
|
46
|
+
for body in BODY_CASES {
|
|
47
|
+
with_server(tls, |addr| {
|
|
48
|
+
// single-threaded client
|
|
49
|
+
let mut group = c.benchmark_group(format!(
|
|
50
|
+
"{cpu_model}/{OS}_{ARCH}/{CURRENT_THREAD_LABEL}/{tls}/{http_version}/{concurrent_limit}/{}KB",
|
|
51
|
+
body.0.len() / 1024,
|
|
52
|
+
));
|
|
53
|
+
|
|
54
|
+
bench_clients(
|
|
55
|
+
&mut group,
|
|
56
|
+
current_thread_runtime,
|
|
57
|
+
addr,
|
|
58
|
+
tls,
|
|
59
|
+
http_version,
|
|
60
|
+
num_requests,
|
|
61
|
+
concurrent_limit,
|
|
62
|
+
body,
|
|
63
|
+
)?;
|
|
64
|
+
group.finish();
|
|
65
|
+
Ok(())
|
|
66
|
+
})?;
|
|
67
|
+
|
|
68
|
+
with_server(tls, |addr| {
|
|
69
|
+
// multi-threaded client
|
|
70
|
+
let mut group = c.benchmark_group(format!(
|
|
71
|
+
"{cpu_model}/{OS}_{ARCH}/{MULTI_THREAD_LABEL}/{tls}/{http_version}/{concurrent_limit}/{}KB",
|
|
72
|
+
body.0.len() / 1024,
|
|
73
|
+
));
|
|
74
|
+
bench_clients(
|
|
75
|
+
&mut group,
|
|
76
|
+
multi_thread_runtime,
|
|
77
|
+
addr,
|
|
78
|
+
tls,
|
|
79
|
+
http_version,
|
|
80
|
+
num_requests,
|
|
81
|
+
concurrent_limit,
|
|
82
|
+
body,
|
|
83
|
+
)?;
|
|
84
|
+
group.finish();
|
|
85
|
+
Ok(())
|
|
86
|
+
})?;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
Ok(())
|
|
91
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
use std::{convert::Infallible, net::SocketAddr, sync::Arc};
|
|
2
|
+
|
|
3
|
+
use bytes::Bytes;
|
|
4
|
+
use criterion::{BenchmarkGroup, measurement::WallTime};
|
|
5
|
+
use http_body_util::BodyExt;
|
|
6
|
+
use tokio::{runtime::Runtime, sync::Semaphore};
|
|
7
|
+
|
|
8
|
+
use super::{BoxError, HttpVersion, Tls};
|
|
9
|
+
|
|
10
|
+
fn create_wreq_client(tls: Tls, http_version: HttpVersion) -> Result<wreq::Client, BoxError> {
|
|
11
|
+
let builder = wreq::Client::builder()
|
|
12
|
+
.no_proxy()
|
|
13
|
+
.redirect(wreq::redirect::Policy::none())
|
|
14
|
+
.tls_cert_verification(!matches!(tls, Tls::Enabled));
|
|
15
|
+
|
|
16
|
+
let builder = match http_version {
|
|
17
|
+
HttpVersion::Http1 => builder.http1_only(),
|
|
18
|
+
HttpVersion::Http2 => builder.http2_only(),
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
Ok(builder.build()?)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
fn create_reqwest_client(tls: Tls, http_version: HttpVersion) -> Result<reqwest::Client, BoxError> {
|
|
25
|
+
let builder = reqwest::Client::builder()
|
|
26
|
+
.no_proxy()
|
|
27
|
+
.redirect(reqwest::redirect::Policy::none())
|
|
28
|
+
.danger_accept_invalid_certs(matches!(tls, Tls::Enabled));
|
|
29
|
+
|
|
30
|
+
let builder = match http_version {
|
|
31
|
+
HttpVersion::Http1 => builder.http1_only(),
|
|
32
|
+
HttpVersion::Http2 => builder.http2_prior_knowledge(),
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
Ok(builder.build()?)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async fn wreq_body_assert(mut response: wreq::Response, expected_body_size: usize) {
|
|
39
|
+
let mut body_size = 0;
|
|
40
|
+
while let Some(Ok(chunk)) = response.frame().await {
|
|
41
|
+
if let Ok(chunk) = chunk.into_data() {
|
|
42
|
+
body_size += chunk.len();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
assert!(
|
|
46
|
+
body_size == expected_body_size,
|
|
47
|
+
"Unexpected response body: got {body_size} bytes, expected {expected_body_size} bytes"
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async fn reqwest_body_assert(mut response: reqwest::Response, expected_body_size: usize) {
|
|
52
|
+
let mut body_size = 0;
|
|
53
|
+
while let Ok(Some(chunk)) = response.chunk().await {
|
|
54
|
+
body_size += chunk.len();
|
|
55
|
+
}
|
|
56
|
+
assert!(
|
|
57
|
+
body_size == expected_body_size,
|
|
58
|
+
"Unexpected response body: got {body_size} bytes, expected {expected_body_size} bytes"
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
fn stream_from_bytes(
|
|
63
|
+
body: &'static [u8],
|
|
64
|
+
chunk_size: usize,
|
|
65
|
+
) -> impl futures_util::stream::TryStream<Ok = Bytes, Error = Infallible> + Send + 'static {
|
|
66
|
+
futures_util::stream::unfold((body, 0), move |(body, offset)| async move {
|
|
67
|
+
if offset >= body.len() {
|
|
68
|
+
None
|
|
69
|
+
} else {
|
|
70
|
+
let end = (offset + chunk_size).min(body.len());
|
|
71
|
+
let chunk = Bytes::from_static(&body[offset..end]);
|
|
72
|
+
Some((Ok::<Bytes, Infallible>(chunk), (body, end)))
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
#[inline]
|
|
78
|
+
fn wreq_body(stream: bool, (body, chunk_size): (&'static [u8], usize)) -> wreq::Body {
|
|
79
|
+
if stream {
|
|
80
|
+
let stream = stream_from_bytes(body, chunk_size);
|
|
81
|
+
wreq::Body::wrap_stream(stream)
|
|
82
|
+
} else {
|
|
83
|
+
wreq::Body::from(body)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
#[inline]
|
|
88
|
+
fn reqwest_body(stream: bool, (body, chunk_size): (&'static [u8], usize)) -> reqwest::Body {
|
|
89
|
+
if stream {
|
|
90
|
+
let stream = stream_from_bytes(body, chunk_size);
|
|
91
|
+
reqwest::Body::wrap_stream(stream)
|
|
92
|
+
} else {
|
|
93
|
+
reqwest::Body::from(body)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async fn wreq_requests_concurrent(
|
|
98
|
+
client: &wreq::Client,
|
|
99
|
+
url: &str,
|
|
100
|
+
num_requests: usize,
|
|
101
|
+
concurrent_limit: usize,
|
|
102
|
+
body: (&'static [u8], usize),
|
|
103
|
+
stream: bool,
|
|
104
|
+
) {
|
|
105
|
+
let semaphore = Arc::new(Semaphore::new(concurrent_limit));
|
|
106
|
+
let mut handles = Vec::with_capacity(num_requests);
|
|
107
|
+
for _ in 0..num_requests {
|
|
108
|
+
let client = client.clone();
|
|
109
|
+
let url = url.to_string();
|
|
110
|
+
let semaphore = semaphore.clone();
|
|
111
|
+
let fut = async move {
|
|
112
|
+
let _permit = semaphore
|
|
113
|
+
.acquire()
|
|
114
|
+
.await
|
|
115
|
+
.expect("Semaphore should be acquirable");
|
|
116
|
+
let response = client
|
|
117
|
+
.post(url)
|
|
118
|
+
.body(wreq_body(stream, body))
|
|
119
|
+
.send()
|
|
120
|
+
.await
|
|
121
|
+
.expect("Unexpected request failure");
|
|
122
|
+
wreq_body_assert(response, body.0.len()).await;
|
|
123
|
+
};
|
|
124
|
+
handles.push(tokio::spawn(fut));
|
|
125
|
+
}
|
|
126
|
+
futures_util::future::join_all(handles).await;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async fn reqwest_requests_concurrent(
|
|
130
|
+
client: &reqwest::Client,
|
|
131
|
+
url: &str,
|
|
132
|
+
num_requests: usize,
|
|
133
|
+
concurrent_limit: usize,
|
|
134
|
+
body: (&'static [u8], usize),
|
|
135
|
+
stream: bool,
|
|
136
|
+
) {
|
|
137
|
+
let semaphore = Arc::new(Semaphore::new(concurrent_limit));
|
|
138
|
+
let mut handles = Vec::with_capacity(num_requests);
|
|
139
|
+
for _ in 0..num_requests {
|
|
140
|
+
let client = client.clone();
|
|
141
|
+
let url = url.to_string();
|
|
142
|
+
let semaphore = semaphore.clone();
|
|
143
|
+
let fut = async move {
|
|
144
|
+
let _permit = semaphore
|
|
145
|
+
.acquire()
|
|
146
|
+
.await
|
|
147
|
+
.expect("Semaphore should be acquirable");
|
|
148
|
+
let response = client
|
|
149
|
+
.post(url)
|
|
150
|
+
.body(reqwest_body(stream, body))
|
|
151
|
+
.send()
|
|
152
|
+
.await
|
|
153
|
+
.expect("Unexpected request failure");
|
|
154
|
+
reqwest_body_assert(response, body.0.len()).await;
|
|
155
|
+
};
|
|
156
|
+
handles.push(tokio::spawn(fut));
|
|
157
|
+
}
|
|
158
|
+
futures_util::future::join_all(handles).await;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
#[allow(clippy::too_many_arguments)]
|
|
162
|
+
pub fn bench_clients(
|
|
163
|
+
group: &mut BenchmarkGroup<'_, WallTime>,
|
|
164
|
+
rt: fn() -> Runtime,
|
|
165
|
+
addr: SocketAddr,
|
|
166
|
+
tls: Tls,
|
|
167
|
+
http_version: HttpVersion,
|
|
168
|
+
num_requests: usize,
|
|
169
|
+
concurrent_limit: usize,
|
|
170
|
+
body: (&'static [u8], usize),
|
|
171
|
+
) -> Result<(), BoxError> {
|
|
172
|
+
let url = format!("{tls}://{addr}");
|
|
173
|
+
|
|
174
|
+
fn make_benchmark_label<T: ?Sized>(stream: bool) -> String {
|
|
175
|
+
let client = std::any::type_name::<T>()
|
|
176
|
+
.split("::")
|
|
177
|
+
.next()
|
|
178
|
+
.expect("Type name should contain at least one segment");
|
|
179
|
+
|
|
180
|
+
let body_type = if stream { "stream" } else { "full" };
|
|
181
|
+
format!("{body_type}/{client}")
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
for stream in [false, true] {
|
|
185
|
+
let client = create_wreq_client(tls, http_version)?;
|
|
186
|
+
group.bench_function(make_benchmark_label::<wreq::Client>(stream), |b| {
|
|
187
|
+
b.to_async(rt()).iter(|| {
|
|
188
|
+
wreq_requests_concurrent(
|
|
189
|
+
&client,
|
|
190
|
+
&url,
|
|
191
|
+
num_requests,
|
|
192
|
+
concurrent_limit,
|
|
193
|
+
body,
|
|
194
|
+
stream,
|
|
195
|
+
)
|
|
196
|
+
})
|
|
197
|
+
});
|
|
198
|
+
::std::mem::drop(client);
|
|
199
|
+
|
|
200
|
+
let client = create_reqwest_client(tls, http_version)?;
|
|
201
|
+
group.bench_function(make_benchmark_label::<reqwest::Client>(stream), |b| {
|
|
202
|
+
b.to_async(rt()).iter(|| {
|
|
203
|
+
reqwest_requests_concurrent(
|
|
204
|
+
&client,
|
|
205
|
+
&url,
|
|
206
|
+
num_requests,
|
|
207
|
+
concurrent_limit,
|
|
208
|
+
body,
|
|
209
|
+
stream,
|
|
210
|
+
)
|
|
211
|
+
})
|
|
212
|
+
});
|
|
213
|
+
::std::mem::drop(client);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
Ok(())
|
|
217
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
use std::{convert::Infallible, io, net::SocketAddr, pin::Pin, sync::Arc, time::Duration};
|
|
2
|
+
|
|
3
|
+
use btls::{
|
|
4
|
+
pkey::PKey,
|
|
5
|
+
ssl::{Ssl, SslAcceptor, SslMethod},
|
|
6
|
+
x509::X509,
|
|
7
|
+
};
|
|
8
|
+
use bytes::Bytes;
|
|
9
|
+
use http_body_util::{BodyExt, Collected, Full};
|
|
10
|
+
use hyper::{body::Incoming, service::service_fn};
|
|
11
|
+
use hyper_util::{
|
|
12
|
+
rt::{TokioExecutor, TokioIo, TokioTimer},
|
|
13
|
+
server::conn::auto::Builder,
|
|
14
|
+
};
|
|
15
|
+
use tokio::{
|
|
16
|
+
io::{AsyncRead, AsyncWrite},
|
|
17
|
+
net::{TcpListener, TcpStream},
|
|
18
|
+
sync::oneshot,
|
|
19
|
+
task::JoinSet,
|
|
20
|
+
};
|
|
21
|
+
use tokio_btls::SslStream;
|
|
22
|
+
|
|
23
|
+
use super::{BoxError, Tls, multi_thread_runtime};
|
|
24
|
+
|
|
25
|
+
pub struct Server {
|
|
26
|
+
listener: std::net::TcpListener,
|
|
27
|
+
tls_acceptor: Option<Arc<SslAcceptor>>,
|
|
28
|
+
builder: Builder<TokioExecutor>,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
impl Server {
|
|
32
|
+
pub fn new(tls: Tls) -> Result<Self, BoxError> {
|
|
33
|
+
let tls_acceptor = match tls {
|
|
34
|
+
Tls::Enabled => {
|
|
35
|
+
let mut builder = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls())?;
|
|
36
|
+
|
|
37
|
+
let cert = X509::from_der(include_bytes!("../../tests/support/server.cert"))?;
|
|
38
|
+
let key =
|
|
39
|
+
PKey::private_key_from_der(include_bytes!("../../tests/support/server.key"))?;
|
|
40
|
+
|
|
41
|
+
builder.set_certificate(&cert)?;
|
|
42
|
+
builder.set_private_key(&key)?;
|
|
43
|
+
builder.check_private_key()?;
|
|
44
|
+
|
|
45
|
+
Some(Arc::new(builder.build()))
|
|
46
|
+
}
|
|
47
|
+
Tls::Disabled => None,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
let mut builder = Builder::new(TokioExecutor::new());
|
|
51
|
+
builder.http1().timer(TokioTimer::new()).keep_alive(true);
|
|
52
|
+
builder
|
|
53
|
+
.http2()
|
|
54
|
+
.timer(TokioTimer::new())
|
|
55
|
+
.keep_alive_interval(Duration::from_secs(30));
|
|
56
|
+
|
|
57
|
+
let listener = std::net::TcpListener::bind("127.0.0.1:0")?;
|
|
58
|
+
listener.set_nonblocking(true)?;
|
|
59
|
+
|
|
60
|
+
Ok(Server {
|
|
61
|
+
listener,
|
|
62
|
+
tls_acceptor,
|
|
63
|
+
builder,
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
fn local_addr(&self) -> io::Result<SocketAddr> {
|
|
68
|
+
self.listener.local_addr()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async fn run(self, mut shutdown: oneshot::Receiver<()>) -> Result<(), BoxError> {
|
|
72
|
+
let mut join_set = JoinSet::new();
|
|
73
|
+
let listener = TcpListener::from_std(self.listener)?;
|
|
74
|
+
|
|
75
|
+
loop {
|
|
76
|
+
tokio::select! {
|
|
77
|
+
_ = &mut shutdown => {
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
accept = listener.accept() => {
|
|
81
|
+
if let Ok((socket, _peer_addr)) = accept {
|
|
82
|
+
let tls_acceptor = self.tls_acceptor.clone();
|
|
83
|
+
let builder = self.builder.clone();
|
|
84
|
+
join_set.spawn(async move {
|
|
85
|
+
handle_connection(socket, tls_acceptor, builder).await;
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
while let Some(result) = join_set.join_next().await {
|
|
93
|
+
if let Err(e) = result {
|
|
94
|
+
eprintln!("connection task failed: {e}");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Tokio internally accepts TCP connections while the TCPListener is active;
|
|
99
|
+
// drop the listener to immediately refuse connections rather than letting
|
|
100
|
+
// them hang.
|
|
101
|
+
::std::mem::drop(listener);
|
|
102
|
+
Ok(())
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
pub struct Handle {
|
|
107
|
+
shutdown: oneshot::Sender<()>,
|
|
108
|
+
join: std::thread::JoinHandle<()>,
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
impl Handle {
|
|
112
|
+
pub fn shutdown(self) {
|
|
113
|
+
let _ = self.shutdown.send(());
|
|
114
|
+
let _ = self.join.join();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
pub fn with_server<F>(tls: Tls, f: F) -> Result<(), BoxError>
|
|
119
|
+
where
|
|
120
|
+
F: FnOnce(SocketAddr) -> Result<(), BoxError>,
|
|
121
|
+
{
|
|
122
|
+
let server = Server::new(tls)?;
|
|
123
|
+
let addr = server.local_addr()?;
|
|
124
|
+
|
|
125
|
+
let (shutdown_tx, shutdown_rx) = oneshot::channel();
|
|
126
|
+
|
|
127
|
+
let join = std::thread::spawn(move || {
|
|
128
|
+
let rt = multi_thread_runtime();
|
|
129
|
+
rt.block_on(server.run(shutdown_rx))
|
|
130
|
+
.expect("Failed to run server with shutdown");
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
std::thread::sleep(Duration::from_millis(100));
|
|
134
|
+
|
|
135
|
+
let handle = Handle {
|
|
136
|
+
shutdown: shutdown_tx,
|
|
137
|
+
join,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
f(addr)?;
|
|
141
|
+
handle.shutdown();
|
|
142
|
+
|
|
143
|
+
std::thread::sleep(Duration::from_millis(100));
|
|
144
|
+
Ok(())
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async fn serve<S>(builder: Builder<TokioExecutor>, stream: S)
|
|
148
|
+
where
|
|
149
|
+
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
|
150
|
+
{
|
|
151
|
+
let _ = builder
|
|
152
|
+
.serve_connection(
|
|
153
|
+
TokioIo::new(stream),
|
|
154
|
+
service_fn(|req: http::Request<Incoming>| async {
|
|
155
|
+
let bytes = req
|
|
156
|
+
.into_body()
|
|
157
|
+
.collect()
|
|
158
|
+
.await
|
|
159
|
+
.map(Collected::<Bytes>::to_bytes);
|
|
160
|
+
let bytes = bytes.unwrap_or_else(|_| Bytes::new());
|
|
161
|
+
Ok::<_, Infallible>(http::Response::new(Full::new(bytes)))
|
|
162
|
+
}),
|
|
163
|
+
)
|
|
164
|
+
.await;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async fn handle_connection(
|
|
168
|
+
socket: TcpStream,
|
|
169
|
+
tls_acceptor: Option<Arc<SslAcceptor>>,
|
|
170
|
+
builder: Builder<TokioExecutor>,
|
|
171
|
+
) {
|
|
172
|
+
if let Some(acceptor) = tls_acceptor {
|
|
173
|
+
let ssl = Ssl::new(acceptor.context()).expect("failed to create Ssl");
|
|
174
|
+
let mut stream = SslStream::new(ssl, socket).expect("failed to create SslStream");
|
|
175
|
+
|
|
176
|
+
// The client (or its connection pool) may proactively close the connection,
|
|
177
|
+
// especially during benchmarks or when cleaning up idle connections.
|
|
178
|
+
// This can cause TLS handshake failures (e.g., ConnectionReset, ConnectionAborted).
|
|
179
|
+
// Such errors are expected and should be handled gracefully to avoid panicking
|
|
180
|
+
// and to ensure the server remains robust under load.
|
|
181
|
+
if Pin::new(&mut stream).accept().await.is_err() {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
serve(builder, stream).await;
|
|
185
|
+
} else {
|
|
186
|
+
serve(builder, socket).await;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
pub mod bench;
|
|
2
|
+
pub mod client;
|
|
3
|
+
pub mod server;
|
|
4
|
+
|
|
5
|
+
use std::fmt;
|
|
6
|
+
|
|
7
|
+
pub type BoxError = Box<dyn std::error::Error + Send + Sync>;
|
|
8
|
+
|
|
9
|
+
#[allow(unused)]
|
|
10
|
+
#[derive(Clone, Copy, Debug)]
|
|
11
|
+
pub enum HttpVersion {
|
|
12
|
+
Http1,
|
|
13
|
+
Http2,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
impl fmt::Display for HttpVersion {
|
|
17
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
18
|
+
let value = match self {
|
|
19
|
+
HttpVersion::Http1 => "h1",
|
|
20
|
+
HttpVersion::Http2 => "h2",
|
|
21
|
+
};
|
|
22
|
+
f.write_str(value)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#[allow(unused)]
|
|
27
|
+
#[derive(Clone, Copy, Debug)]
|
|
28
|
+
pub enum Tls {
|
|
29
|
+
Enabled,
|
|
30
|
+
Disabled,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
impl fmt::Display for Tls {
|
|
34
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
35
|
+
let value = match self {
|
|
36
|
+
Tls::Enabled => "https",
|
|
37
|
+
Tls::Disabled => "http",
|
|
38
|
+
};
|
|
39
|
+
f.write_str(value)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
pub fn current_thread_runtime() -> tokio::runtime::Runtime {
|
|
44
|
+
tokio::runtime::Builder::new_current_thread()
|
|
45
|
+
.enable_all()
|
|
46
|
+
.build()
|
|
47
|
+
.expect("Failed to build current-thread runtime")
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
pub fn multi_thread_runtime() -> tokio::runtime::Runtime {
|
|
51
|
+
tokio::runtime::Builder::new_multi_thread()
|
|
52
|
+
.worker_threads(4)
|
|
53
|
+
.enable_all()
|
|
54
|
+
.build()
|
|
55
|
+
.expect("Failed to build multi-thread runtime")
|
|
56
|
+
}
|
|
@@ -2,7 +2,7 @@ use std::time::Duration;
|
|
|
2
2
|
|
|
3
3
|
use wreq::{
|
|
4
4
|
Client,
|
|
5
|
-
tls::{
|
|
5
|
+
tls::{TlsInfo, trust::CertStore},
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
/// Certificate Store Example
|
|
@@ -39,7 +39,7 @@ use wreq::{
|
|
|
39
39
|
async fn main() -> wreq::Result<()> {
|
|
40
40
|
// Create a client with a custom certificate store using webpki-roots
|
|
41
41
|
let client = Client::builder()
|
|
42
|
-
.
|
|
42
|
+
.tls_cert_store(CertStore::from_der_certs(
|
|
43
43
|
webpki_root_certs::TLS_SERVER_ROOT_CERTS,
|
|
44
44
|
)?)
|
|
45
45
|
.build()?;
|
|
@@ -51,7 +51,7 @@ async fn main() -> wreq::Result<()> {
|
|
|
51
51
|
// Skip certificate verification for self-signed certificates
|
|
52
52
|
let client = Client::builder()
|
|
53
53
|
.tls_info(true)
|
|
54
|
-
.
|
|
54
|
+
.tls_cert_verification(false)
|
|
55
55
|
.build()?;
|
|
56
56
|
|
|
57
57
|
// Use the API you're already familiar with
|
|
@@ -63,7 +63,7 @@ async fn main() -> wreq::Result<()> {
|
|
|
63
63
|
|
|
64
64
|
// Create a client with self-signed certificate store
|
|
65
65
|
let client = Client::builder()
|
|
66
|
-
.
|
|
66
|
+
.tls_cert_store(self_signed_store)
|
|
67
67
|
.connect_timeout(Duration::from_secs(10))
|
|
68
68
|
.build()?;
|
|
69
69
|
|
|
@@ -102,12 +102,12 @@ async fn main() -> wreq::Result<()> {
|
|
|
102
102
|
.http2_options(http2)
|
|
103
103
|
.headers(headers)
|
|
104
104
|
.orig_headers(orig_headers)
|
|
105
|
-
.build();
|
|
105
|
+
.build(Default::default());
|
|
106
106
|
|
|
107
107
|
// Build a client with emulation config
|
|
108
108
|
let client = Client::builder()
|
|
109
109
|
.emulation(emulation)
|
|
110
|
-
.
|
|
110
|
+
.tls_cert_verification(false)
|
|
111
111
|
.build()?;
|
|
112
112
|
|
|
113
113
|
// Use the API you're already familiar with
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
//! ```
|
|
7
7
|
|
|
8
8
|
use futures_util::{SinkExt, StreamExt, TryStreamExt};
|
|
9
|
-
use wreq::{header, ws::message::Message};
|
|
9
|
+
use wreq::{Version, header, ws::message::Message};
|
|
10
10
|
|
|
11
11
|
#[tokio::main]
|
|
12
12
|
async fn main() -> wreq::Result<()> {
|
|
13
13
|
// Use the API you're already familiar with
|
|
14
14
|
let resp = wreq::websocket("wss://127.0.0.1:3000/ws")
|
|
15
|
-
.
|
|
15
|
+
.version(Version::HTTP_2)
|
|
16
16
|
.header(header::USER_AGENT, env!("CARGO_PKG_NAME"))
|
|
17
17
|
.read_buffer_size(1024 * 1024)
|
|
18
18
|
.send()
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
use wreq::tls::KeyLog;
|
|
1
|
+
use wreq::tls::keylog::KeyLog;
|
|
2
2
|
|
|
3
3
|
#[tokio::main]
|
|
4
4
|
async fn main() -> wreq::Result<()> {
|
|
5
5
|
// Build a client
|
|
6
6
|
let client = wreq::Client::builder()
|
|
7
|
-
.
|
|
8
|
-
.
|
|
7
|
+
.tls_keylog(KeyLog::from_file("keylog.txt"))
|
|
8
|
+
.tls_cert_verification(false)
|
|
9
9
|
.build()?;
|
|
10
10
|
|
|
11
11
|
// Use the API you're already familiar with
|