1pub mod metadata;
2pub mod types;
3
4use bytes::{Buf, BufMut, Bytes, BytesMut};
5use serde::{Deserialize, Serialize};
6
7use self::metadata::MessageMetadata;
8use self::types::{
9 CompressionType, MAGIC, MAX_MESSAGE_SIZE, MIN_HEADER_SIZE, MessageFlags, MessageId,
10 MessageType, VERSION,
11};
12use crate::codec::{BincodeCodec, Codec};
13use crate::error::{Result, TransportError, TransportResult};
14
15const FRAME_PREFIX_SIZE: usize = 4 + 1 + 1 + 4;
16const LEGACY_OMITTED_LENGTH_SIZE: usize = 2 + 4 + 4;
17
18#[derive(Debug, Clone)]
19pub struct Message<C: Codec = BincodeCodec> {
20 pub id: MessageId,
21 pub msg_type: MessageType,
22 pub method: String,
23 pub payload: Bytes,
24 pub metadata: MessageMetadata,
25 pub codec: C,
26}
27
28impl<C: Codec + Default> Message<C> {
29 pub fn new(
30 id: MessageId,
31 msg_type: MessageType,
32 method: impl Into<String>,
33 payload: Bytes,
34 metadata: MessageMetadata,
35 ) -> Self {
36 Self::new_with_codec(id, msg_type, method, payload, metadata, C::default())
37 }
38
39 pub fn call<T: Serialize>(method: impl Into<String>, request: T) -> Result<Self> {
40 Self::call_with_codec(method, request, C::default())
41 }
42
43 pub fn reply<T: Serialize>(id: MessageId, response: T) -> Result<Self> {
44 let codec = C::default();
45 let payload = codec.encode(&response)?;
46 Ok(Self {
47 id,
48 msg_type: MessageType::Reply,
49 method: String::new(),
50 payload: Bytes::from(payload),
51 metadata: MessageMetadata::new(),
52 codec,
53 })
54 }
55
56 pub fn notification<T: Serialize>(method: impl Into<String>, data: T) -> Result<Self> {
57 Self::notification_with_codec(method, data, C::default())
58 }
59
60 pub fn error(id: MessageId, error_msg: impl Into<String>) -> Self {
61 Self::error_with_codec(id, error_msg, C::default())
62 }
63
64 pub fn stream_error(id: MessageId, stream_id: u64, error_msg: impl Into<String>) -> Self {
66 Self::stream_error_with_codec(id, stream_id, error_msg, C::default())
67 }
68
69 pub fn stream_chunk<T: Serialize>(stream_id: u64, sequence: u64, data: T) -> Result<Self> {
71 let codec = C::default();
72 let payload = codec.encode(&data)?;
73 Ok(Self {
74 id: MessageId::new(),
75 msg_type: MessageType::StreamChunk,
76 method: String::new(),
77 payload: Bytes::from(payload),
78 metadata: MessageMetadata::new().with_stream(stream_id, sequence),
79 codec,
80 })
81 }
82
83 pub fn stream_end(stream_id: u64) -> Self {
85 Self {
86 id: MessageId::new(),
87 msg_type: MessageType::StreamEnd,
88 method: String::new(),
89 payload: Bytes::new(),
90 metadata: MessageMetadata::new().with_stream(stream_id, 0),
91 codec: C::default(),
92 }
93 }
94
95 pub fn decode(mut buf: impl Buf) -> TransportResult<Self> {
97 if buf.remaining() < MIN_HEADER_SIZE {
98 return Err(TransportError::Protocol(
99 "Buffer too small for message header".to_string(),
100 ));
101 }
102
103 let mut magic = [0u8; 4];
105 buf.copy_to_slice(&mut magic);
106 if magic != MAGIC {
107 return Err(TransportError::Protocol(format!(
108 "Invalid magic bytes: {:?}",
109 magic
110 )));
111 }
112
113 let version = buf.get_u8();
115 if version != VERSION {
116 return Err(TransportError::Protocol(format!(
117 "Unsupported protocol version: {}",
118 version
119 )));
120 }
121
122 let _flags = MessageFlags::from_u8(buf.get_u8());
124
125 let msg_len = buf.get_u32_le() as usize;
127
128 let actual_body_len = buf.remaining();
129 let legacy_msg_len = actual_body_len.checked_sub(LEGACY_OMITTED_LENGTH_SIZE);
130 if msg_len != actual_body_len && legacy_msg_len != Some(msg_len) {
131 return Err(TransportError::Protocol(format!(
132 "Message length mismatch: declared {}, actual {}",
133 msg_len, actual_body_len
134 )));
135 }
136 let total_size = FRAME_PREFIX_SIZE.checked_add(actual_body_len).ok_or(
137 TransportError::MessageTooLarge {
138 size: usize::MAX,
139 max: MAX_MESSAGE_SIZE,
140 },
141 )?;
142 if total_size > MAX_MESSAGE_SIZE {
143 return Err(TransportError::MessageTooLarge {
144 size: total_size,
145 max: MAX_MESSAGE_SIZE,
146 });
147 }
148
149 let id = MessageId(buf.get_u64_le());
151
152 let msg_type = MessageType::from_u8(buf.get_u8())
154 .map_err(|e| TransportError::Protocol(e.to_string()))?;
155
156 let method_len = buf.get_u16_le() as usize;
158 if buf.remaining() < method_len {
159 return Err(TransportError::Protocol(
160 "Incomplete method name".to_string(),
161 ));
162 }
163 let mut method_bytes = vec![0u8; method_len];
164 buf.copy_to_slice(&mut method_bytes);
165 let method = String::from_utf8(method_bytes)
166 .map_err(|e| TransportError::Protocol(format!("Invalid method name: {}", e)))?;
167
168 if buf.remaining() < std::mem::size_of::<u32>() {
170 return Err(TransportError::Protocol(
171 "Missing payload length".to_string(),
172 ));
173 }
174 let payload_len = buf.get_u32_le() as usize;
175 if buf.remaining() < payload_len {
176 return Err(TransportError::Protocol("Incomplete payload".to_string()));
177 }
178 let mut payload_bytes = vec![0u8; payload_len];
179 buf.copy_to_slice(&mut payload_bytes);
180
181 if buf.remaining() < std::mem::size_of::<u32>() {
183 return Err(TransportError::Protocol(
184 "Missing metadata length".to_string(),
185 ));
186 }
187 let metadata_len = buf.get_u32_le() as usize;
188 if buf.remaining() != metadata_len {
189 return Err(TransportError::Protocol(format!(
190 "Metadata length mismatch: declared {}, remaining {}",
191 metadata_len,
192 buf.remaining()
193 )));
194 }
195 let mut metadata_bytes = vec![0u8; metadata_len];
196 buf.copy_to_slice(&mut metadata_bytes);
197
198 let metadata: MessageMetadata = bincode::deserialize(&metadata_bytes)
200 .map_err(|e| TransportError::Protocol(format!("Invalid metadata: {}", e)))?;
201
202 let payload = Self::decompress_payload(&payload_bytes, &metadata)?;
204
205 Ok(Self {
206 id,
207 msg_type,
208 method,
209 payload,
210 metadata,
211 codec: C::default(),
212 })
213 }
214}
215
216impl<C: Codec> Message<C> {
217 pub(crate) fn error_with_codec(id: MessageId, error_msg: impl Into<String>, codec: C) -> Self {
218 let error_msg = error_msg.into();
219 let payload = codec.encode(&error_msg).unwrap_or_default();
220 Self {
221 id,
222 msg_type: MessageType::Error,
223 method: String::new(),
224 payload: Bytes::from(payload),
225 metadata: MessageMetadata::new(),
226 codec,
227 }
228 }
229
230 pub(crate) fn stream_error_with_codec(
231 id: MessageId,
232 stream_id: u64,
233 error_msg: impl Into<String>,
234 codec: C,
235 ) -> Self {
236 let error_msg = error_msg.into();
237 let payload = codec.encode(&error_msg).unwrap_or_default();
238 Self {
239 id,
240 msg_type: MessageType::Error,
241 method: String::new(),
242 payload: Bytes::from(payload),
243 metadata: MessageMetadata::new().with_stream(stream_id, 0),
244 codec,
245 }
246 }
247
248 pub(crate) fn new_with_codec(
249 id: MessageId,
250 msg_type: MessageType,
251 method: impl Into<String>,
252 payload: Bytes,
253 metadata: MessageMetadata,
254 codec: C,
255 ) -> Self {
256 Self {
257 id,
258 msg_type,
259 method: method.into(),
260 payload,
261 metadata,
262 codec,
263 }
264 }
265
266 pub(crate) fn call_with_codec<T: Serialize>(
267 method: impl Into<String>,
268 request: T,
269 codec: C,
270 ) -> Result<Self> {
271 let payload = codec.encode(&request)?;
272 Ok(Self::new_with_codec(
273 MessageId::new(),
274 MessageType::Call,
275 method,
276 Bytes::from(payload),
277 MessageMetadata::new(),
278 codec,
279 ))
280 }
281
282 pub(crate) fn notification_with_codec<T: Serialize>(
283 method: impl Into<String>,
284 data: T,
285 codec: C,
286 ) -> Result<Self> {
287 let payload = codec.encode(&data)?;
288 Ok(Self::new_with_codec(
289 MessageId::new(),
290 MessageType::Notification,
291 method,
292 Bytes::from(payload),
293 MessageMetadata::new(),
294 codec,
295 ))
296 }
297
298 pub fn encode(&self) -> TransportResult<BytesMut> {
300 let method_bytes = self.method.as_bytes();
301 let method_len = method_bytes.len();
302
303 if method_len > u16::MAX as usize {
304 return Err(TransportError::Protocol("Method name too long".to_string()));
305 }
306
307 let payload_to_write = self.compress_payload()?;
309
310 let metadata_bytes = bincode::serialize(&self.metadata)
312 .map_err(|e| TransportError::Protocol(e.to_string()))?;
313
314 let total_size = MIN_HEADER_SIZE
316 .checked_add(method_len)
317 .and_then(|size| size.checked_add(payload_to_write.len()))
318 .and_then(|size| size.checked_add(metadata_bytes.len()))
319 .ok_or(TransportError::MessageTooLarge {
320 size: usize::MAX,
321 max: MAX_MESSAGE_SIZE,
322 })?;
323
324 if total_size > MAX_MESSAGE_SIZE {
325 return Err(TransportError::MessageTooLarge {
326 size: total_size,
327 max: MAX_MESSAGE_SIZE,
328 });
329 }
330
331 let mut buf = BytesMut::with_capacity(total_size);
332
333 buf.put_slice(&MAGIC);
335
336 buf.put_u8(VERSION);
338
339 let flags = MessageFlags {
341 compressed: self.metadata.compression != CompressionType::None,
342 streaming: matches!(
343 self.msg_type,
344 MessageType::StreamChunk | MessageType::StreamEnd
345 ),
346 batch: false,
347 };
348 buf.put_u8(flags.to_u8());
349
350 let msg_len = total_size - FRAME_PREFIX_SIZE;
352 buf.put_u32_le(msg_len as u32);
353
354 buf.put_u64_le(self.id.0);
356
357 buf.put_u8(self.msg_type.to_u8());
359
360 buf.put_u16_le(method_len as u16);
362 buf.put_slice(method_bytes);
363
364 buf.put_u32_le(payload_to_write.len() as u32);
366 buf.put_slice(&payload_to_write);
367
368 buf.put_u32_le(metadata_bytes.len() as u32);
370 buf.put_slice(&metadata_bytes);
371
372 Ok(buf)
373 }
374
375 fn compress_payload(&self) -> TransportResult<Vec<u8>> {
377 match self.metadata.compression {
378 CompressionType::None => Ok(self.payload.to_vec()),
379 CompressionType::Lz4 => lz4::block::compress(&self.payload, None, true)
380 .map_err(|e| TransportError::Protocol(format!("LZ4 compression failed: {}", e))),
381 CompressionType::Zstd => zstd::bulk::compress(&self.payload, 3)
382 .map_err(|e| TransportError::Protocol(format!("Zstd compression failed: {}", e))),
383 }
384 }
385
386 fn decompress_payload(compressed: &[u8], metadata: &MessageMetadata) -> TransportResult<Bytes> {
388 let decompressed = match metadata.compression {
389 CompressionType::None => compressed.to_vec(),
390 CompressionType::Lz4 => lz4::block::decompress(compressed, None).map_err(|e| {
391 TransportError::Protocol(format!("LZ4 decompression failed: {}", e))
392 })?,
393 CompressionType::Zstd => {
394 zstd::bulk::decompress(compressed, MAX_MESSAGE_SIZE).map_err(|e| {
395 TransportError::Protocol(format!("Zstd decompression failed: {}", e))
396 })?
397 }
398 };
399
400 Ok(Bytes::from(decompressed))
401 }
402
403 pub fn deserialize_payload<T: for<'de> Deserialize<'de>>(&self) -> Result<T> {
404 self.codec.decode(&self.payload)
405 }
406}
407
408#[cfg(test)]
409mod tests {
410 use super::*;
411 use crate::codec::JsonCodec;
412
413 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
414 struct Request {
415 value1: String,
416 value2: i32,
417 }
418
419 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
420 struct Response {
421 result: bool,
422 }
423
424 #[test]
425 fn test_message_call_reply() {
426 let req = Request {
427 value1: "test".to_string(),
428 value2: 42,
429 };
430
431 let call_msg = Message::<BincodeCodec>::call("test_method", req.clone()).unwrap();
433 assert_eq!(call_msg.msg_type, MessageType::Call);
434 assert_eq!(call_msg.method, "test_method");
435
436 let mut buf = call_msg.encode().unwrap();
438 let decoded_msg: Message = Message::decode(&mut buf).unwrap();
439
440 assert_eq!(decoded_msg.id, call_msg.id);
441 assert_eq!(decoded_msg.method, call_msg.method);
442
443 let decoded_req: Request = decoded_msg.deserialize_payload().unwrap();
445 assert_eq!(decoded_req, req);
446
447 let resp = Response { result: true };
449 let reply_msg = Message::<BincodeCodec>::reply(call_msg.id, resp.clone()).unwrap();
450
451 let mut buf = reply_msg.encode().unwrap();
452 let decoded_reply: Message = Message::decode(&mut buf).unwrap();
453
454 let decoded_resp: Response = decoded_reply.deserialize_payload().unwrap();
455 assert_eq!(decoded_resp, resp);
456 }
457
458 #[test]
459 fn test_encoded_message_length_covers_complete_body() {
460 let message = Message::<BincodeCodec>::notification("event", 42u32).unwrap();
461 let encoded = message.encode().unwrap();
462 let declared = u32::from_le_bytes(encoded[6..10].try_into().unwrap()) as usize;
463
464 assert_eq!(declared, encoded.len() - FRAME_PREFIX_SIZE);
465 }
466
467 #[test]
468 fn test_decode_accepts_legacy_understated_message_length() {
469 let message = Message::<BincodeCodec>::notification("event", 42u32).unwrap();
470 let mut encoded = message.encode().unwrap();
471 let declared = u32::from_le_bytes(encoded[6..10].try_into().unwrap());
472 encoded[6..10]
473 .copy_from_slice(&(declared - LEGACY_OMITTED_LENGTH_SIZE as u32).to_le_bytes());
474
475 let decoded: Message = Message::decode(encoded.freeze()).unwrap();
476
477 assert_eq!(decoded.method, "event");
478 }
479
480 #[test]
481 fn test_decode_rejects_truncated_and_oversized_fields_without_panicking() {
482 let mut truncated = BytesMut::with_capacity(19);
483 truncated.put_slice(&MAGIC);
484 truncated.put_u8(VERSION);
485 truncated.put_u8(0);
486 truncated.put_u32_le(9);
487 truncated.put_u64_le(1);
488 truncated.put_u8(MessageType::Call.to_u8());
489 assert!(matches!(
490 Message::<BincodeCodec>::decode(truncated.freeze()),
491 Err(TransportError::Protocol(_))
492 ));
493
494 let message = Message::<BincodeCodec>::notification("event", 42u32).unwrap();
495 let mut oversized_method = message.encode().unwrap();
496 oversized_method[19..21].copy_from_slice(&u16::MAX.to_le_bytes());
497 assert!(matches!(
498 Message::<BincodeCodec>::decode(oversized_method.freeze()),
499 Err(TransportError::Protocol(_))
500 ));
501 }
502
503 #[test]
504 fn test_json_codec() {
505 let req = Request {
506 value1: "json".to_string(),
507 value2: 123,
508 };
509
510 let call_msg = Message::<JsonCodec>::call("json_method", req.clone()).unwrap();
512
513 let payload_str = std::str::from_utf8(&call_msg.payload).unwrap();
515 assert!(payload_str.contains("\"value1\":\"json\""));
516
517 let mut buf = call_msg.encode().unwrap();
518 let decoded_msg: Message<JsonCodec> = Message::decode(&mut buf).unwrap();
519
520 let decoded_req: Request = decoded_msg.deserialize_payload().unwrap();
521 assert_eq!(decoded_req, req);
522 }
523
524 #[test]
525 fn test_metadata() {
526 let req = Request {
527 value1: "s".to_string(),
528 value2: 1,
529 };
530 let mut msg = Message::<BincodeCodec>::call("m", req).unwrap();
531
532 msg.metadata = msg
533 .metadata
534 .with_timeout(1000)
535 .with_compression(CompressionType::Lz4);
536
537 let mut buf = msg.encode().unwrap();
538 let decoded: Message = Message::decode(&mut buf).unwrap();
539
540 assert_eq!(decoded.metadata.timeout_ms, Some(1000));
541 assert_eq!(decoded.metadata.compression, CompressionType::Lz4);
542 }
543
544 #[test]
545 fn test_streaming_flag() {
546 let chunk = Message::<BincodeCodec>::stream_chunk(1, 0, 42i32).unwrap();
548 let buf = chunk.encode().unwrap();
549 let flags = MessageFlags::from_u8(buf[5]); assert!(flags.streaming, "StreamChunk should have streaming flag");
551
552 let end = Message::<BincodeCodec>::stream_end(1);
554 let buf = end.encode().unwrap();
555 let flags = MessageFlags::from_u8(buf[5]);
556 assert!(flags.streaming, "StreamEnd should have streaming flag");
557
558 let call = Message::<BincodeCodec>::call("test", ()).unwrap();
560 let buf = call.encode().unwrap();
561 let flags = MessageFlags::from_u8(buf[5]);
562 assert!(!flags.streaming, "Call should not have streaming flag");
563 }
564}