Skip to main content

msf_rtsp/server/
response.rs

1//! RTSP server response types.
2
3use std::{borrow::Borrow, fmt::Display, ops::Deref};
4
5use bytes::Bytes;
6
7use crate::{
8    Version,
9    header::{HeaderField, HeaderFieldValue, ValueListDisplay},
10    response::{Response, ResponseBuilder, ResponseHeader},
11};
12
13pub use crate::response::Status;
14
15/// Builder for outgoing RTSP responses.
16pub struct OutgoingResponseBuilder {
17    inner: ResponseBuilder,
18    body: Bytes,
19}
20
21impl OutgoingResponseBuilder {
22    /// Create a new builder.
23    #[inline]
24    pub const fn new() -> Self {
25        Self {
26            inner: Response::builder(),
27            body: Bytes::new(),
28        }
29    }
30
31    /// Create a new response builder with a given status.
32    #[inline]
33    pub fn new_with_status(status: Status) -> Self {
34        Self {
35            inner: ResponseBuilder::new_with_status(status),
36            body: Bytes::new(),
37        }
38    }
39
40    /// Set the RTSP version.
41    #[inline]
42    pub fn set_version(mut self, version: Version) -> Self {
43        self.inner = self.inner.set_version(version);
44        self
45    }
46
47    /// Set response status.
48    #[inline]
49    pub fn set_status(mut self, status: Status) -> Self {
50        self.inner = self.inner.set_status(status);
51        self
52    }
53
54    /// Replace all header fields having the same name (if any).
55    pub fn set_header_field<T>(mut self, field: T) -> Self
56    where
57        T: Into<HeaderField>,
58    {
59        self.inner = self.inner.set_header_field(field);
60        self
61    }
62
63    /// Add a given header field.
64    pub fn add_header_field<T>(mut self, field: T) -> Self
65    where
66        T: Into<HeaderField>,
67    {
68        self.inner = self.inner.add_header_field(field);
69        self
70    }
71
72    /// Set the response body.
73    pub fn set_body<B>(mut self, body: B) -> Self
74    where
75        B: Into<Bytes>,
76    {
77        self.body = body.into();
78        self
79    }
80
81    /// Build the response.
82    pub fn build(self) -> OutgoingResponse {
83        let inner = self
84            .inner
85            .set_header_field(("Content-Length", self.body.len()))
86            .body(self.body)
87            .into();
88
89        OutgoingResponse { inner }
90    }
91}
92
93impl Default for OutgoingResponseBuilder {
94    #[inline]
95    fn default() -> Self {
96        Self::new()
97    }
98}
99
100impl From<ResponseHeader> for OutgoingResponseBuilder {
101    #[inline]
102    fn from(header: ResponseHeader) -> Self {
103        Self {
104            inner: header.into(),
105            body: Bytes::new(),
106        }
107    }
108}
109
110impl From<OutgoingResponse> for OutgoingResponseBuilder {
111    #[inline]
112    fn from(response: OutgoingResponse) -> Self {
113        let (header, body) = response.deconstruct();
114
115        Self {
116            inner: header.into(),
117            body,
118        }
119    }
120}
121
122/// Outgoing RTSP response.
123#[derive(Clone)]
124pub struct OutgoingResponse {
125    inner: Box<Response>,
126}
127
128impl OutgoingResponse {
129    /// Get a builder for the outgoing RTSP response.
130    #[inline]
131    pub const fn builder() -> OutgoingResponseBuilder {
132        OutgoingResponseBuilder::new()
133    }
134
135    /// Deconstruct the response back into its response header and body.
136    #[inline]
137    pub fn deconstruct(self) -> (ResponseHeader, Bytes) {
138        self.inner.deconstruct()
139    }
140}
141
142impl AsRef<Response> for OutgoingResponse {
143    #[inline]
144    fn as_ref(&self) -> &Response {
145        &self.inner
146    }
147}
148
149impl Borrow<Response> for OutgoingResponse {
150    #[inline]
151    fn borrow(&self) -> &Response {
152        &self.inner
153    }
154}
155
156impl Deref for OutgoingResponse {
157    type Target = Response;
158
159    #[inline]
160    fn deref(&self) -> &Self::Target {
161        &self.inner
162    }
163}
164
165/// Create a new empty response with a given status.
166#[inline(never)]
167pub fn empty_response(status: Status) -> OutgoingResponse {
168    OutgoingResponse::builder().set_status(status).build()
169}
170
171/// Create a new plain text response with a given status and body.
172pub fn plain_text_response<T>(status: Status, body: T) -> OutgoingResponse
173where
174    T: Into<String>,
175{
176    // helper function to avoid expensive monomorphizations
177    #[inline(never)]
178    fn inner(status: Status, body: String) -> OutgoingResponse {
179        OutgoingResponse::builder()
180            .set_status(status)
181            .add_header_field(("Content-Type", "text/plain"))
182            .set_body(Bytes::from(body))
183            .build()
184    }
185
186    inner(status, body.into())
187}
188
189/// Create a new OK response.
190#[inline]
191pub fn ok() -> OutgoingResponse {
192    empty_response(Status::OK)
193}
194
195/// Create a new No Content response.
196#[inline]
197pub fn no_content() -> OutgoingResponse {
198    empty_response(Status::NO_CONTENT)
199}
200
201/// Create a new Bad Request response.
202pub fn bad_request<T>(msg: T) -> OutgoingResponse
203where
204    T: Into<String>,
205{
206    plain_text_response(Status::BAD_REQUEST, msg)
207}
208
209/// Create a new Unauthorized response.
210pub fn unauthorized<T, I>(challenges: T) -> OutgoingResponse
211where
212    T: IntoIterator<Item = I>,
213    I: Into<HeaderFieldValue>,
214{
215    let mut builder = OutgoingResponse::builder().set_status(Status::UNAUTHORIZED);
216
217    for challenge in challenges {
218        builder = builder.add_header_field(("WWW-Authenticate", challenge));
219    }
220
221    builder.build()
222}
223
224/// Create a new Not Found response.
225#[inline]
226pub fn not_found() -> OutgoingResponse {
227    empty_response(Status::NOT_FOUND)
228}
229
230/// Create a new Session Not Found response.
231#[inline]
232pub fn session_not_found() -> OutgoingResponse {
233    empty_response(Status::SESSION_NOT_FOUND)
234}
235
236/// Create a new Method Not Allowed response.
237pub fn method_not_allowed<T>(allow: T) -> OutgoingResponse
238where
239    T: Into<HeaderFieldValue>,
240{
241    // helper function to avoid expensive monomorphizations
242    fn inner(allow: HeaderFieldValue) -> OutgoingResponse {
243        OutgoingResponse::builder()
244            .set_status(Status::METHOD_NOT_ALLOWED)
245            .add_header_field(("Allow", allow))
246            .build()
247    }
248
249    inner(allow.into())
250}
251
252/// Create a new Method Not Valid in This State response.
253#[inline]
254pub fn method_not_valid_in_this_state() -> OutgoingResponse {
255    empty_response(Status::METHOD_NOT_VALID_IN_THIS_STATE)
256}
257
258/// Create a new Invalid Range response.
259#[inline]
260pub fn invalid_range() -> OutgoingResponse {
261    empty_response(Status::INVALID_RANGE)
262}
263
264/// Create a new Unsupported Transport response.
265#[inline]
266pub fn unsupported_transport() -> OutgoingResponse {
267    empty_response(Status::UNSUPPORTED_TRANSPORT)
268}
269
270/// A function returning Destination Prohibited response.
271#[inline]
272pub fn destination_prohibited() -> OutgoingResponse {
273    empty_response(Status::DESTINATION_PROHIBITED)
274}
275
276/// Create a new Internal Server Error response.
277#[inline]
278pub fn internal_server_error<T>(msg: T) -> OutgoingResponse
279where
280    T: Into<String>,
281{
282    plain_text_response(Status::INTERNAL_SERVER_ERROR, msg)
283}
284
285/// Create a new Not Implemented response.
286pub fn not_implemented<T>(public: T) -> OutgoingResponse
287where
288    T: Into<HeaderFieldValue>,
289{
290    // helper function to avoid expensive monomorphizations
291    fn inner(public: HeaderFieldValue) -> OutgoingResponse {
292        OutgoingResponse::builder()
293            .set_status(Status::NOT_IMPLEMENTED)
294            .add_header_field(("Public", public))
295            .build()
296    }
297
298    inner(public.into())
299}
300
301/// Create a new Bad Gateway response.
302pub fn bad_gateway<T>(msg: T) -> OutgoingResponse
303where
304    T: Into<String>,
305{
306    plain_text_response(Status::BAD_GATEWAY, msg)
307}
308
309/// Create a new RTSP Version Not Supported response.
310#[inline]
311pub fn rtsp_version_not_supported() -> OutgoingResponse {
312    empty_response(Status::RTSP_VERSION_NOT_SUPPORTED)
313}
314
315/// Create a new Option Not Supported response.
316pub fn option_not_supported<T, I>(unsupported: T) -> OutgoingResponse
317where
318    T: IntoIterator<Item = I> + Clone,
319    I: Display,
320{
321    // helper function to avoid expensive monomorphizations
322    fn inner(unsupported: HeaderFieldValue) -> OutgoingResponse {
323        OutgoingResponse::builder()
324            .set_status(Status::OPTION_NOT_SUPPORTED)
325            .add_header_field(("Unsupported", unsupported))
326            .build()
327    }
328
329    let unsupported = ValueListDisplay::new(", ", unsupported);
330
331    inner(unsupported.into())
332}