hyper_openssl/client/
cache.rs

1use linked_hash_set::LinkedHashSet;
2#[cfg(ossl111)]
3use openssl::ssl::SslVersion;
4use openssl::ssl::{SslSession, SslSessionRef};
5use std::borrow::Borrow;
6use std::collections::hash_map::Entry;
7use std::collections::HashMap;
8use std::hash::{Hash, Hasher};
9
10#[derive(Hash, PartialEq, Eq, Clone)]
11pub struct SessionKey {
12    pub host: String,
13    pub port: u16,
14}
15
16#[derive(Clone)]
17struct HashSession(SslSession);
18
19impl PartialEq for HashSession {
20    fn eq(&self, other: &HashSession) -> bool {
21        self.0.id() == other.0.id()
22    }
23}
24
25impl Eq for HashSession {}
26
27impl Hash for HashSession {
28    fn hash<H>(&self, state: &mut H)
29    where
30        H: Hasher,
31    {
32        self.0.id().hash(state)
33    }
34}
35
36impl Borrow<[u8]> for HashSession {
37    fn borrow(&self) -> &[u8] {
38        self.0.id()
39    }
40}
41
42pub struct SessionCache {
43    sessions: HashMap<SessionKey, LinkedHashSet<HashSession>>,
44    reverse: HashMap<HashSession, SessionKey>,
45}
46
47impl SessionCache {
48    pub fn new() -> SessionCache {
49        SessionCache {
50            sessions: HashMap::new(),
51            reverse: HashMap::new(),
52        }
53    }
54
55    pub fn insert(&mut self, key: SessionKey, session: SslSession) {
56        let session = HashSession(session);
57
58        self.sessions
59            .entry(key.clone())
60            .or_default()
61            .insert(session.clone());
62        self.reverse.insert(session, key);
63    }
64
65    pub fn get(&mut self, key: &SessionKey) -> Option<SslSession> {
66        let sessions = self.sessions.get_mut(key)?;
67        let session = sessions.front().cloned()?.0;
68
69        #[cfg(ossl111)]
70        {
71            // https://tools.ietf.org/html/rfc8446#appendix-C.4
72            // OpenSSL will remove the session from its cache after the handshake completes anyway, but this ensures
73            // that concurrent handshakes don't end up with the same session.
74            if session.protocol_version() == SslVersion::TLS1_3 {
75                self.remove(&session);
76            }
77        }
78
79        Some(session)
80    }
81
82    pub fn remove(&mut self, session: &SslSessionRef) {
83        let key = match self.reverse.remove(session.id()) {
84            Some(key) => key,
85            None => return,
86        };
87
88        if let Entry::Occupied(mut sessions) = self.sessions.entry(key) {
89            sessions.get_mut().remove(session.id());
90            if sessions.get().is_empty() {
91                sessions.remove();
92            }
93        }
94    }
95}