confium_coordinator/coordinator/
version_negotiation.rs1use serde::{Deserialize, Serialize};
4
5pub const PROTOCOL_VERSION: u32 = 1;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct VersionHandshake {
11 pub client_version: u32,
12 pub min_supported: u32,
13}
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct VersionResponse {
18 pub server_version: u32,
19 pub accepted: bool,
20 pub negotiated_version: u32,
21 pub reason: Option<String>,
22}
23
24pub fn negotiate(client_handshake: &VersionHandshake) -> VersionResponse {
26 if client_handshake.client_version > PROTOCOL_VERSION {
27 if client_handshake.min_supported <= PROTOCOL_VERSION {
28 return VersionResponse {
29 server_version: PROTOCOL_VERSION,
30 accepted: true,
31 negotiated_version: PROTOCOL_VERSION,
32 reason: Some(format!(
33 "downgraded from {} to {}",
34 client_handshake.client_version, PROTOCOL_VERSION
35 )),
36 };
37 }
38 return VersionResponse {
39 server_version: PROTOCOL_VERSION,
40 accepted: false,
41 negotiated_version: 0,
42 reason: Some("client requires newer protocol".into()),
43 };
44 }
45 if client_handshake.client_version < 1 {
46 return VersionResponse {
47 server_version: PROTOCOL_VERSION,
48 accepted: false,
49 negotiated_version: 0,
50 reason: Some("invalid client version".into()),
51 };
52 }
53 VersionResponse {
54 server_version: PROTOCOL_VERSION,
55 accepted: true,
56 negotiated_version: client_handshake.client_version.min(PROTOCOL_VERSION),
57 reason: None,
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 fn matching_version_accepted() {
67 let hs = VersionHandshake {
68 client_version: 1,
69 min_supported: 1,
70 };
71 let resp = negotiate(&hs);
72 assert!(resp.accepted);
73 assert_eq!(resp.negotiated_version, 1);
74 }
75
76 #[test]
77 fn newer_client_downgrades() {
78 let hs = VersionHandshake {
79 client_version: 5,
80 min_supported: 1,
81 };
82 let resp = negotiate(&hs);
83 assert!(resp.accepted);
84 assert_eq!(resp.negotiated_version, PROTOCOL_VERSION);
85 }
86
87 #[test]
88 fn client_requires_newer_rejected() {
89 let hs = VersionHandshake {
90 client_version: 5,
91 min_supported: 5,
92 };
93 let resp = negotiate(&hs);
94 assert!(!resp.accepted);
95 }
96
97 #[test]
98 fn invalid_version_rejected() {
99 let hs = VersionHandshake {
100 client_version: 0,
101 min_supported: 0,
102 };
103 let resp = negotiate(&hs);
104 assert!(!resp.accepted);
105 }
106
107 #[test]
108 fn handshake_serializes() {
109 let hs = VersionHandshake {
110 client_version: 1,
111 min_supported: 1,
112 };
113 let json = serde_json::to_string(&hs).unwrap();
114 assert!(json.contains("client_version"));
115 }
116}