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,208 @@
1
+ //! Runtime components — executor and timer abstractions.
2
+ //!
3
+ //! This module provides [`Executor`] and [`Timer`], the two runtime primitives
4
+ //! used by the HTTP client for spawning background tasks and driving timeouts.
5
+ //!
6
+ //! # Feature flags
7
+ //!
8
+ //! At least one of the following features must be enabled:
9
+ //!
10
+ //! - `tokio-rt` — uses [tokio] as the underlying runtime (default).
11
+ //! - `compio-rt` — uses [compio] as the underlying runtime.
12
+ //!
13
+ //! When both are enabled, `tokio-rt` takes precedence for both [`Executor`]
14
+ //! and [`Timer`]. When neither is enabled, [`Executor::default`] and
15
+ //! [`Timer::default`] return empty placeholders that panic on use, so a
16
+ //! runtime feature flag **must** be active in practice.
17
+ //!
18
+ //! [tokio]: https://docs.rs/tokio
19
+ //! [compio]: https://docs.rs/compio
20
+
21
+ use std::{
22
+ future::Future,
23
+ pin::Pin,
24
+ sync::Arc,
25
+ time::{Duration, Instant},
26
+ };
27
+
28
+ use wreq_proto::rt::{self, Sleep, Time};
29
+
30
+ /// A heap-allocated, type-erased future that is [`Send`] and resolves to `()`.
31
+ ///
32
+ /// This is the concrete future type passed to [`rt::Executor::execute`] by the
33
+ /// client's background task machinery. Callers do not need to construct this
34
+ /// type directly; the [`rt::Executor<F>`] blanket implementation boxes and
35
+ /// pins any qualifying `F` automatically.
36
+ pub type BoxSendFuture = Pin<Box<dyn Future<Output = ()> + Send>>;
37
+
38
+ /// A handle to an async task executor.
39
+ ///
40
+ /// `Executor` is used by the HTTP client to spawn background tasks such as
41
+ /// connection-pool cleanup and keep-alive management, without coupling the
42
+ /// client to a specific async runtime.
43
+ ///
44
+ /// # Default behavior
45
+ ///
46
+ /// [`Executor::default`] picks the runtime-appropriate implementation based
47
+ /// on the active feature flags:
48
+ ///
49
+ /// | Feature flags active | Executor |
50
+ /// |-----------------------------------|-------------------|
51
+ /// | `tokio-rt` only | `TokioExecutor` |
52
+ /// | `compio-rt` only | `CompioExecutor` |
53
+ /// | both `tokio-rt` and `compio-rt` | `TokioExecutor` |
54
+ /// | neither | empty (panics) |
55
+ #[derive(Clone)]
56
+ pub struct Executor(Arc<dyn rt::Executor<BoxSendFuture> + Send + Sync>);
57
+
58
+ // ===== impl Executor =====
59
+
60
+ impl Executor {
61
+ /// Creates an [`Executor`] backed by a custom implementation.
62
+ ///
63
+ /// The value is wrapped in an [`Arc`] and type-erased, so the resulting
64
+ /// handle is cheap to clone and safe to share across threads.
65
+ #[inline]
66
+ pub fn new<E>(exec: E) -> Self
67
+ where
68
+ E: rt::Executor<BoxSendFuture> + Send + Sync + 'static,
69
+ {
70
+ Executor(Arc::new(exec))
71
+ }
72
+ }
73
+
74
+ impl<Fut> rt::Executor<Fut> for Executor
75
+ where
76
+ Fut: Future<Output = ()> + Send + 'static,
77
+ {
78
+ /// Spawns `fut` on the underlying executor.
79
+ ///
80
+ /// The future is boxed and pinned internally, so any `F` satisfying the
81
+ /// bounds can be passed without the caller needing to allocate first.
82
+ #[track_caller]
83
+ #[inline(always)]
84
+ fn execute(&self, fut: Fut) {
85
+ self.0.execute(Box::pin(fut))
86
+ }
87
+ }
88
+
89
+ impl Default for Executor {
90
+ /// Returns the runtime-appropriate default executor.
91
+ ///
92
+ /// See the [type-level documentation][Executor] for the feature-flag
93
+ /// selection table.
94
+ #[inline]
95
+ fn default() -> Self {
96
+ if_tokio_rt!(block: {
97
+ return Executor(Arc::new(wreq_rt::rt::tokio::TokioExecutor::new()))
98
+ });
99
+
100
+ if_compio_rt!(block: {
101
+ return Executor(Arc::new(wreq_rt::rt::compio::CompioExecutor::new()))
102
+ });
103
+
104
+ if_all_rt!(block: {
105
+ return Executor(Arc::new(wreq_rt::rt::tokio::TokioExecutor::new()))
106
+ });
107
+
108
+ if_no_rt!(block:{
109
+ panic!(
110
+ "no async runtime feature enabled; at least one of `tokio-rt` or `compio-rt` must be active"
111
+ );
112
+ });
113
+ }
114
+ }
115
+
116
+ // ===== Timer =====
117
+
118
+ /// A handle to an async timer.
119
+ ///
120
+ /// `Timer` is used by the HTTP client to drive request and connection timeouts,
121
+ /// as well as the connection pool's idle-expiry loop. It wraps an
122
+ /// [`rt::Timer`] implementation in a cheap-to-clone, type-erased handle.
123
+ ///
124
+ /// # Default behavior
125
+ ///
126
+ /// [`Timer::default`] picks the runtime-appropriate implementation based on
127
+ /// the active feature flags:
128
+ ///
129
+ /// | Feature flags active | Timer |
130
+ /// |-----------------------------------|-----------------|
131
+ /// | `tokio-rt` only | `TokioTimer` |
132
+ /// | `compio-rt` only | `CompioTimer` |
133
+ /// | both `tokio-rt` and `compio-rt` | `TokioTimer` |
134
+ /// | neither | empty (panics) |
135
+ #[derive(Clone)]
136
+ pub struct Timer(Time);
137
+
138
+ // ===== impl Timer =====
139
+
140
+ impl Timer {
141
+ /// Creates a [`Timer`] backed by a custom implementation.
142
+ #[inline]
143
+ pub fn new<M>(timer: M) -> Self
144
+ where
145
+ M: rt::Timer + Send + Sync + 'static,
146
+ {
147
+ Timer(Time::Timer(Arc::new(timer)))
148
+ }
149
+
150
+ #[cfg(test)]
151
+ #[doc(hidden)]
152
+ pub fn empty() -> Self {
153
+ Timer(Time::Empty)
154
+ }
155
+
156
+ /// Returns `true` if no timer implementation has been configured.
157
+ #[inline]
158
+ pub fn is_empty(&self) -> bool {
159
+ matches!(self.0, Time::Empty)
160
+ }
161
+ }
162
+
163
+ impl Default for Timer {
164
+ #[inline]
165
+ fn default() -> Self {
166
+ if_tokio_rt!(block: {
167
+ return Timer(rt::Time::Timer(Arc::new(wreq_rt::rt::tokio::TokioTimer::new())))
168
+ });
169
+
170
+ if_compio_rt!(block: {
171
+ return Timer(rt::Time::Timer(Arc::new(wreq_rt::rt::compio::CompioTimer::new())))
172
+ });
173
+
174
+ if_all_rt!(block: {
175
+ return Timer(rt::Time::Timer(Arc::new(wreq_rt::rt::tokio::TokioTimer::new())))
176
+ });
177
+
178
+ if_no_rt!(block: {
179
+ Timer(Time::Empty)
180
+ })
181
+ }
182
+ }
183
+
184
+ impl rt::Timer for Timer {
185
+ /// Returns a future that resolves after `duration`.
186
+ #[inline]
187
+ fn sleep(&self, duration: Duration) -> Pin<Box<dyn Sleep>> {
188
+ self.0.sleep(duration)
189
+ }
190
+
191
+ /// Returns the current time according to the underlying runtime.
192
+ #[inline]
193
+ fn now(&self) -> Instant {
194
+ self.0.now()
195
+ }
196
+
197
+ /// Returns a future that resolves at `deadline`.
198
+ #[inline]
199
+ fn sleep_until(&self, deadline: Instant) -> Pin<Box<dyn Sleep>> {
200
+ self.0.sleep_until(deadline)
201
+ }
202
+
203
+ /// Resets an in-flight sleep future to fire at `new_deadline` instead.
204
+ #[inline]
205
+ fn reset(&self, sleep: &mut Pin<Box<dyn Sleep>>, new_deadline: Instant) {
206
+ self.0.reset(sleep, new_deadline)
207
+ }
208
+ }
@@ -5,125 +5,124 @@
5
5
  //!
