avalanche_types/message/
ping.rs1use std::io::{self, Error, ErrorKind};
2
3use crate::{message, proto::pb::p2p};
4use prost::bytes::Bytes;
5use prost::Message as ProstMessage;
6
7#[derive(Debug, PartialEq, Clone)]
8pub struct Message {
9 pub msg: p2p::Ping,
10 pub gzip_compress: bool,
11}
12
13impl Default for Message {
14 fn default() -> Self {
15 Message {
16 msg: p2p::Ping {
17 uptime: 0,
18 subnet_uptimes: vec![p2p::SubnetUptime {
19 subnet_id: Bytes::new(),
20 uptime: 0,
21 }],
22 },
23 gzip_compress: false,
24 }
25 }
26}
27
28impl Message {
29 #[must_use]
30 pub fn gzip_compress(mut self, gzip_compress: bool) -> Self {
31 self.gzip_compress = gzip_compress;
32 self
33 }
34
35 pub fn serialize(&self) -> io::Result<Vec<u8>> {
36 let msg = p2p::Message {
37 message: Some(p2p::message::Message::Ping(self.msg.clone())),
38 };
39 let encoded = ProstMessage::encode_to_vec(&msg);
40 if !self.gzip_compress {
41 return Ok(encoded);
42 }
43
44 let uncompressed_len = encoded.len();
45 let compressed = message::compress::pack_gzip(&encoded)?;
46 let msg = p2p::Message {
47 message: Some(p2p::message::Message::CompressedGzip(Bytes::from(
48 compressed,
49 ))),
50 };
51
52 let compressed_len = msg.encoded_len();
53 if uncompressed_len > compressed_len {
54 log::debug!(
55 "ping compression saved {} bytes",
56 uncompressed_len - compressed_len
57 );
58 } else {
59 log::debug!(
60 "ping compression added {} byte(s)",
61 compressed_len - uncompressed_len
62 );
63 }
64
65 Ok(ProstMessage::encode_to_vec(&msg))
66 }
67
68 pub fn deserialize(d: impl AsRef<[u8]>) -> io::Result<Self> {
69 let buf = bytes::Bytes::from(d.as_ref().to_vec());
70 let p2p_msg: p2p::Message = ProstMessage::decode(buf).map_err(|e| {
71 Error::new(
72 ErrorKind::InvalidData,
73 format!("failed prost::Message::decode '{}'", e),
74 )
75 })?;
76
77 match p2p_msg.message.unwrap() {
78 p2p::message::Message::Ping(msg) => Ok(Message {
80 msg,
81 gzip_compress: false,
82 }),
83
84 p2p::message::Message::CompressedGzip(msg) => {
86 let decompressed = message::compress::unpack_gzip(msg.as_ref())?;
87 let decompressed_msg: p2p::Message =
88 ProstMessage::decode(Bytes::from(decompressed)).map_err(|e| {
89 Error::new(
90 ErrorKind::InvalidData,
91 format!("failed prost::Message::decode '{}'", e),
92 )
93 })?;
94 match decompressed_msg.message.unwrap() {
95 p2p::message::Message::Ping(msg) => Ok(Message {
96 msg,
97 gzip_compress: false,
98 }),
99 _ => Err(Error::new(
100 ErrorKind::InvalidInput,
101 "unknown message type after decompress",
102 )),
103 }
104 }
105
106 _ => Err(Error::new(ErrorKind::InvalidInput, "unknown message type")),
108 }
109 }
110}
111
112#[test]
114fn test_message() {
115 let _ = env_logger::builder()
116 .filter_level(log::LevelFilter::Debug)
117 .is_test(true)
118 .try_init();
119
120 let msg1_with_no_compression = Message::default();
121
122 let data1 = msg1_with_no_compression.serialize().unwrap();
123 let msg1_with_no_compression_deserialized = Message::deserialize(data1).unwrap();
124 assert_eq!(
125 msg1_with_no_compression,
126 msg1_with_no_compression_deserialized
127 );
128
129 let msg2_with_compression = msg1_with_no_compression.clone().gzip_compress(true);
130 assert_ne!(msg1_with_no_compression, msg2_with_compression);
131
132 let data2 = msg2_with_compression.serialize().unwrap();
133 let msg2_with_compression_deserialized = Message::deserialize(data2).unwrap();
134 assert_eq!(msg1_with_no_compression, msg2_with_compression_deserialized);
135}