1use serde::{Deserialize, Serialize};
2use snafu::ResultExt;
3
4use pb_mapper_auth::{
5 AuthStatus, IssuedTemporaryKey, KeyPage, LegacyProtocolPolicy, TemporaryKeyMetadata,
6};
7use pb_mapper_core::checksum::AesKeyType;
8use pb_mapper_core::error::{MsgSerializeSnafu, Result};
9
10pub const CONTROL_PROTOCOL_V2: u16 = 2;
11
12pub trait MessageSerializer {
13 fn encode(&self) -> Result<Vec<u8>>;
14 fn decode(msg: &[u8]) -> Result<Self>
15 where
16 Self: Sized;
17}
18
19#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
20pub enum PbConnStatusReq {
21 RemoteId,
22 Keys,
23 Service { key: String },
24}
25
26#[derive(Debug, Clone, Deserialize, Serialize)]
27pub enum PbConnStatusResp {
28 RemoteId {
29 server_map: String,
30 active: String,
31 idle: String,
32 },
33 Keys(Vec<String>),
34 Service {
35 key: String,
36 connections: Vec<PbServiceConnStatus>,
37 },
38}
39
40#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
41pub struct PbServiceConnStatus {
42 pub conn_id: u32,
43 pub generation: u64,
44 pub protocol_version: u16,
45 pub healthy: bool,
46 pub last_rx_age_ms: u64,
47}
48
49#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
50pub enum PbConnRequest {
51 Register {
52 need_codec: bool,
53 is_datagram: bool,
54 key: String,
55 #[serde(default, skip_serializing_if = "Option::is_none")]
56 protocol_version: Option<u16>,
57 #[serde(default, skip_serializing_if = "Option::is_none")]
58 client_instance_id: Option<String>,
59 #[serde(default, skip_serializing_if = "Option::is_none")]
60 heartbeat_interval_ms: Option<u64>,
61 #[serde(default, skip_serializing_if = "Option::is_none")]
62 heartbeat_tolerance_ms: Option<u64>,
63 },
64 RegisterScoped {
65 need_codec: bool,
66 is_datagram: bool,
67 key: String,
68 namespace: u64,
69 force_namespace: bool,
70 #[serde(default, skip_serializing_if = "Option::is_none")]
71 protocol_version: Option<u16>,
72 #[serde(default, skip_serializing_if = "Option::is_none")]
73 client_instance_id: Option<String>,
74 #[serde(default, skip_serializing_if = "Option::is_none")]
75 heartbeat_interval_ms: Option<u64>,
76 #[serde(default, skip_serializing_if = "Option::is_none")]
77 heartbeat_tolerance_ms: Option<u64>,
78 },
79 Subcribe {
80 key: String,
81 },
82 SubcribeScoped {
83 key: String,
84 namespace: u64,
85 },
86 Status(PbConnStatusReq),
87 StatusScoped {
88 status: PbConnStatusReq,
89 namespace: u64,
90 },
91 Stream {
92 key: String,
93 dst_id: u32,
94 #[serde(default)]
95 server_generation: u64,
96 },
97 StreamScoped {
98 key: String,
99 namespace: u64,
100 dst_id: u32,
101 #[serde(default)]
102 server_generation: u64,
103 },
104 Admin(AdminRequest),
105}
106
107#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
108pub enum AdminRequest {
109 KeyIssue {
110 ttl_seconds: u64,
111 #[serde(default, skip_serializing_if = "Option::is_none")]
112 label: Option<String>,
113 },
114 KeyList {
115 #[serde(default)]
116 page: u32,
117 #[serde(default = "default_page_size")]
118 page_size: u16,
119 },
120 KeyShow {
121 key_id: u64,
122 },
123 KeyReveal {
124 key_id: u64,
125 },
126 KeyRenew {
127 key_id: u64,
128 ttl_seconds: u64,
129 },
130 KeyRevoke {
131 key_id: u64,
132 },
133 KeyGc,
134 AuthStatus,
135 AuthStateReset {
136 confirm: bool,
137 },
138 RootKeyRotate {
139 new_admin_key: String,
140 },
141 LegacyProtocolSet {
142 policy: LegacyProtocolPolicy,
143 },
144 ConnectionList {
145 #[serde(default, skip_serializing_if = "Option::is_none")]
146 key_id: Option<u64>,
147 #[serde(default)]
148 page: u32,
149 #[serde(default = "default_page_size")]
150 page_size: u16,
151 },
152 ServiceList {
153 #[serde(default, skip_serializing_if = "Option::is_none")]
154 key_id: Option<u64>,
155 #[serde(default)]
156 page: u32,
157 #[serde(default = "default_page_size")]
158 page_size: u16,
159 },
160 ConnectionRetire {
167 #[serde(default, skip_serializing_if = "Option::is_none")]
170 key_id: Option<u64>,
171 service_name: String,
172 #[serde(default, skip_serializing_if = "Option::is_none")]
175 conn_id: Option<u32>,
176 },
177}
178
179impl AdminRequest {
180 pub fn is_mutating(&self) -> bool {
181 matches!(
182 self,
183 Self::KeyIssue { .. }
184 | Self::KeyRenew { .. }
185 | Self::KeyRevoke { .. }
186 | Self::KeyGc
187 | Self::AuthStateReset { .. }
188 | Self::RootKeyRotate { .. }
189 | Self::LegacyProtocolSet { .. }
190 | Self::ConnectionRetire { .. }
191 )
192 }
193}
194
195const fn default_page_size() -> u16 {
196 100
197}
198
199#[derive(Debug, Clone, Deserialize, Serialize)]
200pub struct PbErrorResponse {
201 pub code: String,
202 pub message: String,
203 pub retryable: bool,
204 pub server_time: u64,
205}
206
207#[derive(Debug, Clone, Deserialize, Serialize)]
208pub struct AdminServiceInfo {
209 pub key_id: u64,
210 pub namespace: u64,
211 pub service_name: String,
212 pub transport: String,
213 pub codec_enabled: bool,
214 pub connection_count: u32,
215}
216
217#[derive(Debug, Clone, Deserialize, Serialize)]
218pub struct AdminConnectionInfo {
219 pub key_id: u64,
220 pub namespace: u64,
221 pub service_name: String,
222 pub conn_id: u32,
223 pub generation: u64,
224 pub protocol_version: u16,
225 pub healthy: bool,
226 pub transport: String,
227 pub codec_enabled: bool,
228 pub last_rx_age_ms: u64,
229}
230
231#[derive(Debug, Clone, Deserialize, Serialize)]
232pub struct AdminServicePage {
233 pub schema_version: u16,
234 pub items: Vec<AdminServiceInfo>,
235 pub next_page: Option<u32>,
236}
237
238#[derive(Debug, Clone, Deserialize, Serialize)]
239pub struct AdminConnectionPage {
240 pub schema_version: u16,
241 pub items: Vec<AdminConnectionInfo>,
242 pub next_page: Option<u32>,
243}
244
245#[derive(Debug, Clone, Deserialize, Serialize)]
246pub enum AdminResponse {
247 KeyIssued(IssuedTemporaryKey),
248 KeyList(KeyPage),
249 KeyShown(IssuedTemporaryKey),
250 KeyRenewed(IssuedTemporaryKey),
251 KeyRevoked(TemporaryKeyMetadata),
252 KeyGc {
253 removed: u64,
254 },
255 AuthStatus(AuthStatus),
256 Services(AdminServicePage),
257 Connections(AdminConnectionPage),
258 ConnectionsRetired {
261 retired: u32,
262 },
263 Ok {
264 action: String,
265 },
266}
267
268#[derive(Debug, Clone, Deserialize, Serialize)]
269pub enum PbConnResponse {
270 Register(u32),
271 RegisterV2 {
272 conn_id: u32,
273 generation: u64,
274 lease_ttl_ms: u64,
275 },
276 Subcribe {
277 codec_key: Option<AesKeyType>,
278 client_id: u32,
279 server_id: u32,
280 },
281 Stream {
282 codec_key: Option<AesKeyType>,
283 },
284 Status(PbConnStatusResp),
285 Admin(AdminResponse),
286 Error(PbErrorResponse),
287}
288
289impl PbConnResponse {
290 pub fn error(code: impl Into<String>, message: impl Into<String>, retryable: bool) -> Self {
291 Self::Error(PbErrorResponse {
292 code: code.into(),
293 message: message.into(),
294 retryable,
295 server_time: std::time::SystemTime::now()
296 .duration_since(std::time::UNIX_EPOCH)
297 .unwrap_or_default()
298 .as_secs(),
299 })
300 }
301}
302
303#[derive(Debug, Clone, Deserialize, Serialize)]
304pub enum PbServerRequest {
305 Ping,
306 PingV2 {
307 seq: u64,
308 },
309 StreamAck {
310 client_id: u32,
311 #[serde(default)]
312 server_generation: u64,
313 },
314}
315
316#[derive(Debug, Clone, Deserialize, Serialize)]
317pub enum LocalServer {
318 Stream {
320 client_id: u32,
321 #[serde(default)]
322 server_generation: u64,
323 },
324 Pong,
326 PongV2 {
327 seq: u64,
328 },
329 Retire {
330 reason: String,
331 conn_id: u32,
332 #[serde(default)]
333 server_generation: u64,
334 },
335}
336
337macro_rules! gen_impl_msg_serializer {
338 ($struct_name:ident) => {
339 impl MessageSerializer for $struct_name {
340 fn encode(&self) -> Result<Vec<u8>> {
341 serde_json::to_vec(self).with_context(|_| MsgSerializeSnafu {
342 action: "encode",
343 struct_name: stringify!($struct_name),
344 content: "payload redacted".to_string(),
345 })
346 }
347
348 fn decode(msg: &[u8]) -> Result<Self> {
349 serde_json::from_slice(msg).with_context(|_| MsgSerializeSnafu {
350 action: "decode",
351 struct_name: stringify!($struct_name),
352 content: format!("{}-byte payload redacted", msg.len()),
353 })
354 }
355 }
356 };
357}
358
359gen_impl_msg_serializer!(PbConnRequest);
360gen_impl_msg_serializer!(PbConnResponse);
361gen_impl_msg_serializer!(PbServerRequest);
362gen_impl_msg_serializer!(LocalServer);
363
364#[cfg(test)]
365mod tests {
366 use super::PbConnRequest;
367
368 #[test]
372 fn test_serde_mapper_header() {
373 let mapper = PbConnRequest::Register {
374 key: "test".into(),
375 need_codec: false,
376 is_datagram: false,
377 protocol_version: None,
378 client_instance_id: None,
379 heartbeat_interval_ms: None,
380 heartbeat_tolerance_ms: None,
381 };
382 let json_value = serde_json::to_string(&mapper).unwrap();
383 let raw_json_str =
384 r##"{"Register":{"need_codec":false,"is_datagram":false,"key":"test"}}"##;
385 assert_eq!(raw_json_str, json_value);
386
387 let value: PbConnRequest = serde_json::from_str(raw_json_str).unwrap();
388 assert_eq!(mapper, value)
389 }
390}