oddity_rtsp_protocol/
transport.rs

1use std::fmt;
2use std::net::IpAddr;
3use std::str::FromStr;
4
5use super::{Error, Method};
6
7#[derive(Debug, Clone, PartialEq)]
8pub struct Transport {
9    lower: Option<Lower>,
10    parameters: Vec<Parameter>,
11}
12
13impl Transport {
14    pub fn new() -> Self {
15        Self {
16            lower: None,
17            parameters: Vec::new(),
18        }
19    }
20
21    pub fn with_lower_protocol(mut self, lower: Lower) -> Self {
22        self.lower = Some(lower);
23        self
24    }
25
26    pub fn with_parameter(mut self, parameter: Parameter) -> Self {
27        self.parameters.push(parameter);
28        self
29    }
30
31    pub fn with_parameters(mut self, parameters: impl IntoIterator<Item = Parameter>) -> Self {
32        self.parameters.extend(parameters);
33        self
34    }
35
36    pub fn lower_protocol(&self) -> Option<&Lower> {
37        self.lower.as_ref()
38    }
39
40    pub fn parameters(&self) -> &impl IntoIterator<Item = Parameter> {
41        &self.parameters
42    }
43
44    pub fn parameters_iter(&self) -> impl Iterator<Item = &Parameter> {
45        self.parameters.iter()
46    }
47
48    pub fn destination(&self) -> Option<&IpAddr> {
49        self.parameters_iter()
50            .filter_map(|parameter| {
51                if let Parameter::Destination(ip_addr) = parameter {
52                    Some(ip_addr)
53                } else {
54                    None
55                }
56            })
57            .next()
58    }
59
60    pub fn port(&self) -> Option<&Port> {
61        self.parameters_iter()
62            .filter_map(|parameter| {
63                if let Parameter::Port(port) = parameter {
64                    Some(port)
65                } else {
66                    None
67                }
68            })
69            .next()
70    }
71
72    pub fn client_port(&self) -> Option<&Port> {
73        self.parameters_iter()
74            .filter_map(|parameter| {
75                if let Parameter::ClientPort(port) = parameter {
76                    Some(port)
77                } else {
78                    None
79                }
80            })
81            .next()
82    }
83
84    pub fn server_port(&self) -> Option<&Port> {
85        self.parameters_iter()
86            .filter_map(|parameter| {
87                if let Parameter::ServerPort(port) = parameter {
88                    Some(port)
89                } else {
90                    None
91                }
92            })
93            .next()
94    }
95
96    pub fn interleaved_channel(&self) -> Option<&Channel> {
97        self.parameters_iter()
98            .filter_map(|parameter| {
99                if let Parameter::Interleaved(channel) = parameter {
100                    Some(channel)
101                } else {
102                    None
103                }
104            })
105            .next()
106    }
107}
108
109impl Default for Transport {
110    fn default() -> Self {
111        Self::new()
112    }
113}
114
115impl fmt::Display for Transport {
116    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
117        write!(f, "RTP/AVP")?;
118        if let Some(lower) = self.lower.as_ref() {
119            write!(f, "/{}", lower)?;
120        }
121        for parameter in self.parameters.iter() {
122            write!(f, ";{}", parameter)?;
123        }
124        Ok(())
125    }
126}
127
128impl FromStr for Transport {
129    type Err = Error;
130
131    fn from_str(s: &str) -> Result<Self, Self::Err> {
132        let (spec, params) = s
133            .split_once(';')
134            .map(|(spec, params)| (spec, Some(params)))
135            .unwrap_or_else(|| (s, None));
136
137        if spec.starts_with("RTP/AVP") {
138            let lower = spec
139                .split('/')
140                .nth(2)
141                .map(|lower| lower.parse())
142                .transpose()?;
143
144            let parameters = params
145                .map(|params| {
146                    params
147                        .split(';')
148                        .map(|p| p.parse())
149                        .collect::<Result<Vec<_>, _>>()
150                })
151                .transpose()?
152                .unwrap_or_default();
153
154            Ok(Transport { lower, parameters })
155        } else {
156            Err(Error::TransportProtocolProfileMissing {
157                value: s.to_string(),
158            })
159        }
160    }
161}
162
163#[derive(Debug, Clone, PartialEq)]
164pub enum Lower {
165    Tcp,
166    Udp,
167}
168
169impl fmt::Display for Lower {
170    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
171        match self {
172            Lower::Tcp => write!(f, "TCP"),
173            Lower::Udp => write!(f, "UDP"),
174        }
175    }
176}
177
178impl FromStr for Lower {
179    type Err = Error;
180
181    fn from_str(s: &str) -> Result<Self, Self::Err> {
182        match s {
183            "TCP" => Ok(Lower::Tcp),
184            "UDP" => Ok(Lower::Udp),
185            _ => Err(Error::TransportLowerUnknown {
186                value: s.to_string(),
187            }),
188        }
189    }
190}
191
192#[derive(Debug, Clone, PartialEq)]
193pub enum Parameter {
194    Unicast,
195    Multicast,
196    Destination(IpAddr),
197    Interleaved(Channel),
198    Append,
199    Ttl(usize),
200    Layers(usize),
201    Port(Port),
202    ClientPort(Port),
203    ServerPort(Port),
204    Ssrc(String),
205    Mode(Method),
206}
207
208impl fmt::Display for Parameter {
209    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
210        match self {
211            Parameter::Unicast => {
212                write!(f, "unicast")
213            }
214            Parameter::Multicast => {
215                write!(f, "multicast")
216            }
217            Parameter::Destination(host) => {
218                write!(f, "destination={}", host)
219            }
220            Parameter::Interleaved(channel) => {
221                write!(f, "interleaved={}", channel)
222            }
223            Parameter::Append => {
224                write!(f, "append")
225            }
226            Parameter::Ttl(ttl) => {
227                write!(f, "ttl={}", ttl)
228            }
229            Parameter::Layers(layers) => {
230                write!(f, "layers={}", layers)
231            }
232            Parameter::Port(port) => {
233                write!(f, "port={}", port)
234            }
235            Parameter::ClientPort(client_port) => {
236                write!(f, "client_port={}", client_port)
237            }
238            Parameter::ServerPort(server_port) => {
239                write!(f, "server_port={}", server_port)
240            }
241            Parameter::Ssrc(ssrc) => {
242                write!(f, "ssrc={}", ssrc)
243            }
244            Parameter::Mode(method) => {
245                write!(f, "mode=\"{}\"", method)
246            }
247        }
248    }
249}
250
251impl FromStr for Parameter {
252    type Err = Error;
253
254    fn from_str(s: &str) -> Result<Self, Self::Err> {
255        let mut parts = s.split('=');
256        let var = parts
257            .next()
258            .ok_or_else(|| Error::TransportParameterInvalid {
259                parameter: s.to_string(),
260            })?;
261
262        let mut val_or_err = || {
263            parts
264                .next()
265                .ok_or_else(|| Error::TransportParameterValueMissing {
266                    var: var.to_string(),
267                })
268        };
269
270        fn parse_or_err<T: FromStr>(var: &str, val: &str) -> Result<T, Error> {
271            val.parse::<T>()
272                .map_err(|_| Error::TransportParameterValueInvalid {
273                    var: var.to_string(),
274                    val: val.to_string(),
275                })
276        }
277
278        match var {
279            "unicast" => Ok(Parameter::Unicast),
280            "multicast" => Ok(Parameter::Multicast),
281            "destination" => {
282                let val = val_or_err()?;
283                let host = parse_or_err(var, val)?;
284                Ok(Parameter::Destination(host))
285            }
286            "interleaved" => {
287                let val = val_or_err()?;
288                let channel = parse_or_err(var, val)?;
289                Ok(Parameter::Interleaved(channel))
290            }
291            "append" => Ok(Parameter::Append),
292            "ttl" => {
293                let val = val_or_err()?;
294                let ttl = parse_or_err(var, val)?;
295                Ok(Parameter::Ttl(ttl))
296            }
297            "layers" => {
298                let val = val_or_err()?;
299                let layers = parse_or_err(var, val)?;
300                Ok(Parameter::Layers(layers))
301            }
302            "port" => {
303                let val = val_or_err()?;
304                let port = parse_or_err(var, val)?;
305                Ok(Parameter::Port(port))
306            }
307            "client_port" => {
308                let val = val_or_err()?;
309                let port = parse_or_err(var, val)?;
310                Ok(Parameter::ClientPort(port))
311            }
312            "server_port" => {
313                let val = val_or_err()?;
314                let port = parse_or_err(var, val)?;
315                Ok(Parameter::ServerPort(port))
316            }
317            "ssrc" => {
318                let val = val_or_err()?;
319                Ok(Parameter::Ssrc(val.to_string()))
320            }
321            "mode" => {
322                let val = val_or_err()?;
323                let val = val
324                    .strip_prefix('"')
325                    .unwrap_or(val)
326                    .strip_suffix('"')
327                    .unwrap_or(val);
328                let method = parse_or_err(var, val)?;
329                Ok(Parameter::Mode(method))
330            }
331            _ => Err(Error::TransportParameterUnknown {
332                var: var.to_string(),
333            }),
334        }
335    }
336}
337
338#[derive(Debug, Clone, PartialEq)]
339pub enum Channel {
340    Single(u8),
341    Range(u8, u8),
342}
343
344impl fmt::Display for Channel {
345    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
346        match self {
347            Channel::Single(channel) => {
348                write!(f, "{}", channel)
349            }
350            Channel::Range(channel_1, channel_2) => {
351                write!(f, "{}-{}", channel_1, channel_2)
352            }
353        }
354    }
355}
356
357impl FromStr for Channel {
358    type Err = Error;
359
360    fn from_str(s: &str) -> Result<Self, Self::Err> {
361        let mut parts = s.split('-');
362        let channel_1 = parts
363            .next()
364            .and_then(|channel| channel.parse::<u8>().ok())
365            .ok_or_else(|| Error::TransportChannelMalformed {
366                value: s.to_string(),
367            })?;
368        let channel_2 = parts.next().map(|channel| {
369            channel
370                .parse::<u8>()
371                .map_err(|_| Error::TransportChannelMalformed {
372                    value: s.to_string(),
373                })
374        });
375
376        Ok(if let Some(channel_2) = channel_2 {
377            Channel::Range(channel_1, channel_2?)
378        } else {
379            Channel::Single(channel_1)
380        })
381    }
382}
383
384#[derive(Debug, Clone, PartialEq)]
385pub enum Port {
386    Single(u16),
387    Range(u16, u16),
388}
389
390impl fmt::Display for Port {
391    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
392        match self {
393            Port::Single(port) => {
394                write!(f, "{}", port)
395            }
396            Port::Range(port_1, port_2) => {
397                write!(f, "{}-{}", port_1, port_2)
398            }
399        }
400    }
401}
402
403impl FromStr for Port {
404    type Err = Error;
405
406    fn from_str(s: &str) -> Result<Self, Self::Err> {
407        let mut parts = s.split('-');
408        let port_1 = parts
409            .next()
410            .and_then(|port| port.parse::<u16>().ok())
411            .ok_or_else(|| Error::TransportPortMalformed {
412                value: s.to_string(),
413            })?;
414        let port_2 = parts.next().map(|port| {
415            port.parse::<u16>()
416                .map_err(|_| Error::TransportPortMalformed {
417                    value: s.to_string(),
418                })
419        });
420
421        Ok(if let Some(port_2) = port_2 {
422            Port::Range(port_1, port_2?)
423        } else {
424            Port::Single(port_1)
425        })
426    }
427}
428
429#[cfg(test)]
430mod tests {
431
432    use super::{Channel, Error, Lower, Method, Parameter, Port, Transport};
433
434    #[test]
435    fn parse_minimal() {
436        assert_eq!("RTP/AVP".parse::<Transport>().unwrap(), Transport::new(),);
437    }
438
439    #[test]
440    fn parse_lower_tcp() {
441        assert_eq!(
442            "RTP/AVP/TCP".parse::<Transport>().unwrap(),
443            Transport::new().with_lower_protocol(Lower::Tcp),
444        );
445    }
446
447    #[test]
448    fn parse_lower_udp() {
449        assert_eq!(
450            "RTP/AVP/UDP".parse::<Transport>().unwrap(),
451            Transport::new().with_lower_protocol(Lower::Udp),
452        );
453    }
454
455    #[test]
456    fn parse_unicast() {
457        assert_eq!(
458            "RTP/AVP;unicast".parse::<Transport>().unwrap(),
459            Transport::new().with_parameter(Parameter::Unicast),
460        );
461    }
462
463    #[test]
464    fn parse_destination_missing_value() {
465        assert!(matches!(
466            "RTP/AVP/UDP;destination".parse::<Transport>(),
467            Err(Error::TransportParameterValueMissing { var: _ }),
468        ),);
469    }
470
471    #[test]
472    fn parse_destination_ip() {
473        assert_eq!(
474            "RTP/AVP/UDP;destination=127.0.0.1"
475                .parse::<Transport>()
476                .unwrap(),
477            Transport::new()
478                .with_lower_protocol(Lower::Udp)
479                .with_parameter(Parameter::Destination([127, 0, 0, 1].into())),
480        );
481    }
482
483    #[test]
484    fn parse_interleaved_invalid() {
485        assert!(matches!(
486            "RTP/AVP/UDP;interleaved=invalid".parse::<Transport>(),
487            Err(Error::TransportParameterValueInvalid { var: _, val: _ }),
488        ),);
489    }
490
491    #[test]
492    fn parse_interleaved_channel() {
493        assert_eq!(
494            "RTP/AVP/UDP;interleaved=8-9".parse::<Transport>().unwrap(),
495            Transport::new()
496                .with_lower_protocol(Lower::Udp)
497                .with_parameter(Parameter::Interleaved(Channel::Range(8, 9))),
498        );
499    }
500
501    #[test]
502    fn parse_layers() {
503        assert_eq!(
504            "RTP/AVP/UDP;layers=3".parse::<Transport>().unwrap(),
505            Transport::new()
506                .with_lower_protocol(Lower::Udp)
507                .with_parameter(Parameter::Layers(3)),
508        );
509    }
510
511    #[test]
512    fn parse_port_single() {
513        assert_eq!(
514            "RTP/AVP/UDP;port=3".parse::<Transport>().unwrap(),
515            Transport::new()
516                .with_lower_protocol(Lower::Udp)
517                .with_parameter(Parameter::Port(Port::Single(3))),
518        );
519    }
520
521    #[test]
522    fn parse_server_port_range() {
523        assert_eq!(
524            "RTP/AVP/UDP;server_port=3-4".parse::<Transport>().unwrap(),
525            Transport::new()
526                .with_lower_protocol(Lower::Udp)
527                .with_parameter(Parameter::ServerPort(Port::Range(3, 4))),
528        );
529    }
530
531    #[test]
532    fn parse_ssrc() {
533        assert_eq!(
534            "RTP/AVP/UDP;ssrc=ABCDEF".parse::<Transport>().unwrap(),
535            Transport::new()
536                .with_lower_protocol(Lower::Udp)
537                .with_parameter(Parameter::Ssrc("ABCDEF".to_string())),
538        );
539    }
540
541    #[test]
542    fn parse_mode_method_unknown() {
543        assert!(matches!(
544            "RTP/AVP/UDP;mode=UNKNOWN".parse::<Transport>(),
545            Err(Error::TransportParameterValueInvalid { var: _, val: _ }),
546        ),);
547    }
548
549    #[test]
550    fn parse_mode_method() {
551        assert_eq!(
552            "RTP/AVP/UDP;mode=PLAY".parse::<Transport>().unwrap(),
553            Transport::new()
554                .with_lower_protocol(Lower::Udp)
555                .with_parameter(Parameter::Mode(Method::Play)),
556        );
557        assert_eq!(
558            "RTP/AVP/UDP;mode=\"PLAY\"".parse::<Transport>().unwrap(),
559            Transport::new()
560                .with_lower_protocol(Lower::Udp)
561                .with_parameter(Parameter::Mode(Method::Play)),
562        );
563    }
564
565    #[test]
566    fn parse_rfc2326_section_12_39_examples() {
567        assert_eq!(
568            "RTP/AVP;multicast;ttl=127;mode=\"PLAY\""
569                .parse::<Transport>()
570                .unwrap(),
571            Transport::new()
572                .with_parameter(Parameter::Multicast)
573                .with_parameter(Parameter::Ttl(127))
574                .with_parameter(Parameter::Mode(Method::Play)),
575        );
576        assert_eq!(
577            "RTP/AVP;unicast;client_port=3456-3457;mode=\"PLAY\""
578                .parse::<Transport>()
579                .unwrap(),
580            Transport::new()
581                .with_parameter(Parameter::Unicast)
582                .with_parameter(Parameter::ClientPort(Port::Range(3456, 3457)))
583                .with_parameter(Parameter::Mode(Method::Play)),
584        );
585    }
586
587    #[test]
588    fn format_minimal() {
589        assert_eq!(&Transport::new().to_string(), "RTP/AVP",);
590    }
591
592    #[test]
593    fn format_lower_tcp() {
594        assert_eq!(
595            &Transport::new().with_lower_protocol(Lower::Tcp).to_string(),
596            "RTP/AVP/TCP",
597        );
598    }
599
600    #[test]
601    fn format_lower_udp() {
602        assert_eq!(
603            &Transport::new().with_lower_protocol(Lower::Udp).to_string(),
604            "RTP/AVP/UDP",
605        );
606    }
607
608    #[test]
609    fn format_unicast() {
610        assert_eq!(
611            &Transport::new()
612                .with_lower_protocol(Lower::Udp)
613                .with_parameter(Parameter::Unicast)
614                .to_string(),
615            "RTP/AVP/UDP;unicast",
616        );
617    }
618
619    #[test]
620    fn format_rfc2326_section_12_39_examples() {
621        assert_eq!(
622            &Transport::new()
623                .with_parameter(Parameter::Multicast)
624                .with_parameter(Parameter::Ttl(127))
625                .with_parameter(Parameter::Mode(Method::Play))
626                .to_string(),
627            "RTP/AVP;multicast;ttl=127;mode=\"PLAY\"",
628        );
629        assert_eq!(
630            &Transport::new()
631                .with_parameter(Parameter::Unicast)
632                .with_parameter(Parameter::ClientPort(Port::Range(3456, 3457)))
633                .with_parameter(Parameter::Mode(Method::Play))
634                .to_string(),
635            "RTP/AVP;unicast;client_port=3456-3457;mode=\"PLAY\"",
636        );
637    }
638
639    #[test]
640    fn format_all_parameters() {
641        assert_eq!(
642      &Transport::new()
643        .with_lower_protocol(Lower::Tcp)
644        .with_parameter(Parameter::Unicast)
645        .with_parameter(Parameter::Multicast)
646        .with_parameter(Parameter::Destination([1, 2, 3, 4].into()))
647        .with_parameter(Parameter::Interleaved(Channel::Range(12, 13)))
648        .with_parameter(Parameter::Append)
649        .with_parameter(Parameter::Ttl(999))
650        .with_parameter(Parameter::Layers(2))
651        .with_parameter(Parameter::Port(Port::Single(8)))
652        .with_parameter(Parameter::ClientPort(Port::Range(9, 10)))
653        .with_parameter(Parameter::ServerPort(Port::Range(11, 12)))
654        .with_parameter(Parameter::Ssrc("01234ABCDEF".to_string()))
655        .with_parameter(Parameter::Mode(Method::Describe))
656        .to_string(),
657      "RTP/AVP/TCP;unicast;multicast;destination=1.2.3.4;interleaved=12-13;append;ttl=999;layers=2;port=8;client_port=9-10;server_port=11-12;ssrc=01234ABCDEF;mode=\"DESCRIBE\"",
658    );
659    }
660}