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.
Files changed (150) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +1922 -397
  3. data/LICENSE +203 -0
  4. data/README.md +19 -15
  5. data/ext/wreq_rb/Cargo.toml +4 -6
  6. data/ext/wreq_rb/src/client.rs +41 -48
  7. data/lib/wreq-rb/version.rb +1 -1
  8. data/patches/0001-add-transfer-size-tracking.patch +76 -67
  9. data/vendor/wreq/Cargo.toml +119 -71
  10. data/vendor/wreq/README.md +25 -20
  11. data/vendor/wreq/bench/http1.rs +25 -0
  12. data/vendor/wreq/bench/http1_over_tls.rs +25 -0
  13. data/vendor/wreq/bench/http2.rs +25 -0
  14. data/vendor/wreq/bench/http2_over_tls.rs +25 -0
  15. data/vendor/wreq/bench/support/bench.rs +91 -0
  16. data/vendor/wreq/bench/support/client.rs +217 -0
  17. data/vendor/wreq/bench/support/server.rs +188 -0
  18. data/vendor/wreq/bench/support.rs +56 -0
  19. data/vendor/wreq/examples/cert_store.rs +4 -4
  20. data/vendor/wreq/examples/{emulation.rs → emulate.rs} +2 -2
  21. data/vendor/wreq/examples/http2_websocket.rs +2 -2
  22. data/vendor/wreq/examples/keylog.rs +3 -3
  23. data/vendor/wreq/examples/{request_with_emulation.rs → request_with_emulate.rs} +2 -2
  24. data/vendor/wreq/examples/rt.rs +23 -0
  25. data/vendor/wreq/src/client/body.rs +23 -61
  26. data/vendor/wreq/src/client/emulate.rs +119 -0
  27. data/vendor/wreq/src/client/{http/future.rs → future.rs} +11 -32
  28. data/vendor/wreq/src/client/{http → layer}/client/pool.rs +66 -61
  29. data/vendor/wreq/src/client/{http → layer}/client.rs +416 -270
  30. data/vendor/wreq/src/client/layer/config.rs +27 -6
  31. data/vendor/wreq/src/client/layer/decoder.rs +9 -4
  32. data/vendor/wreq/src/client/layer/redirect/future.rs +6 -3
  33. data/vendor/wreq/src/client/layer/redirect.rs +4 -5
  34. data/vendor/wreq/src/client/layer/retry.rs +8 -5
  35. data/vendor/wreq/src/client/layer/timeout/body.rs +15 -6
  36. data/vendor/wreq/src/client/layer/timeout/future.rs +23 -18
  37. data/vendor/wreq/src/client/layer/timeout.rs +24 -74
  38. data/vendor/wreq/src/client/layer.rs +1 -2
  39. data/vendor/wreq/src/client/multipart.rs +137 -154
  40. data/vendor/wreq/src/client/request.rs +202 -118
  41. data/vendor/wreq/src/client/response.rs +46 -45
  42. data/vendor/wreq/src/client/upgrade.rs +15 -0
  43. data/vendor/wreq/src/client/ws.rs +73 -25
  44. data/vendor/wreq/src/client.rs +1655 -17
  45. data/vendor/wreq/src/config.rs +11 -11
  46. data/vendor/wreq/src/{client/conn → conn}/connector.rs +139 -137
  47. data/vendor/wreq/src/conn/descriptor.rs +143 -0
  48. data/vendor/wreq/src/conn/http.rs +484 -0
  49. data/vendor/wreq/src/conn/net/io.rs +75 -0
  50. data/vendor/wreq/src/conn/net/tcp/compio.rs +71 -0
  51. data/vendor/wreq/src/conn/net/tcp/tokio.rs +57 -0
  52. data/vendor/wreq/src/conn/net/tcp.rs +561 -0
  53. data/vendor/wreq/src/conn/net/uds/compio.rs +60 -0
  54. data/vendor/wreq/src/{client/conn/uds.rs → conn/net/uds/tokio.rs} +18 -12
  55. data/vendor/wreq/src/conn/net/uds.rs +11 -0
  56. data/vendor/wreq/src/conn/net.rs +130 -0
  57. data/vendor/wreq/src/{client/conn → conn}/proxy/socks.rs +2 -9
  58. data/vendor/wreq/src/{client/conn → conn}/proxy/tunnel.rs +21 -56
  59. data/vendor/wreq/src/conn/tls_info.rs +47 -0
  60. data/vendor/wreq/src/{client/conn.rs → conn.rs} +202 -54
  61. data/vendor/wreq/src/cookie.rs +302 -142
  62. data/vendor/wreq/src/dns/gai/compio.rs +77 -0
  63. data/vendor/wreq/src/dns/gai/tokio.rs +90 -0
  64. data/vendor/wreq/src/dns/gai.rs +14 -164
  65. data/vendor/wreq/src/dns/hickory.rs +16 -23
  66. data/vendor/wreq/src/dns/resolve.rs +7 -41
  67. data/vendor/wreq/src/dns.rs +90 -7
  68. data/vendor/wreq/src/error.rs +57 -31
  69. data/vendor/wreq/src/ext.rs +25 -0
  70. data/vendor/wreq/src/group.rs +211 -0
  71. data/vendor/wreq/src/header.rs +100 -112
  72. data/vendor/wreq/src/lib.rs +124 -73
  73. data/vendor/wreq/src/proxy.rs +6 -20
  74. data/vendor/wreq/src/redirect.rs +1 -1
  75. data/vendor/wreq/src/rt.rs +208 -0
  76. data/vendor/wreq/src/sync.rs +97 -98
  77. data/vendor/wreq/src/tls/compress.rs +124 -0
  78. data/vendor/wreq/src/tls/conn/ext.rs +54 -45
  79. data/vendor/wreq/src/tls/conn/service.rs +14 -18
  80. data/vendor/wreq/src/tls/conn.rs +169 -241
  81. data/vendor/wreq/src/tls/keylog.rs +68 -5
  82. data/vendor/wreq/src/tls/session.rs +205 -0
  83. data/vendor/wreq/src/tls/{x509 → trust}/identity.rs +4 -21
  84. data/vendor/wreq/src/tls/{x509/parser.rs → trust/parse.rs} +1 -1
  85. data/vendor/wreq/src/tls/{x509 → trust}/store.rs +42 -81
  86. data/vendor/wreq/src/tls/{x509.rs → trust.rs} +8 -2
  87. data/vendor/wreq/src/tls.rs +489 -25
  88. data/vendor/wreq/src/trace.rs +0 -12
  89. data/vendor/wreq/src/util.rs +1 -1
  90. data/vendor/wreq/tests/badssl.rs +10 -10
  91. data/vendor/wreq/tests/client.rs +3 -9
  92. data/vendor/wreq/tests/cookie.rs +6 -8
  93. data/vendor/wreq/tests/{emulation.rs → emulate.rs} +130 -22
  94. data/vendor/wreq/tests/multipart.rs +43 -1
  95. data/vendor/wreq/tests/proxy.rs +1 -1
  96. data/vendor/wreq/tests/support/layer.rs +1 -0
  97. metadata +49 -71
  98. data/patches/0002-add-cancel-connections.patch +0 -181
  99. data/vendor/wreq/src/client/conn/conn.rs +0 -231
  100. data/vendor/wreq/src/client/conn/http.rs +0 -1023
  101. data/vendor/wreq/src/client/conn/tls_info.rs +0 -98
  102. data/vendor/wreq/src/client/core/body/incoming.rs +0 -485
  103. data/vendor/wreq/src/client/core/body/length.rs +0 -118
  104. data/vendor/wreq/src/client/core/body.rs +0 -34
  105. data/vendor/wreq/src/client/core/common/buf.rs +0 -149
  106. data/vendor/wreq/src/client/core/common/rewind.rs +0 -141
  107. data/vendor/wreq/src/client/core/common/watch.rs +0 -76
  108. data/vendor/wreq/src/client/core/common.rs +0 -3
  109. data/vendor/wreq/src/client/core/conn/http1.rs +0 -342
  110. data/vendor/wreq/src/client/core/conn/http2.rs +0 -307
  111. data/vendor/wreq/src/client/core/conn.rs +0 -11
  112. data/vendor/wreq/src/client/core/dispatch.rs +0 -299
  113. data/vendor/wreq/src/client/core/error.rs +0 -435
  114. data/vendor/wreq/src/client/core/ext.rs +0 -201
  115. data/vendor/wreq/src/client/core/http1.rs +0 -178
  116. data/vendor/wreq/src/client/core/http2.rs +0 -483
  117. data/vendor/wreq/src/client/core/proto/h1/conn.rs +0 -988
  118. data/vendor/wreq/src/client/core/proto/h1/decode.rs +0 -1170
  119. data/vendor/wreq/src/client/core/proto/h1/dispatch.rs +0 -684
  120. data/vendor/wreq/src/client/core/proto/h1/encode.rs +0 -580
  121. data/vendor/wreq/src/client/core/proto/h1/io.rs +0 -879
  122. data/vendor/wreq/src/client/core/proto/h1/role.rs +0 -694
  123. data/vendor/wreq/src/client/core/proto/h1.rs +0 -104
  124. data/vendor/wreq/src/client/core/proto/h2/client.rs +0 -650
  125. data/vendor/wreq/src/client/core/proto/h2/ping.rs +0 -539
  126. data/vendor/wreq/src/client/core/proto/h2.rs +0 -379
  127. data/vendor/wreq/src/client/core/proto/headers.rs +0 -138
  128. data/vendor/wreq/src/client/core/proto.rs +0 -58
  129. data/vendor/wreq/src/client/core/rt/bounds.rs +0 -57
  130. data/vendor/wreq/src/client/core/rt/timer.rs +0 -150
  131. data/vendor/wreq/src/client/core/rt/tokio.rs +0 -99
  132. data/vendor/wreq/src/client/core/rt.rs +0 -25
  133. data/vendor/wreq/src/client/core/upgrade.rs +0 -267
  134. data/vendor/wreq/src/client/core.rs +0 -16
  135. data/vendor/wreq/src/client/emulation.rs +0 -161
  136. data/vendor/wreq/src/client/http/client/error.rs +0 -142
  137. data/vendor/wreq/src/client/http/client/exec.rs +0 -29
  138. data/vendor/wreq/src/client/http/client/extra.rs +0 -77
  139. data/vendor/wreq/src/client/http/client/util.rs +0 -104
  140. data/vendor/wreq/src/client/http.rs +0 -1629
  141. data/vendor/wreq/src/client/layer/config/options.rs +0 -156
  142. data/vendor/wreq/src/client/layer/cookie.rs +0 -161
  143. data/vendor/wreq/src/hash.rs +0 -143
  144. data/vendor/wreq/src/tls/conn/cache.rs +0 -123
  145. data/vendor/wreq/src/tls/conn/cert_compression.rs +0 -125
  146. data/vendor/wreq/src/tls/keylog/handle.rs +0 -64
  147. data/vendor/wreq/src/tls/options.rs +0 -464
  148. /data/vendor/wreq/src/client/{http → layer}/client/lazy.rs +0 -0
  149. /data/vendor/wreq/src/{client/conn → conn}/proxy.rs +0 -0
  150. /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::{CertStore, TlsInfo},
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
- .cert_store(CertStore::from_der_certs(
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
- .cert_verification(false)
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
- .cert_store(self_signed_store)
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
- .cert_verification(false)
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
- .force_http2()
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
- .keylog(KeyLog::from_file("keylog.txt"))
8
- .cert_verification(false)
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