Skip to main content

msf_rtsp/header/
session.rs

1//! Session header and related types.
2
3use std::{
4    borrow::{Borrow, Cow},
5    fmt::{self, Display, Formatter},
6    str::FromStr,
7};
8
9use crate::{Error, header::HeaderFieldValue};
10
11/// Session header field.
12#[derive(Clone)]
13pub struct SessionHeader {
14    inner: SessionHeaderRef<'static>,
15}
16
17impl SessionHeader {
18    /// Create a new session header with a given session ID.
19    pub fn new<T>(id: T) -> Self
20    where
21        T: Into<String>,
22    {
23        let inner = SessionHeaderRef {
24            id: Cow::Owned(id.into()),
25            timeout: None,
26        };
27
28        Self { inner }
29    }
30
31    /// Get session ID.
32    #[inline]
33    pub fn id(&self) -> &str {
34        self.inner.id()
35    }
36
37    /// Get session timeout.
38    #[inline]
39    pub fn timeout(&self) -> u64 {
40        self.inner.timeout()
41    }
42
43    /// Set the session timeout.
44    #[inline]
45    pub const fn with_timeout(mut self, timeout: u64) -> Self {
46        self.inner.timeout = Some(timeout);
47        self
48    }
49}
50
51impl Borrow<SessionHeaderRef<'static>> for SessionHeader {
52    #[inline]
53    fn borrow(&self) -> &SessionHeaderRef<'static> {
54        &self.inner
55    }
56}
57
58impl Display for SessionHeader {
59    #[inline]
60    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
61        Display::fmt(Borrow::<SessionHeaderRef>::borrow(self), f)
62    }
63}
64
65impl From<SessionHeader> for HeaderFieldValue {
66    #[inline]
67    fn from(value: SessionHeader) -> Self {
68        HeaderFieldValue::from(value.to_string())
69    }
70}
71
72impl FromStr for SessionHeader {
73    type Err = Error;
74
75    #[inline]
76    fn from_str(header: &str) -> Result<Self, Self::Err> {
77        SessionHeaderRef::try_from(header).map(SessionHeaderRef::into_owned)
78    }
79}
80
81impl TryFrom<&HeaderFieldValue> for SessionHeader {
82    type Error = Error;
83
84    #[inline]
85    fn try_from(value: &HeaderFieldValue) -> Result<Self, Self::Error> {
86        SessionHeaderRef::try_from(value).map(SessionHeaderRef::into_owned)
87    }
88}
89
90/// Session header reference.
91#[derive(Clone)]
92pub struct SessionHeaderRef<'a> {
93    id: Cow<'a, str>,
94    timeout: Option<u64>,
95}
96
97impl<'a> SessionHeaderRef<'a> {
98    /// Create a new session header with a given session ID.
99    #[inline]
100    pub const fn new(id: &'a str) -> Self {
101        Self {
102            id: Cow::Borrowed(id),
103            timeout: None,
104        }
105    }
106}
107
108impl SessionHeaderRef<'_> {
109    /// Get the session ID.
110    #[inline]
111    pub fn id(&self) -> &str {
112        &self.id
113    }
114
115    /// Get the session timeout.
116    #[inline]
117    pub fn timeout(&self) -> u64 {
118        self.timeout.unwrap_or(60)
119    }
120
121    /// Set the session timeout.
122    #[inline]
123    pub const fn with_timeout(mut self, timeout: u64) -> Self {
124        self.timeout = Some(timeout);
125        self
126    }
127
128    /// Convert the session header into the owned representation.
129    #[inline]
130    pub fn into_owned(self) -> SessionHeader {
131        let inner = SessionHeaderRef {
132            id: Cow::Owned(self.id.into_owned()),
133            timeout: self.timeout,
134        };
135
136        SessionHeader { inner }
137    }
138
139    /// Parse header parameters.
140    fn parse_params(&mut self, params: &str) -> Result<(), Error> {
141        for element in params.split(';') {
142            let (name, value) = super::parse_header_parameter(element);
143
144            if name.is_empty() {
145                continue;
146            }
147
148            self.parse_param(name, value)?;
149        }
150
151        Ok(())
152    }
153
154    /// Parse a given parameter.
155    fn parse_param(&mut self, name: &str, value: &str) -> Result<(), Error> {
156        if name.eq_ignore_ascii_case("timeout") {
157            self.timeout = value
158                .parse()
159                .map(Some)
160                .map_err(|_| Error::from_static_msg("invalid timeout parameter"))?;
161        }
162
163        Ok(())
164    }
165}
166
167impl Display for SessionHeaderRef<'_> {
168    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
169        write!(f, "{}", self.id)?;
170
171        if let Some(timeout) = self.timeout {
172            write!(f, ";timeout={timeout}")?;
173        }
174
175        Ok(())
176    }
177}
178
179impl From<SessionHeaderRef<'_>> for HeaderFieldValue {
180    #[inline]
181    fn from(value: SessionHeaderRef<'_>) -> Self {
182        HeaderFieldValue::from(value.to_string())
183    }
184}
185
186impl<'a> TryFrom<&'a str> for SessionHeaderRef<'a> {
187    type Error = Error;
188
189    fn try_from(header: &'a str) -> Result<Self, Self::Error> {
190        let (id, params) = header.split_once(';').unwrap_or((header, ""));
191
192        let mut res = Self::new(id);
193
194        res.parse_params(params)?;
195
196        Ok(res)
197    }
198}
199
200impl<'a> TryFrom<&'a HeaderFieldValue> for SessionHeaderRef<'a> {
201    type Error = Error;
202
203    fn try_from(value: &'a HeaderFieldValue) -> Result<Self, Self::Error> {
204        value
205            .to_str()
206            .map_err(|_| Error::from_static_msg("header field is not UTF-8 encoded"))?
207            .try_into()
208    }
209}