up_rust/frame/
envelope.rs1use std::{error::Error, fmt::Display};
35
36use bytes::Bytes;
37
38#[cfg(feature = "protobuf-support")]
39use crate::{ProtobufMappable, SerializationError, UMessage};
40use crate::{UFrameMetadataError, UOwnedFrame};
41
42#[derive(Clone, Debug, Eq, PartialEq)]
44pub enum UFrameWireError {
45 InvalidFrame(String),
47 UnsupportedPayloadEncoding(String),
49 SerializationError(String),
51}
52
53impl UFrameWireError {
54 #[must_use]
56 pub fn invalid_frame(message: impl Into<String>) -> Self {
57 Self::InvalidFrame(message.into())
58 }
59
60 #[must_use]
62 pub fn unsupported_payload_encoding(message: impl Into<String>) -> Self {
63 Self::UnsupportedPayloadEncoding(message.into())
64 }
65
66 #[must_use]
68 pub fn serialization_error(message: impl Into<String>) -> Self {
69 Self::SerializationError(message.into())
70 }
71}
72
73impl Display for UFrameWireError {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 match self {
76 Self::InvalidFrame(message) => write!(f, "invalid frame: {message}"),
77 Self::UnsupportedPayloadEncoding(message) => {
78 write!(f, "unsupported payload encoding: {message}")
79 }
80 Self::SerializationError(message) => write!(f, "frame serialization error: {message}"),
81 }
82 }
83}
84
85impl Error for UFrameWireError {}
86
87#[cfg(feature = "protobuf-support")]
88impl From<SerializationError> for UFrameWireError {
89 fn from(value: SerializationError) -> Self {
90 Self::serialization_error(value.to_string())
91 }
92}
93
94impl From<UFrameMetadataError> for UFrameWireError {
95 fn from(value: UFrameMetadataError) -> Self {
96 match value {
97 UFrameMetadataError::EncodingNotRepresentable { encoding } => {
98 Self::unsupported_payload_encoding(format!(
99 "payload encoding `{encoding}` cannot be represented by generated UMessage payload_format"
100 ))
101 }
102 other @ (UFrameMetadataError::EmptyPayloadEncoding
103 | UFrameMetadataError::EmptyCustomEncodingId
104 | UFrameMetadataError::EmptyCustomEncodingContentType
105 | UFrameMetadataError::InvalidCustomEncodingContentType(_)
106 | UFrameMetadataError::RegisteredPayloadEncodingAlias { .. }
107 | UFrameMetadataError::UnspecifiedPayloadFormat
108 | UFrameMetadataError::PayloadWithoutEncoding
109 | UFrameMetadataError::EncodingWithoutPayload
110 | UFrameMetadataError::FieldNotRepresentable { .. }
111 | UFrameMetadataError::InvalidMetadata(_)
112 | UFrameMetadataError::MessageBuildError(_)) => Self::invalid_frame(other.to_string()),
113 }
114 }
115}
116
117pub trait UFrameWireFormat {
126 fn name() -> &'static str;
128
129 fn content_type() -> &'static str;
131
132 fn serialize_frame(frame: &UOwnedFrame) -> Result<Bytes, UFrameWireError>;
139
140 fn deserialize_frame(src: &[u8]) -> Result<UOwnedFrame, UFrameWireError>;
146}
147
148#[cfg(feature = "protobuf-support")]
156#[derive(Debug)]
157pub struct ProtobufUMessageFrame;
158
159#[cfg(feature = "protobuf-support")]
160impl UFrameWireFormat for ProtobufUMessageFrame {
161 fn name() -> &'static str {
162 "protobuf-umessage"
163 }
164
165 fn content_type() -> &'static str {
166 "application/x-uprotocol-umessage+protobuf"
167 }
168
169 fn serialize_frame(frame: &UOwnedFrame) -> Result<Bytes, UFrameWireError> {
170 let message = crate::frame::metadata::try_project_frame_to_umessage(
171 frame.metadata().clone(),
172 frame.payload().cloned(),
173 )?;
174 message
175 .write_to_protobuf_bytes()
176 .map(Bytes::from)
177 .map_err(UFrameWireError::from)
178 }
179
180 fn deserialize_frame(src: &[u8]) -> Result<UOwnedFrame, UFrameWireError> {
181 let message = UMessage::parse_from_protobuf_bytes(src)?;
182 let metadata = crate::frame::metadata::try_project_umessage_to_frame_metadata(&message)?;
183 let payload = message.payload().as_deref().map(Bytes::copy_from_slice);
184 UOwnedFrame::new(metadata, payload).map_err(UFrameWireError::from)
185 }
186}
187
188#[derive(Debug)]
213pub struct NativeUFrameEnvelope;
214
215pub const NATIVE_ENVELOPE_MAGIC: [u8; 4] = *b"UPFE";
217pub const NATIVE_ENVELOPE_VERSION: u8 = 1;
219pub const NATIVE_ENVELOPE_HEADER_LEN: usize = 20;
221
222impl UFrameWireFormat for NativeUFrameEnvelope {
223 fn name() -> &'static str {
224 "native-uframe-envelope"
225 }
226
227 fn content_type() -> &'static str {
228 "application/vnd.uprotocol.uframe;version=1"
229 }
230
231 fn serialize_frame(frame: &UOwnedFrame) -> Result<Bytes, UFrameWireError> {
232 let metadata = crate::frame::codec::encode_frame_metadata_fields(frame.metadata())
234 .map_err(|error| UFrameWireError::invalid_frame(error.to_string()))?;
235 let metadata_len = u32::try_from(metadata.len()).map_err(|_| {
236 UFrameWireError::invalid_frame("metadata exceeds the u32 envelope limit")
237 })?;
238 let payload = frame.payload().map(bytes::Bytes::as_ref);
239 let payload_len = payload.map_or(0_u64, |payload| payload.len() as u64);
240
241 let mut out = Vec::with_capacity(
242 NATIVE_ENVELOPE_HEADER_LEN + metadata.len() + payload.map_or(0, <[u8]>::len),
243 );
244 out.extend_from_slice(&NATIVE_ENVELOPE_MAGIC);
245 out.push(NATIVE_ENVELOPE_VERSION);
246 out.push(u8::from(payload.is_some()));
247 out.extend_from_slice(&0_u16.to_le_bytes());
248 out.extend_from_slice(&metadata_len.to_le_bytes());
249 out.extend_from_slice(&payload_len.to_le_bytes());
250 out.extend_from_slice(&metadata);
251 if let Some(payload) = payload {
252 out.extend_from_slice(payload);
253 }
254 Ok(Bytes::from(out))
255 }
256
257 fn deserialize_frame(src: &[u8]) -> Result<UOwnedFrame, UFrameWireError> {
258 let header = src
259 .get(..NATIVE_ENVELOPE_HEADER_LEN)
260 .ok_or_else(|| UFrameWireError::invalid_frame("input shorter than envelope header"))?;
261 let magic = header
262 .get(..4)
263 .ok_or_else(|| UFrameWireError::invalid_frame("input shorter than envelope magic"))?;
264 if magic != NATIVE_ENVELOPE_MAGIC {
265 return Err(UFrameWireError::invalid_frame("wrong envelope magic"));
266 }
267 let version = *header
268 .get(4)
269 .ok_or_else(|| UFrameWireError::invalid_frame("input shorter than envelope version"))?;
270 if version != NATIVE_ENVELOPE_VERSION {
271 return Err(UFrameWireError::invalid_frame(format!(
272 "unsupported envelope version {}",
273 version
274 )));
275 }
276 let payload_marker = *header.get(5).ok_or_else(|| {
277 UFrameWireError::invalid_frame("input shorter than payload presence marker")
278 })?;
279 let payload_present = match payload_marker {
280 0 => false,
281 1 => true,
282 other => {
283 return Err(UFrameWireError::invalid_frame(format!(
284 "invalid payload presence marker {other}"
285 )))
286 }
287 };
288 let reserved = header.get(6..8).ok_or_else(|| {
289 UFrameWireError::invalid_frame("input shorter than reserved envelope bytes")
290 })?;
291 if reserved != [0, 0] {
292 return Err(UFrameWireError::invalid_frame(
293 "reserved envelope bytes must be zero",
294 ));
295 }
296 let metadata_len_bytes = header
297 .get(8..12)
298 .ok_or_else(|| UFrameWireError::invalid_frame("input shorter than metadata length"))?;
299 let metadata_len =
300 u32::from_le_bytes(metadata_len_bytes.try_into().expect("4 bytes")) as usize;
301 let payload_len_bytes = header
302 .get(12..20)
303 .ok_or_else(|| UFrameWireError::invalid_frame("input shorter than payload length"))?;
304 let payload_len = u64::from_le_bytes(payload_len_bytes.try_into().expect("8 bytes"));
305
306 let body = src
307 .get(NATIVE_ENVELOPE_HEADER_LEN..)
308 .ok_or_else(|| UFrameWireError::invalid_frame("input shorter than envelope header"))?;
309 let metadata_bytes = body.get(..metadata_len).ok_or_else(|| {
310 UFrameWireError::invalid_frame("input shorter than declared metadata length")
311 })?;
312 let payload_bytes = body.get(metadata_len..).ok_or_else(|| {
313 UFrameWireError::invalid_frame("input shorter than declared metadata length")
314 })?;
315 if payload_bytes.len() as u64 != payload_len || (!payload_present && payload_len != 0) {
316 return Err(UFrameWireError::invalid_frame(
317 "payload length disagrees with envelope header",
318 ));
319 }
320
321 let metadata = crate::frame::codec::decode_frame_metadata_fields(metadata_bytes)
322 .map_err(|error| UFrameWireError::invalid_frame(error.to_string()))?;
323 let payload = payload_present.then(|| Bytes::copy_from_slice(payload_bytes));
324 UOwnedFrame::new(metadata, payload).map_err(UFrameWireError::from)
325 }
326}
327
328#[cfg(test)]
329mod tests {
330 use super::*;
331 #[cfg(feature = "protobuf-support")]
332 use crate::payload::codec::RawBytes;
333 use crate::{PayloadEncoding, UFrameMetadata, UUri};
334 use crate::{UMessageBuilder, UPayloadFormat};
335
336 fn topic() -> UUri {
337 UUri::try_from_parts("vehicle", 0x4210, 0x01, 0x9000).expect("topic")
338 }
339
340 fn metadata_with_raw_encoding() -> UFrameMetadata {
341 let message = UMessageBuilder::publish(topic())
342 .build_with_payload(Bytes::new(), UPayloadFormat::Raw)
343 .expect("message");
344 crate::frame::metadata::try_project_attributes_to_frame_metadata(
345 message.attributes(),
346 Some(RawBytes::encoding()),
347 )
348 .expect("metadata")
349 }
350
351 #[test]
352 fn protobuf_umessage_frame_round_trips_raw_payload() {
353 let frame = UOwnedFrame::with_payload(
354 metadata_with_raw_encoding(),
355 Bytes::from_static(b"raw payload"),
356 )
357 .expect("frame");
358
359 let encoded = ProtobufUMessageFrame::serialize_frame(&frame).expect("serialize frame");
360 let decoded =
361 ProtobufUMessageFrame::deserialize_frame(&encoded).expect("deserialize frame");
362
363 assert_eq!(decoded, frame);
364 }
365
366 #[test]
367 fn protobuf_umessage_frame_round_trips_custom_payload_encoding() {
368 let message = UMessageBuilder::publish(topic()).build().expect("message");
369 let encoding =
370 PayloadEncoding::custom("com.example.native", "application/vnd.example.native")
371 .expect("custom encoding");
372 let metadata = crate::frame::metadata::try_project_attributes_to_frame_metadata(
373 message.attributes(),
374 Some(encoding.clone()),
375 )
376 .expect("metadata");
377 let frame = UOwnedFrame::with_payload(metadata, Bytes::from_static(b"native payload"))
378 .expect("frame");
379
380 let encoded = ProtobufUMessageFrame::serialize_frame(&frame).expect("serialize frame");
381 let decoded =
382 ProtobufUMessageFrame::deserialize_frame(&encoded).expect("deserialize frame");
383
384 assert_eq!(decoded.metadata().payload_encoding(), Some(&encoding));
385 assert_eq!(
386 decoded.payload(),
387 Some(&Bytes::from_static(b"native payload"))
388 );
389 }
390
391 #[test]
392 fn protobuf_umessage_frame_rejects_invalid_metadata() {
393 let message = UMessageBuilder::publish(topic()).build().expect("message");
394 let metadata = crate::frame::metadata::try_project_umessage_to_frame_metadata(&message)
395 .expect("metadata");
396 let frame = UOwnedFrame::with_payload_unchecked(metadata, Bytes::from_static(b"payload"));
398
399 let error = frame.validate().unwrap_err();
402
403 assert!(matches!(error, UFrameMetadataError::PayloadWithoutEncoding));
404 }
405
406 #[test]
407 fn protobuf_umessage_frame_round_trips_unspecified_absent_payload() {
408 let message = UMessageBuilder::publish(topic()).build().expect("message");
409 let metadata = crate::frame::metadata::try_project_attributes_to_frame_metadata(
410 message.attributes(),
411 None,
412 )
413 .expect("metadata");
414 let frame = UOwnedFrame::without_payload(metadata).expect("frame");
415
416 let encoded = ProtobufUMessageFrame::serialize_frame(&frame).expect("serialize frame");
417 let decoded =
418 ProtobufUMessageFrame::deserialize_frame(&encoded).expect("deserialize frame");
419
420 assert_eq!(decoded.metadata().payload_encoding(), None);
421 assert!(!decoded.has_payload());
422 }
423
424 #[test]
425 fn protobuf_umessage_frame_rejects_unknown_payload_format_with_payload() {
426 let message = UMessageBuilder::publish(topic())
427 .build_with_payload(Bytes::from_static(b"payload"), UPayloadFormat::Unspecified)
428 .expect("message");
429 let encoded = message
430 .write_to_protobuf_bytes()
431 .expect("serialize message");
432
433 let error = ProtobufUMessageFrame::deserialize_frame(&encoded).unwrap_err();
434
435 assert!(matches!(error, UFrameWireError::InvalidFrame(_)));
436 }
437
438 #[test]
439 fn native_envelope_round_trips_custom_encoding_frame() {
440 let metadata = UFrameMetadata::publish(topic())
441 .with_payload_encoding(
442 PayloadEncoding::custom("com.example.native", "application/vnd.example.native")
443 .expect("custom encoding"),
444 )
445 .build()
446 .expect("metadata");
447 let frame = UOwnedFrame::with_payload(metadata, Bytes::from_static(b"native payload"))
448 .expect("frame");
449
450 let encoded = NativeUFrameEnvelope::serialize_frame(&frame).expect("serialize frame");
451 let decoded = NativeUFrameEnvelope::deserialize_frame(&encoded).expect("deserialize");
452
453 assert_eq!(decoded, frame);
454 }
455
456 #[test]
457 fn native_envelope_preserves_present_empty_payload() {
458 let metadata = UFrameMetadata::publish(topic())
459 .with_payload_encoding(PayloadEncoding::RAW)
460 .build()
461 .expect("metadata");
462 let frame = UOwnedFrame::with_payload(metadata, Bytes::new()).expect("frame");
463
464 let decoded = NativeUFrameEnvelope::deserialize_frame(
465 &NativeUFrameEnvelope::serialize_frame(&frame).expect("serialize"),
466 )
467 .expect("deserialize");
468
469 assert!(decoded.has_payload());
470 assert_eq!(decoded.payload(), Some(&Bytes::new()));
471 }
472
473 #[test]
474 fn native_envelope_round_trips_absent_payload() {
475 let metadata = UFrameMetadata::publish(topic()).build().expect("metadata");
476 let frame = UOwnedFrame::without_payload(metadata).expect("frame");
477
478 let decoded = NativeUFrameEnvelope::deserialize_frame(
479 &NativeUFrameEnvelope::serialize_frame(&frame).expect("serialize"),
480 )
481 .expect("deserialize");
482
483 assert!(!decoded.has_payload());
484 assert_eq!(decoded, frame);
485 }
486
487 #[test]
488 fn native_envelope_rejects_corrupted_input() {
489 let metadata = UFrameMetadata::publish(topic()).build().expect("metadata");
490 let frame = UOwnedFrame::without_payload(metadata).expect("frame");
491 let encoded = NativeUFrameEnvelope::serialize_frame(&frame).expect("serialize");
492
493 let mut bad = encoded.to_vec();
494 *bad.first_mut().expect("magic byte") = b'X';
495 assert!(NativeUFrameEnvelope::deserialize_frame(&bad).is_err());
496
497 let mut bad = encoded.to_vec();
498 bad.push(0);
499 assert!(NativeUFrameEnvelope::deserialize_frame(&bad).is_err());
500 }
501}