1use rtsp_types::{Message, Method, Request, Response, StatusCode, Version, headers};
16
17use crate::error::{Error, Result};
18use crate::state::{SessionState, server_next_state};
19use crate::transport::Transport;
20
21type Body = Vec<u8>;
23
24#[non_exhaustive]
26#[derive(Debug, Clone, PartialEq, Eq)]
27pub enum ServerEvent {
28 RequestAccepted {
30 method: Method,
32 cseq: u32,
34 state: SessionState,
36 },
37 MethodNotValid {
39 method: Method,
41 state: SessionState,
43 },
44 SessionSetup {
46 session_id: String,
48 transport: Transport,
50 },
51}
52
53const DEFAULT_SESSION_SEED: u64 = 0x1234_5678;
57
58#[derive(Debug)]
60pub struct ServerSession {
61 state: SessionState,
62 session_id: Option<String>,
63 session_timeout: Option<u64>,
64 next_session_seed: u64,
65 negotiated_transport: Option<Transport>,
66 server_header: String,
67}
68
69impl Default for ServerSession {
70 fn default() -> Self {
71 Self::new()
72 }
73}
74
75impl ServerSession {
76 pub fn new() -> Self {
78 ServerSession {
79 state: SessionState::Init,
80 session_id: None,
81 session_timeout: None,
82 next_session_seed: DEFAULT_SESSION_SEED,
83 negotiated_transport: None,
84 server_header: "rtsp-runtime".to_string(),
85 }
86 }
87
88 pub fn with_session_timeout(mut self, seconds: u64) -> Self {
90 self.session_timeout = Some(seconds);
91 self
92 }
93
94 pub fn with_session_seed(mut self, seed: u64) -> Self {
96 self.next_session_seed = seed;
97 self
98 }
99
100 pub fn state(&self) -> SessionState {
102 self.state
103 }
104
105 pub fn session_id(&self) -> Option<&str> {
107 self.session_id.as_deref()
108 }
109
110 pub fn negotiated_transport(&self) -> Option<&Transport> {
112 self.negotiated_transport.as_ref()
113 }
114
115 pub fn handle_request(&mut self, data: &[u8]) -> Result<(Vec<u8>, Vec<ServerEvent>)> {
118 let (message, _consumed) =
119 Message::<Body>::parse(data).map_err(|e| Error::MessageParse(format!("{e:?}")))?;
120 let request = match message {
121 Message::Request(r) => r,
122 _ => return Err(Error::MessageParse("expected an RTSP request".into())),
123 };
124 self.handle_parsed(request)
125 }
126
127 fn handle_parsed(&mut self, request: Request<Body>) -> Result<(Vec<u8>, Vec<ServerEvent>)> {
128 let method = request.method().clone();
129 let cseq = header_value(request.header(&headers::CSEQ))
130 .and_then(|s| s.trim().parse::<u32>().ok())
131 .ok_or(Error::MissingCSeq)?;
132
133 let next_state = match server_next_state(self.state, &method) {
135 Ok(s) => s,
136 Err(_) => {
137 let resp = self.build_response(StatusCode::MethodNotValidInThisState, cseq, |b| b);
138 let bytes = serialize(&Message::from(resp))?;
139 return Ok((
140 bytes,
141 vec![ServerEvent::MethodNotValid {
142 method,
143 state: self.state,
144 }],
145 ));
146 }
147 };
148
149 let mut events = Vec::new();
150
151 if method == Method::Setup {
153 let transport = match header_value(request.header(&headers::TRANSPORT)) {
154 Some(t) => Transport::parse(t)?,
155 None => {
156 let resp = self.build_response(StatusCode::UnsupportedTransport, cseq, |b| b);
157 return Ok((serialize(&Message::from(resp))?, events));
158 }
159 };
160 if transport.first().is_none() {
161 let resp = self.build_response(StatusCode::UnsupportedTransport, cseq, |b| b);
162 return Ok((serialize(&Message::from(resp))?, events));
163 }
164 let session_id = self
165 .session_id
166 .clone()
167 .unwrap_or_else(|| self.allocate_session());
168 self.session_id = Some(session_id.clone());
169 self.negotiated_transport = Some(transport.clone());
170
171 let sid = session_id.clone();
172 let session_hdr = match self.session_timeout {
173 Some(t) => format!("{sid};timeout={t}"),
174 None => sid.clone(),
175 };
176 let transport_hdr = transport.to_header_value();
177 let resp = self.build_response(StatusCode::Ok, cseq, |b| {
178 b.header(headers::SESSION, session_hdr)
179 .header(headers::TRANSPORT, transport_hdr)
180 });
181 self.state = next_state;
182 events.push(ServerEvent::SessionSetup {
183 session_id,
184 transport,
185 });
186 events.push(ServerEvent::RequestAccepted {
187 method,
188 cseq,
189 state: self.state,
190 });
191 return Ok((serialize(&Message::from(resp))?, events));
192 }
193
194 let session_hdr = self.session_id.clone();
196 let resp = self.build_response(StatusCode::Ok, cseq, |mut b| {
197 if let Some(sid) = &session_hdr {
198 b = b.header(headers::SESSION, sid.clone());
199 }
200 b
201 });
202 self.state = next_state;
203 if method == Method::Teardown {
204 self.session_id = None;
205 self.negotiated_transport = None;
206 }
207 events.push(ServerEvent::RequestAccepted {
208 method,
209 cseq,
210 state: self.state,
211 });
212 Ok((serialize(&Message::from(resp))?, events))
213 }
214
215 fn allocate_session(&mut self) -> String {
216 let id = self.next_session_seed;
217 self.next_session_seed = self.next_session_seed.wrapping_add(1);
219 format!("{id:08}")
220 }
221
222 fn build_response<F>(&self, status: StatusCode, cseq: u32, f: F) -> Response<Body>
225 where
226 F: FnOnce(rtsp_types::ResponseBuilder) -> rtsp_types::ResponseBuilder,
227 {
228 let builder = Response::builder(Version::V1_0, status)
229 .header(headers::CSEQ, cseq.to_string())
230 .header(headers::SERVER, self.server_header.clone());
231 f(builder).build(Vec::new())
232 }
233}
234
235fn header_value(h: Option<&headers::HeaderValue>) -> Option<&str> {
236 h.map(|v| v.as_str())
237}
238
239fn serialize(message: &Message<Body>) -> Result<Vec<u8>> {
240 let mut out = Vec::new();
241 message
242 .write(&mut out)
243 .map_err(|e| Error::MessageWrite(e.to_string()))?;
244 Ok(out)
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250
251 fn req(bytes: &str) -> Vec<u8> {
252 bytes.replace('\n', "\r\n").into_bytes()
253 }
254
255 #[test]
256 fn setup_transitions_init_to_ready() {
257 let mut s = ServerSession::new();
258 let (resp, events) = s
259 .handle_request(&req(
260 "SETUP rtsp://h/s RTSP/1.0\nCSeq: 1\nTransport: RTP/AVP/TCP;interleaved=0-1\n\n",
261 ))
262 .unwrap();
263 assert_eq!(s.state(), SessionState::Ready);
264 let text = String::from_utf8_lossy(&resp);
265 assert!(text.contains("200"));
266 assert!(text.contains("Session:"));
267 assert!(text.contains("Transport:"));
268 assert!(
269 events
270 .iter()
271 .any(|e| matches!(e, ServerEvent::SessionSetup { .. }))
272 );
273 }
274
275 #[test]
276 fn play_in_init_returns_455() {
277 let mut s = ServerSession::new();
278 let (resp, events) = s
279 .handle_request(&req("PLAY rtsp://h/s RTSP/1.0\nCSeq: 1\n\n"))
280 .unwrap();
281 assert_eq!(s.state(), SessionState::Init);
282 assert!(String::from_utf8_lossy(&resp).contains("455"));
283 assert!(
284 events
285 .iter()
286 .any(|e| matches!(e, ServerEvent::MethodNotValid { .. }))
287 );
288 }
289}