1#![warn(clippy::missing_docs_in_private_items)]
3use std::{fmt, io};
4
5use bytes::{Buf, BufMut};
6use radicle::node::Link;
7
8use crate::service::Message;
9use crate::{PROTOCOL_VERSION, wire, wire::varint, wire::varint::VarInt};
10
11pub const PROTOCOL_VERSION_STRING: Version = Version([b'r', b'a', b'd', PROTOCOL_VERSION]);
14
15#[derive(Debug, PartialEq, Eq)]
17pub struct Version([u8; 4]);
18
19impl Version {
20 pub fn number(&self) -> u8 {
22 self.0[3]
23 }
24}
25
26impl wire::Encode for Version {
27 fn encode(&self, buf: &mut impl BufMut) {
28 buf.put_slice(&PROTOCOL_VERSION_STRING.0);
29 }
30}
31
32impl wire::Decode for Version {
33 fn decode(buf: &mut impl Buf) -> Result<Self, wire::Error> {
34 let mut version = [0u8; 4];
35
36 buf.try_copy_to_slice(&mut version[..])?;
37
38 if version != PROTOCOL_VERSION_STRING.0 {
39 return Err(wire::Invalid::ProtocolVersion { actual: version }.into());
40 }
41 Ok(Self(version))
42 }
43}
44
45#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
76pub struct StreamId(VarInt);
77
78impl StreamId {
79 pub fn link(&self) -> Link {
81 let n = *self.0;
82 if 0b1 & n == 0 {
83 Link::Outbound
84 } else {
85 Link::Inbound
86 }
87 }
88
89 pub fn kind(&self) -> Result<StreamType, u8> {
91 let id = *self.0;
92 let kind = ((id >> 1) & 0b11) as u8;
93
94 StreamType::try_from(kind)
95 }
96
97 pub fn control(link: Link) -> Self {
99 let link = if link.is_outbound() { 0 } else { 1 };
100 Self(VarInt::from(((u8::from(StreamType::Control)) << 1) | link))
101 }
102
103 pub fn gossip(link: Link) -> Self {
105 let link = if link.is_outbound() { 0 } else { 1 };
106 Self(VarInt::from((u8::from(StreamType::Gossip) << 1) | link))
107 }
108
109 pub fn git(link: Link) -> Self {
111 let link = if link.is_outbound() { 0 } else { 1 };
112 Self(VarInt::from((u8::from(StreamType::Git) << 1) | link))
113 }
114
115 pub fn nth(self, n: u64) -> Result<Self, varint::BoundsExceeded> {
117 let id = *self.0 + (n << 3);
118 VarInt::new(id).map(Self)
119 }
120}
121
122impl From<StreamId> for u64 {
123 fn from(value: StreamId) -> Self {
124 *value.0
125 }
126}
127
128impl From<StreamId> for VarInt {
129 fn from(value: StreamId) -> Self {
130 value.0
131 }
132}
133
134impl fmt::Display for StreamId {
135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 write!(f, "{}", *self.0)
137 }
138}
139
140impl wire::Decode for StreamId {
141 fn decode(buf: &mut impl Buf) -> Result<Self, wire::Error> {
142 let id = VarInt::decode(buf)?;
143 Ok(Self(id))
144 }
145}
146
147impl wire::Encode for StreamId {
148 fn encode(&self, buf: &mut impl BufMut) {
149 self.0.encode(buf)
150 }
151}
152
153#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
155#[repr(u8)]
156pub enum StreamType {
157 Control = 0b00,
159 Gossip = 0b01,
161 Git = 0b10,
163}
164
165impl TryFrom<u8> for StreamType {
166 type Error = u8;
167
168 fn try_from(value: u8) -> Result<Self, Self::Error> {
169 match value {
170 0b00 => Ok(StreamType::Control),
171 0b01 => Ok(StreamType::Gossip),
172 0b10 => Ok(StreamType::Git),
173 n => Err(n),
174 }
175 }
176}
177
178impl From<StreamType> for u8 {
179 fn from(value: StreamType) -> Self {
180 value as u8
181 }
182}
183
184#[derive(Debug, PartialEq, Eq)]
203pub struct Frame<M = Message> {
204 pub version: Version,
206 pub stream: StreamId,
208 pub data: FrameData<M>,
210}
211
212impl<M> Frame<M> {
213 pub fn git(stream: StreamId, data: Vec<u8>) -> Self {
215 Self {
216 version: PROTOCOL_VERSION_STRING,
217 stream,
218 data: FrameData::Git(data),
219 }
220 }
221
222 pub fn control(link: Link, ctrl: Control) -> Self {
224 Self {
225 version: PROTOCOL_VERSION_STRING,
226 stream: StreamId::control(link),
227 data: FrameData::Control(ctrl),
228 }
229 }
230
231 pub fn gossip(link: Link, msg: M) -> Self {
233 Self {
234 version: PROTOCOL_VERSION_STRING,
235 stream: StreamId::gossip(link),
236 data: FrameData::Gossip(msg),
237 }
238 }
239}
240
241#[derive(Debug, PartialEq, Eq)]
243pub enum FrameData<M> {
244 Control(Control),
246 Gossip(M),
248 Git(Vec<u8>),
250}
251
252#[derive(Debug, PartialEq, Eq)]
254pub enum Control {
255 Open {
257 stream: StreamId,
259 },
260 Close {
262 stream: StreamId,
264 },
265 Eof {
269 stream: StreamId,
271 },
272}
273
274#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
276#[repr(u8)]
277pub enum ControlType {
278 Open = 0,
280 Close = 1,
282 Eof = 2,
284}
285
286impl TryFrom<u8> for ControlType {
287 type Error = u8;
288
289 fn try_from(value: u8) -> Result<Self, Self::Error> {
290 match value {
291 0b00 => Ok(ControlType::Open),
292 0b01 => Ok(ControlType::Close),
293 0b10 => Ok(ControlType::Eof),
294 n => Err(n),
295 }
296 }
297}
298
299impl From<ControlType> for u8 {
300 fn from(value: ControlType) -> Self {
301 value as u8
302 }
303}
304
305impl wire::Decode for Control {
306 fn decode(buf: &mut impl Buf) -> Result<Self, wire::Error> {
307 match ControlType::try_from(u8::decode(buf)?) {
308 Ok(ControlType::Open) => Ok(Control::Open {
309 stream: StreamId::decode(buf)?,
310 }),
311 Ok(ControlType::Close) => Ok(Control::Close {
312 stream: StreamId::decode(buf)?,
313 }),
314 Ok(ControlType::Eof) => Ok(Control::Eof {
315 stream: StreamId::decode(buf)?,
316 }),
317 Err(other) => Err(wire::Invalid::ControlType { actual: other }.into()),
318 }
319 }
320}
321
322impl wire::Encode for Control {
323 fn encode(&self, buf: &mut impl BufMut) {
324 match self {
325 Self::Open { stream: id } => {
326 u8::from(ControlType::Open).encode(buf);
327 id.encode(buf);
328 }
329 Self::Eof { stream: id } => {
330 u8::from(ControlType::Eof).encode(buf);
331 id.encode(buf);
332 }
333 Self::Close { stream: id } => {
334 u8::from(ControlType::Close).encode(buf);
335 id.encode(buf);
336 }
337 }
338 }
339}
340
341impl<M: wire::Decode> wire::Decode for Frame<M> {
342 fn decode(buf: &mut impl Buf) -> Result<Self, wire::Error> {
343 let version = Version::decode(buf)?;
344 if version.number() != PROTOCOL_VERSION {
345 return Err(wire::Invalid::ProtocolVersionUnsupported {
346 actual: version.number(),
347 }
348 .into());
349 }
350 let stream = StreamId::decode(buf)?;
351
352 match stream.kind() {
353 Ok(StreamType::Control) => {
354 let ctrl = Control::decode(buf)?;
355 let frame = Frame {
356 version,
357 stream,
358 data: FrameData::Control(ctrl),
359 };
360 Ok(frame)
361 }
362 Ok(StreamType::Gossip) => {
363 let data = varint::payload::decode(buf)?;
364 let mut cursor = io::Cursor::new(data);
365 let msg = M::decode(&mut cursor)?;
366 let frame = Frame {
367 version,
368 stream,
369 data: FrameData::Gossip(msg),
370 };
371
372 Ok(frame)
376 }
377 Ok(StreamType::Git) => {
378 let data = varint::payload::decode(buf)?;
379 Ok(Frame::git(stream, data))
380 }
381 Err(n) => Err(wire::Invalid::StreamType { actual: n }.into()),
382 }
383 }
384}
385
386impl<M: wire::Encode> wire::Encode for Frame<M> {
387 fn encode(&self, buf: &mut impl BufMut) {
388 self.version.encode(buf);
389 self.stream.encode(buf);
390 match &self.data {
391 FrameData::Control(ctrl) => ctrl.encode(buf),
392 FrameData::Git(data) => varint::payload::encode(data, buf),
393 FrameData::Gossip(msg) => varint::payload::encode(&msg.encode_to_vec(), buf),
394 }
395 }
396}
397
398#[cfg(test)]
399mod test {
400 use super::*;
401
402 #[test]
403 fn test_stream_id() {
404 assert_eq!(StreamId(VarInt(0b000)).kind().unwrap(), StreamType::Control);
405 assert_eq!(StreamId(VarInt(0b010)).kind().unwrap(), StreamType::Gossip);
406 assert_eq!(StreamId(VarInt(0b100)).kind().unwrap(), StreamType::Git);
407 assert_eq!(StreamId(VarInt(0b001)).link(), Link::Inbound);
408 assert_eq!(StreamId(VarInt(0b000)).link(), Link::Outbound);
409 assert_eq!(StreamId(VarInt(0b101)).link(), Link::Inbound);
410 assert_eq!(StreamId(VarInt(0b100)).link(), Link::Outbound);
411
412 assert_eq!(StreamId::git(Link::Outbound), StreamId(VarInt(0b100)));
413 assert_eq!(StreamId::control(Link::Outbound), StreamId(VarInt(0b000)));
414 assert_eq!(StreamId::gossip(Link::Outbound), StreamId(VarInt(0b010)));
415
416 assert_eq!(StreamId::git(Link::Inbound), StreamId(VarInt(0b101)));
417 assert_eq!(StreamId::control(Link::Inbound), StreamId(VarInt(0b001)));
418 assert_eq!(StreamId::gossip(Link::Inbound), StreamId(VarInt(0b011)));
419 }
420
421 #[test]
422 fn test_encode_git_large() {
423 use wire::Encode as _;
424
425 let size = u16::MAX as usize * 3;
426 assert!(
427 size > (wire::Size::MAX as usize * 2),
428 "we want to test sizes that are way larger than any gossip message"
429 );
430
431 let a_lot_of_data = vec![0u8; size];
432
433 let frame: Frame<Message> = Frame::git(StreamId(0u8.into()), a_lot_of_data);
434
435 let bytes = frame.encode_to_vec();
437
438 assert!(
439 bytes.len() > wire::Size::MAX as usize * 2,
440 "just making sure that whatever was encoded is still quite large"
441 );
442 }
443}