1use std::collections::HashMap;
4
5use bytes::Bytes;
6
7use crate::demux::CancelKey;
8
9pub trait CancelKeyMint {
15 type Error;
17
18 fn mint_cancel_key(&mut self) -> Result<CancelKey, Self::Error>;
24}
25
26pub trait CancelKeyRegistry {
32 type Error;
34
35 fn register_cancel_key(
42 &mut self,
43 client: CancelKey,
44 upstream: CancelKey,
45 ) -> Result<(), Self::Error>;
46
47 fn resolve_cancel_key(&self, client: &CancelKey) -> Option<CancelKey>;
50
51 fn remove_cancel_key(&mut self, client: &CancelKey) -> Option<CancelKey>;
53}
54
55#[derive(Debug, Default)]
57pub struct CancelKeyMap {
58 mappings: HashMap<CancelKey, CancelKey>,
59}
60
61#[derive(Clone, Debug, Eq, PartialEq)]
63pub enum RegisterError {
64 InvalidClientKeyLength(usize),
66 InvalidUpstreamKeyLength(usize),
68 ClientKeyCollision,
70}
71
72impl CancelKeyMap {
73 #[must_use]
75 pub fn new() -> Self {
76 Self::default()
77 }
78
79 pub fn register(
86 &mut self,
87 client: CancelKey,
88 upstream: CancelKey,
89 ) -> Result<(), RegisterError> {
90 validate_key(&client).map_err(RegisterError::InvalidClientKeyLength)?;
91 validate_key(&upstream).map_err(RegisterError::InvalidUpstreamKeyLength)?;
92 if self.mappings.contains_key(&client) {
93 return Err(RegisterError::ClientKeyCollision);
94 }
95 self.mappings.insert(client, upstream);
96 Ok(())
97 }
98
99 #[must_use]
101 pub fn resolve(&self, process_id: u32, secret_key: &[u8]) -> Option<&CancelKey> {
102 self.mappings.get(&CancelKey {
103 process_id,
104 secret_key: Bytes::copy_from_slice(secret_key),
105 })
106 }
107
108 pub fn remove(&mut self, client: &CancelKey) -> Option<CancelKey> {
110 self.mappings.remove(client)
111 }
112
113 #[must_use]
115 pub fn len(&self) -> usize {
116 self.mappings.len()
117 }
118
119 #[must_use]
121 pub fn is_empty(&self) -> bool {
122 self.mappings.is_empty()
123 }
124}
125
126impl CancelKeyRegistry for CancelKeyMap {
127 type Error = RegisterError;
128
129 fn register_cancel_key(
130 &mut self,
131 client: CancelKey,
132 upstream: CancelKey,
133 ) -> Result<(), Self::Error> {
134 self.register(client, upstream)
135 }
136
137 fn resolve_cancel_key(&self, client: &CancelKey) -> Option<CancelKey> {
138 self.mappings.get(client).cloned()
139 }
140
141 fn remove_cancel_key(&mut self, client: &CancelKey) -> Option<CancelKey> {
142 self.remove(client)
143 }
144}
145
146fn validate_key(key: &CancelKey) -> Result<(), usize> {
147 if (4..=256).contains(&key.secret_key.len()) {
148 Ok(())
149 } else {
150 Err(key.secret_key.len())
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn maps_variable_length_client_keys_without_overwriting_collisions() {
160 let client = CancelKey {
161 process_id: 7,
162 secret_key: Bytes::from(vec![0xAA; 32]),
163 };
164 let upstream = CancelKey {
165 process_id: 42,
166 secret_key: Bytes::from_static(b"upstream"),
167 };
168 let mut map = CancelKeyMap::new();
169 map.register(client.clone(), upstream.clone()).unwrap();
170
171 assert_eq!(map.resolve(7, &[0xAA; 32]), Some(&upstream));
172 assert_eq!(
173 map.register(client.clone(), upstream.clone()),
174 Err(RegisterError::ClientKeyCollision)
175 );
176 assert_eq!(map.remove(&client), Some(upstream));
177 assert!(map.is_empty());
178 }
179
180 #[test]
181 fn rejects_keys_which_cannot_be_encoded_as_cancel_requests() {
182 let mut map = CancelKeyMap::new();
183 let client = CancelKey {
184 process_id: 1,
185 secret_key: Bytes::from_static(b"bad"),
186 };
187 let upstream = CancelKey {
188 process_id: 2,
189 secret_key: Bytes::from_static(b"valid"),
190 };
191 assert_eq!(
192 map.register(client, upstream),
193 Err(RegisterError::InvalidClientKeyLength(3))
194 );
195 }
196
197 #[test]
198 fn reference_map_can_be_used_through_the_policy_hook() {
199 let client = CancelKey {
200 process_id: 11,
201 secret_key: Bytes::from_static(b"client"),
202 };
203 let upstream = CancelKey {
204 process_id: 22,
205 secret_key: Bytes::from_static(b"server"),
206 };
207 let registry: &mut dyn CancelKeyRegistry<Error = RegisterError> = &mut CancelKeyMap::new();
208
209 registry
210 .register_cancel_key(client.clone(), upstream.clone())
211 .unwrap();
212 assert_eq!(registry.resolve_cancel_key(&client), Some(upstream.clone()));
213 assert_eq!(registry.remove_cancel_key(&client), Some(upstream));
214 }
215}