futu_codec/
frame.rs

1use bytes::Bytes;
2
3use crate::header::{FutuHeader, ProtoFmtType};
4
5/// FutuOpenD 协议帧
6///
7/// 包含帧头信息和 body 数据。
8#[derive(Debug, Clone)]
9pub struct FutuFrame {
10    pub header: FutuHeader,
11    pub body: Bytes,
12}
13
14impl FutuFrame {
15    /// 创建新的协议帧
16    pub fn new(proto_id: u32, serial_no: u32, body: Bytes) -> Self {
17        use sha1::{Digest, Sha1};
18
19        let mut hasher = Sha1::new();
20        hasher.update(&body);
21        let sha1_result = hasher.finalize();
22        let mut body_sha1 = [0u8; 20];
23        body_sha1.copy_from_slice(&sha1_result);
24
25        Self {
26            header: FutuHeader {
27                proto_id,
28                proto_fmt_type: ProtoFmtType::Protobuf,
29                proto_ver: 0,
30                serial_no,
31                body_len: body.len() as u32,
32                body_sha1,
33            },
34            body,
35        }
36    }
37
38    /// 校验 body 的 SHA1 是否与帧头中的一致
39    pub fn verify_sha1(&self) -> bool {
40        use sha1::{Digest, Sha1};
41
42        let mut hasher = Sha1::new();
43        hasher.update(&self.body);
44        let computed = hasher.finalize();
45        computed.as_slice() == self.header.body_sha1
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    #[test]
54    fn test_frame_sha1_verification() {
55        let body = Bytes::from_static(b"hello futu");
56        let frame = FutuFrame::new(1001, 1, body);
57        assert!(frame.verify_sha1());
58    }
59
60    #[test]
61    fn test_frame_sha1_mismatch() {
62        let body = Bytes::from_static(b"hello futu");
63        let mut frame = FutuFrame::new(1001, 1, body);
64        frame.header.body_sha1[0] ^= 0xFF; // 篡改 SHA1
65        assert!(!frame.verify_sha1());
66    }
67}