6
6
  //! This is useful in high-availability systems where panic recovery is done externally,
7
7
  //! or poisoning is not meaningful in context.
8
-
9
- use std::{
10
- ops::{Deref, DerefMut},
11
- sync,
12
- };
13
-
14
- /// A [`Mutex`] that never poisons and has the same interface as [`std::sync::Mutex`].
15
- pub struct Mutex<T: ?Sized>(sync::Mutex<T>);
16
-
17
- impl<T> Mutex<T> {
18
- /// Like [`std::sync::Mutex::new`].
19
- #[inline]
20
- pub fn new(t: T) -> Mutex<T> {
21
- Mutex(sync::Mutex::new(t))
22
- }
23
- }
24
-
25
- impl<T: ?Sized> Mutex<T> {
26
- /// Like [`std::sync::Mutex::lock`].
27
- #[inline]
28
- pub fn lock(&self) -> MutexGuard<'_, T> {
29
- MutexGuard(self.0.lock().unwrap_or_else(|e| e.into_inner()))
8
+ //!
9
+ //! ## Implementation
10
+ //! - When the `parking_lot` feature is enabled, it uses [`parking_lot::Mutex`] and
11
+ //! [`parking_lot::RwLock`].
12
+ //! - Otherwise, it wraps [`std::sync::Mutex`] and [`std::sync::RwLock`], using `.unwrap_or_else(|e|
13
+ //! e.into_inner())` to silently recover from poisoning.
14
+
15
+ #[cfg(feature = "parking_lot")]
16
+ pub use parking_lot::*;
17
+
18
+ #[cfg(not(feature = "parking_lot"))]
19
+ pub use self::std::*;
20
+
21
+ #[cfg(not(feature = "parking_lot"))]
22
+ mod std {
23
+ use std::{
24
+ ops::{Deref, DerefMut},
25
+ sync,
26
+ };
27
+
28
+ /// A [`Mutex`] that never poisons and has the same interface as [`std::sync::Mutex`].
29
+ ///
30
+ /// See [`crate::sync`] for more details.
31
+ #[derive(Debug)]
32
+ pub struct Mutex<T: ?Sized>(sync::Mutex<T>);
33
+
34
+ impl<T> Mutex<T> {
35
+ /// Like [`std::sync::Mutex::new`].
36
+ #[inline]
37
+ pub fn new(t: T) -> Mutex<T> {
38
+ Mutex(sync::Mutex::new(t))
39
+ }
30
40
  }
31
- }
32
41
 
33
- impl<T> Default for Mutex<T>
34
- where
35
- T: Default,
36
- {
37
- #[inline]
38
- fn default() -> Self {
39
- Mutex::new(T::default())
42
+ impl<T: ?Sized> Mutex<T> {
43
+ /// Like [`std::sync::Mutex::lock`].
44
+ #[inline]
45
+ pub fn lock<'a>(&'a self) -> MutexGuard<'a, T> {
46
+ MutexGuard(self.0.lock().unwrap_or_else(|e| e.into_inner()))
47
+ }
40
48
  }
41
- }
42
49
 
