quinn_boring/
session_cache.rs

1use crate::error::Result;
2use crate::{Error, QuicSslSession};
3use boring::ssl::{SslContextRef, SslSession};
4use bytes::{Buf, BufMut, Bytes, BytesMut};
5use lru::LruCache;
6use quinn_proto::{transport_parameters::TransportParameters, Side};
7use std::num::NonZeroUsize;
8use std::sync::Mutex;
9
10/// A client-side Session cache for the BoringSSL crypto provider.
11pub trait SessionCache: Send + Sync {
12    /// Adds the given value to the session cache.
13    fn put(&self, key: Bytes, value: Bytes);
14
15    /// Returns the cached session, if it exists.
16    fn get(&self, key: Bytes) -> Option<Bytes>;
17
18    /// Removes the cached session, if it exists.
19    fn remove(&self, key: Bytes);
20
21    /// Removes all entries from the cache.
22    fn clear(&self);
23}
24
25/// A utility for combining an [SslSession] and server [TransportParameters] as a
26/// [SessionCache] entry.
27pub struct Entry {
28    pub session: SslSession,
29    pub params: TransportParameters,
30}
31
32impl Entry {
33    /// Encodes this [Entry] into a [SessionCache] value.
34    pub fn encode(&self) -> Result<Bytes> {
35        let mut out = BytesMut::with_capacity(2048);
36
37        // Split the buffer in two: the length prefix buffer and the encoded session buffer.
38        // This will be O(1) as both will refer to the same underlying buffer.
39        let mut encoded = out.split_off(8);
40
41        // Store the session in the second buffer.
42        self.session.encode(&mut encoded)?;
43
44        // Go back and write the length to the first buffer.
45        out.put_u64(encoded.len() as u64);
46
47        // Unsplit to merge the two buffers back together. This will be O(1) since
48        // the buffers are already contiguous in memory.
49        out.unsplit(encoded);
50
51        // Now add the transport parameters.
52        out.reserve(128);
53        let mut encoded = out.split_off(out.len() + 8);
54        self.params.write(&mut encoded);
55        out.put_u64(encoded.len() as u64);
56        out.unsplit(encoded);
57
58        Ok(out.freeze())
59    }
60
61    /// Decodes a [SessionCache] value into an [Entry].
62    pub fn decode(ctx: &SslContextRef, mut encoded: Bytes) -> Result<Self> {
63        // Decode the session.
64        let len = encoded.get_u64() as usize;
65        let mut encoded_session = encoded.split_to(len);
66        let session = SslSession::decode(ctx, &mut encoded_session)?;
67
68        // Decode the transport parameters.
69        let len = encoded.get_u64() as usize;
70        let mut encoded_params = encoded.split_to(len);
71        let params = TransportParameters::read(Side::Client, &mut encoded_params).map_err(|e| {
72            Error::invalid_input(format!(
73                "failed parsing cached transport parameters: {:?}",
74                e
75            ))
76        })?;
77
78        Ok(Self { session, params })
79    }
80}
81
82/// A [SessionCache] implementation that will never cache anything. Requires no storage.
83pub struct NoSessionCache;
84
85impl SessionCache for NoSessionCache {
86    fn put(&self, _: Bytes, _: Bytes) {}
87
88    fn get(&self, _: Bytes) -> Option<Bytes> {
89        None
90    }
91
92    fn remove(&self, _: Bytes) {}
93
94    fn clear(&self) {}
95}
96
97pub struct SimpleCache {
98    cache: Mutex<LruCache<Bytes, Bytes>>,
99}
100
101impl SimpleCache {
102    pub fn new(num_entries: usize) -> Self {
103        SimpleCache {
104            cache: Mutex::new(LruCache::new(NonZeroUsize::new(num_entries).unwrap())),
105        }
106    }
107}
108
109impl SessionCache for SimpleCache {
110    fn put(&self, key: Bytes, value: Bytes) {
111        let _ = self.cache.lock().unwrap().put(key, value);
112    }
113
114    fn get(&self, key: Bytes) -> Option<Bytes> {
115        self.cache.lock().unwrap().get(&key).cloned()
116    }
117
118    fn remove(&self, key: Bytes) {
119        let _ = self.cache.lock().unwrap().pop(&key);
120    }
121
122    fn clear(&self) {
123        self.cache.lock().unwrap().clear()
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use crate::session_cache::Entry;
130    use crate::ClientConfig;
131    use bytes::{BufMut, BytesMut};
132    use hex_literal::hex;
133
134    #[test]
135    fn entry_encoding() {
136        let encoded = {
137            // Captured output from integration tests.
138            let encoded_session = hex!("30820412020101020203040402130104206ed381170bbf75"
139                "7901238adcd0a96ee46d1642775001abf602f69484510419d904201f66fb5b215a4f3a5fb5251c9a9a"
140                "17cad88582361f0042faf2a000eb303f42e4a106020463e53fbea205020302a300a382015630820152"
141                "3081f9a003020102020900b06e4d934b5c5d0d300a06082a8648ce3d0403023021311f301d06035504"
142                "030c16726367656e2073656c66207369676e656420636572743020170d373530313031303030303030"
143                "5a180f34303936303130313030303030305a3021311f301d06035504030c16726367656e2073656c66"
144                "207369676e656420636572743059301306072a8648ce3d020106082a8648ce3d030107034200047582"
145                "ef451a59ecae9cb170d4959664eb5631696e553f20df7db5f7cb59f550d67b795738145cf4bcde0e45"
146                "4d0f3bd8d6a2510c75cc66ccaedf4a1340d6166c4ea318301630140603551d11040d300b82096c6f63"
147                "616c686f7374300a06082a8648ce3d0403020348003045022100a46020ca34d0e8b7d79e4894d5c97f"
148                "0eb72962a42cce0d59b8e83817db2216e302205993ee4d874d6d94e32c001354d5ad17959561ac9856"
149                "5fc58abcb1860d2ca3f8a4020400aa81b30481b05c0ece9d9fe026ff4d507ca869cac8734184a0b12e"
150                "18c7551a8612b0de5e409a6f2c5f1fde44ca8ebf6db38e663584a0f9196fa420f38490edf53f553ff9"
151                "cdeb010fb86bebe050289a9e2af41aa6046b5f82d835921f6cca8777085d5dc6c662201331d6ac40bb"
152                "65f11436cf18f0da48e0049ea5aab7e43fc1ba8bb784cbb6248ed26aae2a3fb3d487a040660b7057b3"
153                "a16dd517ce22f3d4d80f2d994520cf1016cf79dad8fb763319b61b9d7abd8278b20302011db3820171"
154                "3082016d30820112a0030201020208383312a7a721d5a4300a06082a8648ce3d0403023021311f301d"
155                "06035504030c16726367656e2073656c66207369676e656420636572743020170d3735303130313030"
156                "303030305a180f34303936303130313030303030305a3021311f301d06035504030c16726367656e20"
157                "73656c66207369676e656420636572743059301306072a8648ce3d020106082a8648ce3d0301070342"
158                "000465ed18244bdfe0b42219d0277c4984a57723a5391d41b658ec6eb1c20d3fb4d0239835d163c158"
159                "88a39e3791b738b24a1b47b35a3c16f9e06a2773495bcc43bca3323030301d0603551d0e04160414a4"
160                "d521a7a7123338ed83272d04d60e50f4c39c1c300f0603551d130101ff040530030101ff300a06082a"
161                "8648ce3d040302034900304602210082207a475582b6a2a48ffecea8dfe3aea6b84a19919a392ae779"
162                "282bf6074e64022100aa5e1fd03607db29ff144d61815f6730f60d9fe4f227b2f093b93f3acb5cefa4"
163                "b506040425b2131eb603010100b70402020403b9050203093a80ba050403666f6fbb030101ff");
164            let encoded_params = hex!("01026710030245c80408ffffffffffffffff0504801312d00604801312d0"
165                "0704801312d008024064090240640e010540b60020048000ffff0f0880145d492e958b6f6ab200");
166            let mut out = BytesMut::new();
167            out.put_u64(encoded_session.len() as u64);
168            out.extend_from_slice(&encoded_session);
169            out.put_u64(encoded_params.len() as u64);
170            out.extend_from_slice(&encoded_params);
171            out.freeze()
172        };
173
174        let cfg = ClientConfig::new().unwrap();
175        let ctx = cfg.ctx().as_ref();
176        let decoded = Entry::decode(ctx, encoded.clone()).unwrap();
177        let re_encoded = decoded.encode().unwrap();
178        assert_eq!(&encoded, &re_encoded);
179    }
180}