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 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}