43
- /// Like [`std::sync::MutexGuard`].
44
- #[must_use]
45
- pub struct MutexGuard<'a, T: ?Sized + 'a>(sync::MutexGuard<'a, T>);
50
+ /// Like [`std::sync::MutexGuard`].
51
+ #[must_use]
52
+ pub struct MutexGuard<'a, T: ?Sized + 'a>(sync::MutexGuard<'a, T>);
46
53
 
47
- impl<T: ?Sized> Deref for MutexGuard<'_, T> {
48
- type Target = T;
54
+ impl<'a, T: ?Sized> Deref for MutexGuard<'a, T> {
55
+ type Target = T;
49
56
 
50
- #[inline]
51
- fn deref(&self) -> &T {
52
- self.0.deref()
57
+ #[inline]
58
+ fn deref(&self) -> &T {
59
+ self.0.deref()
60
+ }
53
61
  }
54
- }
55
62
 
56
- impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
57
- #[inline]
58
- fn deref_mut(&mut self) -> &mut T {
59
- self.0.deref_mut()
63
+ impl<'a, T: ?Sized> DerefMut for MutexGuard<'a, T> {
64
+ #[inline]
65
+ fn deref_mut(&mut self) -> &mut T {
66
+ self.0.deref_mut()
67
+ }
60
68
  }
61
- }
62
69
 
63
- /// A [`RwLock`] that never poisons and has the same interface as [`std::sync::RwLock`].
64
- pub struct RwLock<T: ?Sized>(sync::RwLock<T>);
65
-
66
- impl<T> RwLock<T> {
67
- /// Like [`std::sync::RwLock::new`].
68
- #[inline]
69
- pub fn new(t: T) -> RwLock<T> {
70
- RwLock(sync::RwLock::new(t))
71
- }
72
- }
73
-
74
- impl<T: ?Sized> RwLock<T> {
75
- /// Like [`std::sync::RwLock::read`].
76
- #[inline]
77
- pub fn read(&self) -> RwLockReadGuard<'_, T> {
78
- RwLockReadGuard(self.0.read().unwrap_or_else(|e| e.into_inner()))
79
- }
80
-
81
- /// Like [`std::sync::RwLock::write`].
82
- #[inline]
83
- pub fn write(&self) -> RwLockWriteGuard<'_, T> {
84
- RwLockWriteGuard(self.0.write().unwrap_or_else(|e| e.into_inner()))
70
+ impl<T: Default> Default for Mutex<T> {
71
+ fn default() -> Self {
72
+ Mutex(Default::default())
73
+ }
85
74
  }
86
- }
87
75
 
88
- impl<T> Default for RwLock<T>
89
- where
90
- T: Default,
91
- {
92
- #[inline]
93
- fn default() -> Self {
94
- RwLock::new(T::default())
76
+ /// A [`RwLock`] that never poisons and has the same interface as [`std::sync::RwLock`].
77
+ ///
78
+ /// See [`crate::sync`] for more details.
79
+ #[derive(Debug, Default)]
80
+ pub struct RwLock<T: ?Sized>(sync::RwLock<T>);
81
+
82
+ impl<T: ?Sized> RwLock<T> {
83
+ /// Like [`std::sync::RwLock::read`].
84
+ #[inline]
85
+ pub fn read<'a>(&'a self) -> RwLockReadGuard<'a, T> {
86
+ RwLockReadGuard(self.0.read().unwrap_or_else(|e| e.into_inner()))
87
+ }
88
+
89
+ /// Like [`std::sync::RwLock::write`].
90
+ #[inline]
91
+ pub fn write<'a>(&'a self) -> RwLockWriteGuard<'a, T> {
92
+ RwLockWriteGuard(self.0.write().unwrap_or_else(|e| e.into_inner()))
93
+ }
95
94
  }
