1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
use crate::framing::{CloudProtoError, CloudProtoVersion};
use crate::services::CloudProtoMagic;
use byteorder::{ReadBytesExt, BE};
use std::io::Cursor;

pub(crate) const COMMON_HDR_LEN: usize = 8;

/// The common framing packet structure of the protocol
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct CloudProtoPacket {
    /// One magic value corresponds to one backend service
    pub magic: CloudProtoMagic,
    /// Each value can have a different interpretation for each backend service
    /// There is no common definition of packet kind at the framing level
    pub kind: u8,
    /// Used
    pub version: CloudProtoVersion,
    pub payload: Vec<u8>,
}

impl CloudProtoPacket {
    pub(crate) fn from_buf(buf: &[u8]) -> Result<Self, CloudProtoError> {
        let mut reader = Cursor::new(buf);
        let magic = reader.read_u8()?.into();
        let kind = reader.read_u8()?;
        let version = reader.read_u16::<BE>()?.into();
        let pkt_size = reader.read_u32::<BE>()? as usize - COMMON_HDR_LEN;
        let remaining_size = buf.len() - reader.position() as usize;
        if remaining_size != pkt_size {
            return Err(CloudProtoError::BadFrameSize(remaining_size, pkt_size));
        }
        let payload = buf[reader.position() as usize..].to_vec();
        Ok(Self {
            magic,
            kind,
            version,
            payload,
        })
    }

    pub(crate) fn to_buf(&self) -> Vec<u8> {
        use byteorder::WriteBytesExt;
        use std::io::Write;

        let mut buf = Vec::new();
        let mut writer = Cursor::new(&mut buf);
        writer.write_u8(self.magic.into()).unwrap();
        writer.write_u8(self.kind).unwrap();
        writer.write_u16::<BE>(self.version.into()).unwrap();
        writer
            .write_u32::<BE>((self.payload.len() + COMMON_HDR_LEN) as u32)
            .unwrap();
        writer.write_all(&self.payload).unwrap();
        writer.flush().unwrap();
        buf
    }
}

#[cfg(test)]
mod test {
    use crate::framing::packet::CloudProtoPacket;
    use crate::framing::CloudProtoVersion;
    use crate::services::CloudProtoMagic;
    use anyhow::Result;

    #[test_log::test]
    fn to_from_buf_serialization() -> Result<()> {
        let pkt = CloudProtoPacket {
            magic: CloudProtoMagic::Other(0xFF),
            kind: 0x73,
            version: CloudProtoVersion::Other(0x10E9),
            payload: b"Hello world".to_vec(),
        };
        let pkt2 = CloudProtoPacket::from_buf(&pkt.to_buf())?;
        assert_eq!(pkt, pkt2);

        Ok(())
    }
}