oddity_rtsp_protocol/
serialize.rs

1use bytes::{BufMut, BytesMut};
2
3use super::{
4    error::{Error, Result},
5    message::{Method, StatusCode, Uri, Version},
6    request::Request,
7    response::Response,
8};
9
10pub trait Serialize {
11    fn serialize(self, dst: &mut BytesMut) -> Result<()>;
12}
13
14impl Serialize for Request {
15    fn serialize(self, dst: &mut BytesMut) -> Result<()> {
16        self.method.serialize(dst)?;
17        dst.put_u8(b' ');
18        self.uri.serialize(dst)?;
19        dst.put_u8(b' ');
20        self.version.serialize(dst)?;
21        dst.put_u8(b'\r');
22        dst.put_u8(b'\n');
23
24        for (var, val) in self.headers.into_iter() {
25            dst.put(format!("{}: {}\r\n", var, val).as_bytes());
26        }
27
28        dst.put(b"\r\n".as_slice());
29
30        if let Some(body) = self.body {
31            dst.put(body);
32        }
33
34        Ok(())
35    }
36}
37
38impl Serialize for Response {
39    fn serialize(self, dst: &mut BytesMut) -> Result<()> {
40        self.version.serialize(dst)?;
41        dst.put_u8(b' ');
42        self.status.serialize(dst)?;
43        dst.put_u8(b' ');
44        dst.put(self.reason.as_bytes());
45        dst.put_u8(b'\r');
46        dst.put_u8(b'\n');
47
48        for (var, val) in self.headers.into_iter() {
49            dst.put(format!("{}: {}\r\n", var, val).as_bytes());
50        }
51
52        dst.put(b"\r\n".as_slice());
53
54        if let Some(body) = self.body {
55            dst.put(body);
56        }
57
58        Ok(())
59    }
60}
61
62impl Serialize for Version {
63    fn serialize(self, dst: &mut BytesMut) -> Result<()> {
64        let version = match self {
65            Version::V1 => b"RTSP/1.0".as_slice(),
66            Version::V2 => b"RTSP/2.0".as_slice(),
67            Version::Unknown => return Err(Error::VersionUnknown),
68        };
69
70        dst.put(version);
71        Ok(())
72    }
73}
74
75impl Serialize for Method {
76    fn serialize(self, dst: &mut BytesMut) -> Result<()> {
77        let method = match self {
78            Method::Describe => b"DESCRIBE".as_slice(),
79            Method::Announce => b"ANNOUNCE".as_slice(),
80            Method::Setup => b"SETUP".as_slice(),
81            Method::Play => b"PLAY".as_slice(),
82            Method::Pause => b"PAUSE".as_slice(),
83            Method::Record => b"RECORD".as_slice(),
84            Method::Options => b"OPTIONS".as_slice(),
85            Method::Redirect => b"REDIRECT".as_slice(),
86            Method::Teardown => b"TEARDOWN".as_slice(),
87            Method::GetParameter => b"GET_PARAMETER".as_slice(),
88            Method::SetParameter => b"SET_PARAMETER".as_slice(),
89        };
90
91        dst.put(method);
92        Ok(())
93    }
94}
95
96impl Serialize for Uri {
97    fn serialize(self, dst: &mut BytesMut) -> Result<()> {
98        dst.put(self.to_string().as_bytes());
99        Ok(())
100    }
101}
102
103impl Serialize for StatusCode {
104    fn serialize(self, dst: &mut BytesMut) -> Result<()> {
105        dst.put(self.to_string().as_bytes());
106        Ok(())
107    }
108}
109
110#[cfg(test)]
111mod tests {
112
113    // FIXME A bunch of macros could make this way more readable.
114
115    use std::collections::BTreeMap;
116
117    use bytes::{Bytes, BytesMut};
118
119    use crate::{message::Message, request::RequestMetadata, response::ResponseMetadata};
120
121    use super::{Error, Method, Request, Response, Serialize, Version};
122
123    #[test]
124    fn serialize_options_request() {
125        let request_bytes = Bytes::from(
126            b"OPTIONS rtsp://example.com/media.mp4 RTSP/1.0\r\n\
127CSeq: 1\r\n\
128Proxy-Require: gzipped-messages\r\n\
129Require: implicit-play\r\n\
130\r\n\
131"
132            .as_slice(),
133        );
134
135        let request = Request::new(
136            RequestMetadata::new(
137                Method::Options,
138                "rtsp://example.com/media.mp4".try_into().unwrap(),
139                Version::V1,
140            ),
141            BTreeMap::from([
142                ("CSeq".to_string(), "1".to_string()),
143                ("Proxy-Require".to_string(), "gzipped-messages".to_string()),
144                ("Require".to_string(), "implicit-play".to_string()),
145            ]),
146            None,
147        );
148
149        let mut request_serialized = BytesMut::new();
150        request.serialize(&mut request_serialized).unwrap();
151        assert_eq!(request_serialized, request_bytes);
152    }
153
154    #[test]
155    fn serialize_options_request_headers_alphabetical() {
156        let request_bytes = Bytes::from(
157            b"OPTIONS rtsp://example.com/media.mp4 RTSP/1.0\r\n\
158Aaa: value\r\n\
159Bbb: value\r\n\
160C: value\r\n\
161Ca: value\r\n\
162Cb: value\r\n\
163Cc: value\r\n\
164\r\n\
165"
166            .as_slice(),
167        );
168
169        let request = Request::new(
170            RequestMetadata::new(
171                Method::Options,
172                "rtsp://example.com/media.mp4".try_into().unwrap(),
173                Version::V1,
174            ),
175            BTreeMap::from([
176                ("Cc".to_string(), "value".to_string()),
177                ("C".to_string(), "value".to_string()),
178                ("Cb".to_string(), "value".to_string()),
179                ("Bbb".to_string(), "value".to_string()),
180                ("Aaa".to_string(), "value".to_string()),
181                ("Ca".to_string(), "value".to_string()),
182            ]),
183            None,
184        );
185
186        let mut request_serialized = BytesMut::new();
187        request.serialize(&mut request_serialized).unwrap();
188        assert_eq!(request_serialized, request_bytes);
189    }
190
191    #[test]
192    fn serialize_options_request_any() {
193        let request_bytes = Bytes::from(
194            b"OPTIONS * RTSP/1.0\r\n\
195CSeq: 1\r\n\
196\r\n\
197"
198            .as_slice(),
199        );
200
201        let request = Request::new(
202            RequestMetadata::new(Method::Options, "*".try_into().unwrap(), Version::V1),
203            BTreeMap::from([("CSeq".to_string(), "1".to_string())]),
204            None,
205        );
206
207        let mut request_serialized = BytesMut::new();
208        request.serialize(&mut request_serialized).unwrap();
209        assert_eq!(request_serialized, request_bytes);
210    }
211
212    #[test]
213    fn serialize_options_response() {
214        let response_bytes = Bytes::from(
215            b"RTSP/1.0 200 OK\r\n\
216CSeq: 1\r\n\
217Public: DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE\r\n\
218\r\n\
219"
220            .as_slice(),
221        );
222
223        let response = Response::new(
224            ResponseMetadata::new(Version::V1, 200, "OK".to_string()),
225            BTreeMap::from([
226                ("CSeq".to_string(), "1".to_string()),
227                (
228                    "Public".to_string(),
229                    "DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE".to_string(),
230                ),
231            ]),
232            None,
233        );
234
235        let mut response_serialized = BytesMut::new();
236        response.serialize(&mut response_serialized).unwrap();
237        assert_eq!(response_serialized, response_bytes);
238    }
239
240    #[test]
241    fn serialize_options_response_error() {
242        let response_bytes = Bytes::from(
243            b"RTSP/1.0 404 Stream Not Found\r\n\
244CSeq: 1\r\n\
245\r\n\
246"
247            .as_slice(),
248        );
249
250        let response = Response::new(
251            ResponseMetadata::new(Version::V1, 404, "Stream Not Found".to_string()),
252            BTreeMap::from([("CSeq".to_string(), "1".to_string())]),
253            None,
254        );
255
256        let mut response_serialized = BytesMut::new();
257        response.serialize(&mut response_serialized).unwrap();
258        assert_eq!(response_serialized, response_bytes);
259    }
260
261    #[test]
262    fn serialize_describe_request() {
263        let request_bytes = Bytes::from(
264            b"DESCRIBE rtsp://example.com/media.mp4 RTSP/1.0\r\n\
265CSeq: 2\r\n\
266\r\n\
267"
268            .as_slice(),
269        );
270
271        let request = Request::new(
272            RequestMetadata::new(
273                Method::Describe,
274                "rtsp://example.com/media.mp4".try_into().unwrap(),
275                Version::V1,
276            ),
277            BTreeMap::from([("CSeq".to_string(), "2".to_string())]),
278            None,
279        );
280
281        let mut request_serialized = BytesMut::new();
282        request.serialize(&mut request_serialized).unwrap();
283        assert_eq!(request_serialized, request_bytes);
284    }
285
286    #[test]
287    fn serialize_describe_request_v2() {
288        let request_bytes = Bytes::from(
289            b"DESCRIBE rtsp://example.com/media.mp4 RTSP/2.0\r\n\
290CSeq: 2\r\n\
291\r\n\
292"
293            .as_slice(),
294        );
295
296        let request = Request::new(
297            RequestMetadata::new(
298                Method::Describe,
299                "rtsp://example.com/media.mp4".try_into().unwrap(),
300                Version::V2,
301            ),
302            BTreeMap::from([("CSeq".to_string(), "2".to_string())]),
303            None,
304        );
305
306        let mut request_serialized = BytesMut::new();
307        request.serialize(&mut request_serialized).unwrap();
308        assert_eq!(request_serialized, request_bytes);
309    }
310
311    #[test]
312    fn serialize_describe_request_version_unknown_errors() {
313        let request = Request::new(
314            RequestMetadata::new(
315                Method::Describe,
316                "rtsp://example.com/media.mp4".try_into().unwrap(),
317                Version::Unknown,
318            ),
319            BTreeMap::from([("CSeq".to_string(), "2".to_string())]),
320            None,
321        );
322
323        let mut request_serialized = BytesMut::new();
324        assert!(matches!(
325            request.serialize(&mut request_serialized),
326            Err(Error::VersionUnknown)
327        ))
328    }
329
330    #[test]
331    fn serialize_describe_response() {
332        let response_bytes = Bytes::from(
333            b"RTSP/1.0 200 OK\r\n\
334CSeq: 2\r\n\
335Content-Base: rtsp://example.com/media.mp4\r\n\
336Content-Length: 443\r\n\
337Content-Type: application/sdp\r\n\
338\r\n\
339m=video 0 RTP/AVP 96
340a=control:streamid=0
341a=range:npt=0-7.741000
342a=length:npt=7.741000
343a=rtpmap:96 MP4V-ES/5544
344a=mimetype:string;\"video/MP4V-ES\"
345a=AvgBitRate:integer;304018
346a=StreamName:string;\"hinted video track\"
347m=audio 0 RTP/AVP 97
348a=control:streamid=1
349a=range:npt=0-7.712000
350a=length:npt=7.712000
351a=rtpmap:97 mpeg4-generic/32000/2
352a=mimetype:string;\"audio/mpeg4-generic\"
353a=AvgBitRate:integer;65790
354a=StreamName:string;\"hinted audio track\""
355                .as_slice(),
356        );
357
358        let response = Response::new(
359            ResponseMetadata::new(Version::V1, 200, "OK".to_string()),
360            BTreeMap::from([
361                ("CSeq".to_string(), "2".to_string()),
362                (
363                    "Content-Base".to_string(),
364                    "rtsp://example.com/media.mp4".to_string(),
365                ),
366                ("Content-Length".to_string(), "443".to_string()),
367                ("Content-Type".to_string(), "application/sdp".to_string()),
368            ]),
369            Some(Bytes::from(
370                b"m=video 0 RTP/AVP 96
371a=control:streamid=0
372a=range:npt=0-7.741000
373a=length:npt=7.741000
374a=rtpmap:96 MP4V-ES/5544
375a=mimetype:string;\"video/MP4V-ES\"
376a=AvgBitRate:integer;304018
377a=StreamName:string;\"hinted video track\"
378m=audio 0 RTP/AVP 97
379a=control:streamid=1
380a=range:npt=0-7.712000
381a=length:npt=7.712000
382a=rtpmap:97 mpeg4-generic/32000/2
383a=mimetype:string;\"audio/mpeg4-generic\"
384a=AvgBitRate:integer;65790
385a=StreamName:string;\"hinted audio track\""
386                    .as_slice(),
387            )),
388        );
389
390        let mut response_serialized = BytesMut::new();
391        response.serialize(&mut response_serialized).unwrap();
392        assert_eq!(response_serialized, response_bytes);
393    }
394
395    #[test]
396    fn serialize_play_request() {
397        let request_bytes = Bytes::from(
398            b"PLAY rtsp://example.com/stream/0 RTSP/1.0\r\n\
399CSeq: 1\r\n\
400Content-Length: 16\r\n\
401Session: 1234abcd\r\n\
402\r\n\
4030123456789abcdef"
404                .as_slice(),
405        );
406
407        let request = Request::new(
408            RequestMetadata::new(
409                Method::Play,
410                "rtsp://example.com/stream/0".try_into().unwrap(),
411                Version::V1,
412            ),
413            BTreeMap::from([
414                ("CSeq".to_string(), "1".to_string()),
415                ("Content-Length".to_string(), "16".to_string()),
416                ("Session".to_string(), "1234abcd".to_string()),
417            ]),
418            Some(Bytes::from(b"0123456789abcdef".as_slice())),
419        );
420
421        let mut request_serialized = BytesMut::new();
422        request.serialize(&mut request_serialized).unwrap();
423        assert_eq!(request_serialized, request_bytes);
424    }
425}