96
- }
97
95
 
98
- /// Like [`std::sync::RwLockReadGuard`].
99
- #[must_use]
100
- pub struct RwLockReadGuard<'a, T: ?Sized + 'a>(sync::RwLockReadGuard<'a, T>);
96
+ /// Like [`std::sync::RwLockReadGuard`].
97
+ #[must_use]
98
+ pub struct RwLockReadGuard<'a, T: ?Sized + 'a>(sync::RwLockReadGuard<'a, T>);
101
99
 
102
- impl<T: ?Sized> Deref for RwLockReadGuard<'_, T> {
103
- type Target = T;
100
+ impl<'a, T: ?Sized> Deref for RwLockReadGuard<'a, T> {
101
+ type Target = T;
104
102
 
105
- #[inline]
106
- fn deref(&self) -> &T {
107
- self.0.deref()
103
+ #[inline]
104
+ fn deref(&self) -> &T {
105
+ self.0.deref()
106
+ }
108
107
  }
109
- }
110
108
 
111
- /// Like [`std::sync::RwLockWriteGuard`].
112
- #[must_use]
113
- pub struct RwLockWriteGuard<'a, T: ?Sized + 'a>(sync::RwLockWriteGuard<'a, T>);
109
+ /// Like [`std::sync::RwLockWriteGuard`].
110
+ #[must_use]
111
+ pub struct RwLockWriteGuard<'a, T: ?Sized + 'a>(sync::RwLockWriteGuard<'a, T>);
114
112
 
115
- impl<T: ?Sized> Deref for RwLockWriteGuard<'_, T> {
116
- type Target = T;
113
+ impl<'a, T: ?Sized> Deref for RwLockWriteGuard<'a, T> {
114
+ type Target = T;
117
115
 
118
- #[inline]
119
- fn deref(&self) -> &T {
120
- self.0.deref()
116
+ #[inline]
117
+ fn deref(&self) -> &T {
118
+ self.0.deref()
119
+ }
121
120
  }
122
- }
123
121
 
124
- impl<T: ?Sized> DerefMut for RwLockWriteGuard<'_, T> {
125
- #[inline]
126
- fn deref_mut(&mut self) -> &mut T {
127
- self.0.deref_mut()
122
+ impl<'a, T: ?Sized> DerefMut for RwLockWriteGuard<'a, T> {
123
+ #[inline]
124
+ fn deref_mut(&mut self) -> &mut T {
125
+ self.0.deref_mut()
126
+ }
128
127
  }
129
128
  }
@@ -0,0 +1,124 @@
1
+ //! TLS certificate compression [RFC 8879](https://datatracker.ietf.org/doc/html/rfc8879).
2
+ //!
3
+ //! Reduces handshake latency by compressing certificate chains.
4
+ //! Supports Zlib, Brotli, and Zstd algorithms to minimize bytes-on-wire
5
+ //! and fit within the initial congestion window.
6
+
7
+ use std::{fmt::Debug, io};
8
+
9
+ use btls::{
10
+ error::ErrorStack,
11
+ ssl::{self, SslConnectorBuilder},
12
+ };
13
+ use btls_sys as ffi;
14
+ // Re-export the `CertificateCompressionAlgorithm` enum for users of this module.
15
+ pub use ssl::CertificateCompressionAlgorithm;
16
+
17
+ /// Certificate compression or decompression.
18
+ ///
19
+ /// Wraps a function pointer or closure that processes certificate data.
20
+ #[allow(clippy::type_complexity)]
21
+ pub enum Codec {
22
+ /// Function pointer.
23
+ Pointer(fn(&[u8], &mut dyn io::Write) -> io::Result<()>),
24
+ /// Closure or function object.
25
+ Dynamic(Box<dyn Fn(&[u8], &mut dyn io::Write) -> io::Result<()> + Send + Sync>),
26
+ }
27
+
28
+ /// Trait for TLS certificate compression implementations.
29
+ ///
30
+ /// Provides methods for compressing and decompressing certificate data,
31
+ /// as well as identifying the algorithm in use.
32
+ ///
33
+ /// See [RFC 8879, §3](https://www.rfc-editor.org/rfc/rfc8879.html#name-compression-algorithms)
34
+ /// for the list of IANA-assigned compression algorithm identifiers.
35
+ pub trait CertificateCompressor: Debug + Sync + Send + 'static {
36
+ /// Returns the [`Codec`] used to compress certificate chains for this algorithm.
37
+ fn compress(&self) -> Codec;
38
+
39
+ /// Returns the [`Codec`] used to decompress certificate chains for this algorithm.
40
+ fn decompress(&self) -> Codec;
41
+
42
+ /// Returns the IANA-assigned identifier of the compression algorithm.
43
+ fn algorithm(&self) -> CertificateCompressionAlgorithm;
44
+ }
45
+
46
+ struct Compressor<const ALGORITHM: i32> {
47
+ compress: Codec,
48
+ decompress: Codec,
49
+ }
50
+
51
+ // ===== impl Codec =====
52
+
53
+ impl Codec {
54
+ #[inline]
55
+ fn call(&self, input: &[u8], output: &mut dyn io::Write) -> io::Result<()> {
56
+ match self {
57
+ Codec::Pointer(func) => func(input, output),
58
+ Codec::Dynamic(closure) => closure(input, output),
59
+ }
60
+ }
61
+ }
62
+
63
+ // ===== impl Compressor =====
64
+
65
+ impl<const ALGORITHM: i32> ssl::CertificateCompressor for Compressor<ALGORITHM> {
66
+ const ALGORITHM: CertificateCompressionAlgorithm = match ALGORITHM {
67
+ ffi::TLSEXT_cert_compression_zlib => CertificateCompressionAlgorithm::ZLIB,
68
+ ffi::TLSEXT_cert_compression_brotli => CertificateCompressionAlgorithm::BROTLI,
69
+ ffi::TLSEXT_cert_compression_zstd => CertificateCompressionAlgorithm::ZSTD,
70
+ _ => unreachable!(),
71
+ };
72
+ const CAN_COMPRESS: bool = true;
73
+ const CAN_DECOMPRESS: bool = true;
74
+
75
+ #[inline]
76
+ fn compress<W>(&self, input: &[u8], output: &mut W) -> io::Result<()>
77
+ where
78
+ W: io::Write,
79
+ {
80
+ self.compress.call(input, output)
81
+ }
82
+
83
+ #[inline]
84
+ fn decompress<W>(&self, input: &[u8], output: &mut W) -> io::Result<()>
85
+ where
86
+ W: io::Write,
87
+ {
88
+ self.decompress.call(input, output)
89
+ }
90
+ }
91
+
92
+ /// Register a certificate compressor with the given [`SslConnectorBuilder`].
93
+ pub(super) fn register(
94
+ compressor: &dyn CertificateCompressor,
95
+ builder: &mut SslConnectorBuilder,
96
+ ) -> Result<(), ErrorStack> {
97
+ match compressor.algorithm() {
98
+ CertificateCompressionAlgorithm::ZLIB => {
99
+ builder.add_certificate_compression_algorithm(Compressor::<
100
+ { ffi::TLSEXT_cert_compression_zlib },
101
+ > {
102
+ compress: compressor.compress(),
103
+ decompress: compressor.decompress(),
104
+ })
105
+ }
106
+ CertificateCompressionAlgorithm::BROTLI => {
107
+ builder.add_certificate_compression_algorithm(Compressor::<
108
+ { ffi::TLSEXT_cert_compression_brotli },
109
+ > {
110
+ compress: compressor.compress(),
111
+ decompress: compressor.decompress(),
112
+ })
113
+ }
114
+ CertificateCompressionAlgorithm::ZSTD => {
115
+ builder.add_certificate_compression_algorithm(Compressor::<
116
+ { ffi::TLSEXT_cert_compression_zstd },
117
+ > {
118
+ compress: compressor.compress(),
119
+ decompress: compressor.decompress(),
120
+ })
121
+ }
122
+ _ => unreachable!(),
123
+ }
124
+ }