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
|
@@ -6,10 +6,6 @@
|
|
|
6
6
|
//! The [`KeyLog`] enum lets you control key log behavior, either by respecting the
|
|
7
7
|
//! `SSLKEYLOGFILE` environment variable or by specifying a custom file path. Handles are cached
|
|
8
8
|
//! globally to avoid duplicate file access.
|
|
9
|
-
//!
|
|
10
|
-
//! Use [`KeyLog::handle`] to obtain a [`Handle`] for writing keys.
|
|
11
|
-
|
|
12
|
-
mod handle;
|
|
13
9
|
|
|
14
10
|
use std::{
|
|
15
11
|
borrow::Cow,
|
|
@@ -51,7 +47,7 @@ impl KeyLog {
|
|
|
51
47
|
.0
|
|
52
48
|
.ok_or_else(|| Error::new(ErrorKind::NotFound, "KeyLog: file path is not specified"))?;
|
|
53
49
|
|
|
54
|
-
let cache = GLOBAL_KEYLOG_CACHE.get_or_init(
|
|
50
|
+
let cache = GLOBAL_KEYLOG_CACHE.get_or_init(RwLock::default);
|
|
55
51
|
if let Some(handle) = cache.read().get(path.as_ref()).cloned() {
|
|
56
52
|
return Ok(handle);
|
|
57
53
|
}
|
|
@@ -97,3 +93,70 @@ where
|
|
|
97
93
|
}
|
|
98
94
|
ret
|
|
99
95
|
}
|
|
96
|
+
|
|
97
|
+
mod handle {
|
|
98
|
+
use std::{
|
|
99
|
+
fs::OpenOptions,
|
|
100
|
+
io::{Result, Write},
|
|
101
|
+
path::Path,
|
|
102
|
+
sync::{
|
|
103
|
+
Arc,
|
|
104
|
+
mpsc::{self, Sender},
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/// Handle for writing to a key log file.
|
|
109
|
+
#[derive(Debug, Clone)]
|
|
110
|
+
pub struct Handle {
|
|
111
|
+
#[allow(unused)]
|
|
112
|
+
filepath: Arc<Path>,
|
|
113
|
+
sender: Sender<String>,
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
impl Handle {
|
|
117
|
+
/// Create a new [`Handle`] with the specified path and sender.
|
|
118
|
+
pub fn new(filepath: Arc<Path>) -> Result<Self> {
|
|
119
|
+
if let Some(parent) = filepath.parent() {
|
|
120
|
+
std::fs::create_dir_all(parent)?;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let mut file = OpenOptions::new()
|
|
124
|
+
.create(true)
|
|
125
|
+
.append(true)
|
|
126
|
+
.open(&filepath)?;
|
|
127
|
+
|
|
128
|
+
let (sender, receiver) = mpsc::channel::<String>();
|
|
129
|
+
|
|
130
|
+
let _path_name = filepath.clone();
|
|
131
|
+
std::thread::spawn(move || {
|
|
132
|
+
trace!(
|
|
133
|
+
file = ?_path_name,
|
|
134
|
+
"Handle: receiver task up and running",
|
|
135
|
+
);
|
|
136
|
+
while let Ok(line) = receiver.recv() {
|
|
137
|
+
if let Err(_err) = file.write_all(line.as_bytes()) {
|
|
138
|
+
error!(
|
|
139
|
+
file = ?_path_name,
|
|
140
|
+
error = %_err,
|
|
141
|
+
"Handle: failed to write file",
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
Ok(Handle { filepath, sender })
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/// Write a line to the keylogger.
|
|
151
|
+
pub fn write(&self, line: &str) {
|
|
152
|
+
let line = format!("{line}\n");
|
|
153
|
+
if let Err(_err) = self.sender.send(line) {
|
|
154
|
+
error!(
|
|
155
|
+
file = ?self.filepath,
|
|
156
|
+
error = %_err,
|
|
157
|
+
"Handle: failed to send log line for writing",
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
//! TLS session caching and resumption.
|
|
2
|
+
//!
|
|
3
|
+
//! Handshakes are expensive. This module lets you reuse sessions to save
|
|
4
|
+
//! CPU cycles and reduce latency.
|
|
5
|
+
//!
|
|
6
|
+
//! By default, we use an in-memory LRU cache, but you can plug in your own
|
|
7
|
+
//! implementation if you're running at scale or need to share sessions
|
|
8
|
+
//! across multiple instances.
|
|
9
|
+
|
|
10
|
+
use std::{
|
|
11
|
+
borrow::Borrow,
|
|
12
|
+
collections::{HashMap, hash_map::Entry},
|
|
13
|
+
hash::{Hash, Hasher},
|
|
14
|
+
num::NonZeroUsize,
|
|
15
|
+
sync::Arc,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
use btls::ssl::{SslSession, SslVersion};
|
|
19
|
+
use lru::LruCache;
|
|
20
|
+
|
|
21
|
+
use crate::{conn::descriptor::ConnectionId, sync::Mutex, tls::TlsVersion};
|
|
22
|
+
|
|
23
|
+
/// An opaque key identifying a TLS session cache entry.
|
|
24
|
+
#[derive(Clone, PartialEq, Eq, Hash)]
|
|
25
|
+
pub struct Key(pub(super) ConnectionId);
|
|
26
|
+
|
|
27
|
+
/// A TLS session that can be stored and retrieved from a session cache.
|
|
28
|
+
#[derive(Clone)]
|
|
29
|
+
pub struct TlsSession(pub(super) SslSession);
|
|
30
|
+
|
|
31
|
+
/// A trait for cache storing and retrieving TLS sessions.
|
|
32
|
+
///
|
|
33
|
+
/// # TLS 1.3 Session Handling
|
|
34
|
+
///
|
|
35
|
+
/// For TLS 1.3 sessions, implementations **should** remove the session after
|
|
36
|
+
/// retrieval to comply with [RFC 8446 Appendix C.4](https://tools.ietf.org/html/rfc8446#appendix-C.4),
|
|
37
|
+
/// which requires that session tickets are used at most once to prevent
|
|
38
|
+
/// concurrent handshakes from reusing the same session.
|
|
39
|
+
pub trait TlsSessionCache: Send + Sync {
|
|
40
|
+
/// Store a TLS session associated with the given key.
|
|
41
|
+
fn put(&self, key: Key, session: TlsSession);
|
|
42
|
+
|
|
43
|
+
/// Retrieve a TLS session for the given key.
|
|
44
|
+
///
|
|
45
|
+
/// For TLS 1.3, the session should be removed from the cache upon retrieval
|
|
46
|
+
/// to ensure single-use semantics (see [RFC 8446 Appendix C.4]).
|
|
47
|
+
fn pop(&self, key: &Key) -> Option<TlsSession>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
impl_into_shared!(
|
|
51
|
+
/// Trait for converting types into a shared [`TlsSessionCache`].
|
|
52
|
+
///
|
|
53
|
+
/// This allows accepting bare types, `Arc<T>`, or `Arc<dyn TlsSessionCache>`.
|
|
54
|
+
pub trait IntoTlsSessionCache => TlsSessionCache
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
/// The default two-level LRU session cache.
|
|
58
|
+
///
|
|
59
|
+
/// Maintains both forward (key → sessions) and reverse (session → key) lookups
|
|
60
|
+
/// for efficient session storage, retrieval, and cleanup operations.
|
|
61
|
+
///
|
|
62
|
+
/// This is the built-in implementation of [`TlsSessionCache`] used when no
|
|
63
|
+
/// custom session store is configured.
|
|
64
|
+
pub struct LruTlsSessionCache {
|
|
65
|
+
inner: Mutex<Inner>,
|
|
66
|
+
per_host_session_capacity: usize,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
struct Inner {
|
|
70
|
+
reverse: HashMap<TlsSession, Key>,
|
|
71
|
+
per_host_sessions: HashMap<Key, LruCache<TlsSession, ()>>,
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ===== impl TlsSession =====
|
|
75
|
+
|
|
76
|
+
impl TlsSession {
|
|
77
|
+
/// Returns the TLS session ID.
|
|
78
|
+
#[inline]
|
|
79
|
+
pub fn id(&self) -> &[u8] {
|
|
80
|
+
self.0.id()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/// Returns the time at which the session was established, in seconds since the Unix epoch.
|
|
84
|
+
#[inline]
|
|
85
|
+
pub fn time(&self) -> u64 {
|
|
86
|
+
self.0.time()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/// Returns the sessions timeout, in seconds.
|
|
90
|
+
///
|
|
91
|
+
/// A session older than this time should not be used for session resumption.
|
|
92
|
+
#[inline]
|
|
93
|
+
pub fn timeout(&self) -> u32 {
|
|
94
|
+
self.0.timeout()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// Returns the TLS protocol version negotiated for this session.
|
|
98
|
+
#[inline]
|
|
99
|
+
pub fn protocol_version(&self) -> TlsVersion {
|
|
100
|
+
let version = self.0.protocol_version();
|
|
101
|
+
if version == SslVersion::SSL3 {
|
|
102
|
+
// SSLv3 (SSL 3.0) is obsolete and insecure, and is not supported by btls.
|
|
103
|
+
// This branch should never be reached in normal operation. If it is,
|
|
104
|
+
// it indicates a bug or an unsupported/legacy OpenSSL configuration.
|
|
105
|
+
unreachable!(
|
|
106
|
+
"Encountered unsupported protocol: SSLv3 (SSL 3.0) is obsolete and not accepted by btls"
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
TlsVersion(version)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
impl Eq for TlsSession {}
|
|
114
|
+
|
|
115
|
+
impl PartialEq for TlsSession {
|
|
116
|
+
#[inline]
|
|
117
|
+
fn eq(&self, other: &TlsSession) -> bool {
|
|
118
|
+
self.0.id() == other.0.id()
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
impl Hash for TlsSession {
|
|
123
|
+
#[inline]
|
|
124
|
+
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
125
|
+
self.0.id().hash(state);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
impl Borrow<[u8]> for TlsSession {
|
|
130
|
+
#[inline]
|
|
131
|
+
fn borrow(&self) -> &[u8] {
|
|
132
|
+
self.0.id()
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ===== impl LruTlsSessionCache =====
|
|
137
|
+
|
|
138
|
+
impl LruTlsSessionCache {
|
|
139
|
+
/// Creates a new [`LruTlsSessionCache`] with the given per-host capacity.
|
|
140
|
+
pub fn new(per_host_session_capacity: usize) -> Self {
|
|
141
|
+
LruTlsSessionCache {
|
|
142
|
+
inner: Mutex::new(Inner {
|
|
143
|
+
reverse: HashMap::new(),
|
|
144
|
+
per_host_sessions: HashMap::new(),
|
|
145
|
+
}),
|
|
146
|
+
per_host_session_capacity,
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
impl TlsSessionCache for LruTlsSessionCache {
|
|
152
|
+
fn put(&self, key: Key, session: TlsSession) {
|
|
153
|
+
let mut inner = self.inner.lock();
|
|
154
|
+
|
|
155
|
+
let evicted = {
|
|
156
|
+
let per_host_sessions =
|
|
157
|
+
inner
|
|
158
|
+
.per_host_sessions
|
|
159
|
+
.entry(key.clone())
|
|
160
|
+
.or_insert_with(|| {
|
|
161
|
+
NonZeroUsize::new(self.per_host_session_capacity)
|
|
162
|
+
.map_or_else(LruCache::unbounded, LruCache::new)
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Enforce per-key capacity limit by evicting the least recently used session
|
|
166
|
+
let evicted = if per_host_sessions.len() >= self.per_host_session_capacity {
|
|
167
|
+
per_host_sessions.pop_lru().map(|(s, _)| s)
|
|
168
|
+
} else {
|
|
169
|
+
None
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
per_host_sessions.put(session.clone(), ());
|
|
173
|
+
evicted
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
if let Some(evicted_session) = evicted {
|
|
177
|
+
inner.reverse.remove(&evicted_session);
|
|
178
|
+
}
|
|
179
|
+
inner.reverse.insert(session, key);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
fn pop(&self, key: &Key) -> Option<TlsSession> {
|
|
183
|
+
let mut inner = self.inner.lock();
|
|
184
|
+
let session = {
|
|
185
|
+
let per_host_sessions = inner.per_host_sessions.get_mut(key)?;
|
|
186
|
+
per_host_sessions.peek_lru()?.0.clone()
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// https://tools.ietf.org/html/rfc8446#appendix-C.4
|
|
190
|
+
// OpenSSL will remove the session from its cache after the handshake completes anyway, but
|
|
191
|
+
// this ensures that concurrent handshakes don't end up with the same session.
|
|
192
|
+
if session.protocol_version() == TlsVersion::TLS_1_3 {
|
|
193
|
+
if let Some(key) = inner.reverse.remove(&session) {
|
|
194
|
+
if let Entry::Occupied(mut entry) = inner.per_host_sessions.entry(key) {
|
|
195
|
+
entry.get_mut().pop(&session);
|
|
196
|
+
if entry.get().is_empty() {
|
|
197
|
+
entry.remove();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
Some(session)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
use
|
|
1
|
+
use btls::{
|
|
2
2
|
pkcs12::Pkcs12,
|
|
3
3
|
pkey::{PKey, Private},
|
|
4
4
|
x509::X509,
|
|
@@ -9,9 +9,9 @@ use crate::Error;
|
|
|
9
9
|
/// Represents a private key and X509 cert as a client certificate.
|
|
10
10
|
#[derive(Debug, Clone)]
|
|
11
11
|
pub struct Identity {
|
|
12
|
-
pkey: PKey<Private>,
|
|
13
|
-
cert: X509,
|
|
14
|
-
chain: Vec<X509>,
|
|
12
|
+
pub(in crate::tls) pkey: PKey<Private>,
|
|
13
|
+
pub(in crate::tls) cert: X509,
|
|
14
|
+
pub(in crate::tls) chain: Vec<X509>,
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
impl Identity {
|
|
@@ -87,23 +87,6 @@ impl Identity {
|
|
|
87
87
|
let chain = cert_chain.collect();
|
|
88
88
|
Ok(Identity { pkey, cert, chain })
|
|
89
89
|
}
|
|
90
|
-
|
|
91
|
-
pub(crate) fn add_to_tls(
|
|
92
|
-
&self,
|
|
93
|
-
connector: &mut boring2::ssl::SslConnectorBuilder,
|
|
94
|
-
) -> crate::Result<()> {
|
|
95
|
-
connector.set_certificate(&self.cert).map_err(Error::tls)?;
|
|
96
|
-
connector.set_private_key(&self.pkey).map_err(Error::tls)?;
|
|
97
|
-
for cert in self.chain.iter() {
|
|
98
|
-
// https://www.openssl.org/docs/manmaster/man3/SSL_CTX_add_extra_chain_cert.html
|
|
99
|
-
// specifies that "When sending a certificate chain, extra chain certificates are
|
|
100
|
-
// sent in order following the end entity certificate."
|
|
101
|
-
connector
|
|
102
|
-
.add_extra_chain_cert(cert.clone())
|
|
103
|
-
.map_err(Error::tls)?;
|
|
104
|
-
}
|
|
105
|
-
Ok(())
|
|
106
|
-
}
|
|
107
90
|
}
|
|
108
91
|
|
|
109
92
|
#[cfg(test)]
|
|
@@ -1,24 +1,51 @@
|
|
|
1
1
|
use std::sync::Arc;
|
|
2
2
|
|
|
3
|
-
use
|
|
4
|
-
ssl::SslConnectorBuilder,
|
|
5
|
-
x509::store::{X509Store, X509StoreBuilder},
|
|
6
|
-
};
|
|
3
|
+
use btls::x509::store::{X509Store, X509StoreBuilder};
|
|
7
4
|
|
|
8
5
|
use super::{
|
|
9
6
|
Certificate, CertificateInput,
|
|
10
|
-
|
|
7
|
+
parse::{filter_map_certs, parse_certs, parse_certs_with_stack, process_certs},
|
|
11
8
|
};
|
|
12
9
|
use crate::{Error, Result};
|
|
13
10
|
|
|
14
|
-
/// A builder for constructing a `CertStore
|
|
11
|
+
/// A builder for constructing a [`CertStore`].
|
|
15
12
|
pub struct CertStoreBuilder {
|
|
16
13
|
builder: Result<X509StoreBuilder>,
|
|
17
14
|
}
|
|
18
15
|
|
|
19
|
-
// ====== impl CertStoreBuilder ======
|
|
20
|
-
|
|
21
16
|
impl CertStoreBuilder {
|
|
17
|
+
fn parse_cert<'c, C, P>(mut self, cert: C, parser: P) -> Self
|
|
18
|
+
where
|
|
19
|
+
C: Into<CertificateInput<'c>>,
|
|
20
|
+
P: Fn(&'c [u8]) -> Result<Certificate>,
|
|
21
|
+
{
|
|
22
|
+
if let Ok(ref mut builder) = self.builder {
|
|
23
|
+
let input = cert.into();
|
|
24
|
+
let result = input
|
|
25
|
+
.with_parser(parser)
|
|
26
|
+
.and_then(|cert| builder.add_cert(cert.0).map_err(Error::tls));
|
|
27
|
+
|
|
28
|
+
if let Err(err) = result {
|
|
29
|
+
self.builder = Err(err);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
self
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
fn parse_certs<'c, I>(mut self, certs: I, parser: fn(&'c [u8]) -> Result<Certificate>) -> Self
|
|
36
|
+
where
|
|
37
|
+
I: IntoIterator,
|
|
38
|
+
I::Item: Into<CertificateInput<'c>>,
|
|
39
|
+
{
|
|
40
|
+
if let Ok(ref mut builder) = self.builder {
|
|
41
|
+
let certs = filter_map_certs(certs, parser);
|
|
42
|
+
if let Err(err) = process_certs(certs, builder) {
|
|
43
|
+
self.builder = Err(err);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
self
|
|
47
|
+
}
|
|
48
|
+
|
|
22
49
|
/// Adds a DER-encoded certificate to the certificate store.
|
|
23
50
|
#[inline]
|
|
24
51
|
pub fn add_der_cert<'c, C>(self, cert: C) -> Self
|
|
@@ -87,9 +114,9 @@ impl CertStoreBuilder {
|
|
|
87
114
|
self
|
|
88
115
|
}
|
|
89
116
|
|
|
90
|
-
/// Constructs the `CertStore
|
|
117
|
+
/// Constructs the [`CertStore`].
|
|
91
118
|
///
|
|
92
|
-
/// This method finalizes the builder and constructs the `CertStore`
|
|
119
|
+
/// This method finalizes the builder and constructs the [`CertStore`]
|
|
93
120
|
/// containing all the added certificates.
|
|
94
121
|
#[inline]
|
|
95
122
|
pub fn build(self) -> Result<CertStore> {
|
|
@@ -100,40 +127,6 @@ impl CertStoreBuilder {
|
|
|
100
127
|
}
|
|
101
128
|
}
|
|
102
129
|
|
|
103
|
-
impl CertStoreBuilder {
|
|
104
|
-
fn parse_cert<'c, C, P>(mut self, cert: C, parser: P) -> Self
|
|
105
|
-
where
|
|
106
|
-
C: Into<CertificateInput<'c>>,
|
|
107
|
-
P: Fn(&'c [u8]) -> Result<Certificate>,
|
|
108
|
-
{
|
|
109
|
-
if let Ok(ref mut builder) = self.builder {
|
|
110
|
-
let input = cert.into();
|
|
111
|
-
let result = input
|
|
112
|
-
.with_parser(parser)
|
|
113
|
-
.and_then(|cert| builder.add_cert(cert.0).map_err(Error::tls));
|
|
114
|
-
|
|
115
|
-
if let Err(err) = result {
|
|
116
|
-
self.builder = Err(err);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
self
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
fn parse_certs<'c, I>(mut self, certs: I, parser: fn(&'c [u8]) -> Result<Certificate>) -> Self
|
|
123
|
-
where
|
|
124
|
-
I: IntoIterator,
|
|
125
|
-
I::Item: Into<CertificateInput<'c>>,
|
|
126
|
-
{
|
|
127
|
-
if let Ok(ref mut builder) = self.builder {
|
|
128
|
-
let certs = filter_map_certs(certs, parser);
|
|
129
|
-
if let Err(err) = process_certs(certs, builder) {
|
|
130
|
-
self.builder = Err(err);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
self
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
130
|
/// A thread-safe certificate store for TLS connections.
|
|
138
131
|
///
|
|
139
132
|
/// [`CertStore`] manages a collection of trusted certificates used for verifying peer identities.
|
|
@@ -148,12 +141,10 @@ impl CertStoreBuilder {
|
|
|
148
141
|
/// [`Rc`]: std::rc::Rc
|
|
149
142
|
/// [`Arc`]: std::sync::Arc
|
|
150
143
|
#[derive(Clone)]
|
|
151
|
-
pub struct CertStore(Arc<X509Store>);
|
|
152
|
-
|
|
153
|
-
// ====== impl CertStore ======
|
|
144
|
+
pub struct CertStore(pub(in crate::tls) Arc<X509Store>);
|
|
154
145
|
|
|
155
146
|
impl CertStore {
|
|
156
|
-
/// Creates a new `CertStoreBuilder
|
|
147
|
+
/// Creates a new [`CertStoreBuilder`].
|
|
157
148
|
#[inline]
|
|
158
149
|
pub fn builder() -> CertStoreBuilder {
|
|
159
150
|
CertStoreBuilder {
|
|
@@ -161,7 +152,7 @@ impl CertStore {
|
|
|
161
152
|
}
|
|
162
153
|
}
|
|
163
154
|
|
|
164
|
-
/// Creates a new `CertStore` from a collection of DER-encoded certificates.
|
|
155
|
+
/// Creates a new [`CertStore`] from a collection of DER-encoded certificates.
|
|
165
156
|
#[inline]
|
|
166
157
|
pub fn from_der_certs<'c, C>(certs: C) -> Result<CertStore>
|
|
167
158
|
where
|
|
@@ -173,7 +164,7 @@ impl CertStore {
|
|
|
173
164
|
.map(CertStore)
|
|
174
165
|
}
|
|
175
166
|
|
|
176
|
-
/// Creates a new `CertStore` from a collection of PEM-encoded certificates.
|
|
167
|
+
/// Creates a new [`CertStore`] from a collection of PEM-encoded certificates.
|
|
177
168
|
#[inline]
|
|
178
169
|
pub fn from_pem_certs<'c, C>(certs: C) -> Result<CertStore>
|
|
179
170
|
where
|
|
@@ -185,7 +176,7 @@ impl CertStore {
|
|
|
185
176
|
.map(CertStore)
|
|
186
177
|
}
|
|
187
178
|
|
|
188
|
-
/// Creates a new `CertStore` from a PEM-encoded certificate stack.
|
|
179
|
+
/// Creates a new [`CertStore`] from a PEM-encoded certificate stack.
|
|
189
180
|
#[inline]
|
|
190
181
|
pub fn from_pem_stack<C>(certs: C) -> Result<CertStore>
|
|
191
182
|
where
|
|
@@ -196,33 +187,3 @@ impl CertStore {
|
|
|
196
187
|
.map(CertStore)
|
|
197
188
|
}
|
|
198
189
|
}
|
|
199
|
-
|
|
200
|
-
impl CertStore {
|
|
201
|
-
#[inline]
|
|
202
|
-
pub(crate) fn add_to_tls(&self, tls: &mut SslConnectorBuilder) {
|
|
203
|
-
tls.set_cert_store_ref(&self.0);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
impl Default for CertStore {
|
|
208
|
-
fn default() -> Self {
|
|
209
|
-
#[cfg(feature = "webpki-roots")]
|
|
210
|
-
static LOAD_CERTS: std::sync::LazyLock<CertStore> = std::sync::LazyLock::new(|| {
|
|
211
|
-
CertStore::builder()
|
|
212
|
-
.add_der_certs(webpki_root_certs::TLS_SERVER_ROOT_CERTS)
|
|
213
|
-
.build()
|
|
214
|
-
.expect("failed to load default cert store")
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
#[cfg(not(feature = "webpki-roots"))]
|
|
218
|
-
{
|
|
219
|
-
CertStore::builder()
|
|
220
|
-
.set_default_paths()
|
|
221
|
-
.build()
|
|
222
|
-
.expect("failed to load default cert store")
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
#[cfg(feature = "webpki-roots")]
|
|
226
|
-
LOAD_CERTS.clone()
|
|
227
|
-
}
|
|
228
|
-
}
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
//! TLS Trust and Identity management.
|
|
2
|
+
//!
|
|
3
|
+
//! Handles server certificate verification, mTLS identity, and CA
|
|
4
|
+
//! bundle management. Provides DER/PEM parsing for BoringSSL and
|
|
5
|
+
//! supports both system and custom trust stores.
|
|
6
|
+
|
|
1
7
|
mod identity;
|
|
2
|
-
mod
|
|
8
|
+
mod parse;
|
|
3
9
|
mod store;
|
|
4
10
|
|
|
5
|
-
use
|
|
11
|
+
use btls::x509::X509;
|
|
6
12
|
|
|
7
13
|
pub use self::{
|
|
8
14
|
identity::Identity,
|