msf_rtsp/server/
response.rs1use 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
15pub struct OutgoingResponseBuilder {
17 inner: ResponseBuilder,
18 body: Bytes,
19}
20
21impl OutgoingResponseBuilder {
22 #[inline]
24 pub const fn new() -> Self {
25 Self {
26 inner: Response::builder(),
27 body: Bytes::new(),
28 }
29 }
30
31 #[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 #[inline]
42 pub fn set_version(mut self, version: Version) -> Self {
43 self.inner = self.inner.set_version(version);
44 self
45 }
46
47 #[inline]
49 pub fn set_status(mut self, status: Status) -> Self {
50 self.inner = self.inner.set_status(status);
51 self
52 }
53
54 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 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 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 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#[derive(Clone)]
124pub struct OutgoingResponse {
125 inner: Box<Response>,
126}
127
128impl OutgoingResponse {
129 #[inline]
131 pub const fn builder() -> OutgoingResponseBuilder {
132 OutgoingResponseBuilder::new()
133 }
134
135 #[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#[inline(never)]
167pub fn empty_response(status: Status) -> OutgoingResponse {
168 OutgoingResponse::builder().set_status(status).build()
169}
170
171pub fn plain_text_response<T>(status: Status, body: T) -> OutgoingResponse
173where
174 T: Into<String>,
175{
176 #[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#[inline]
191pub fn ok() -> OutgoingResponse {
192 empty_response(Status::OK)
193}
194
195#[inline]
197pub fn no_content() -> OutgoingResponse {
198 empty_response(Status::NO_CONTENT)
199}
200
201pub fn bad_request<T>(msg: T) -> OutgoingResponse
203where
204 T: Into<String>,
205{
206 plain_text_response(Status::BAD_REQUEST, msg)
207}
208
209pub 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#[inline]
226pub fn not_found() -> OutgoingResponse {
227 empty_response(Status::NOT_FOUND)
228}
229
230#[inline]
232pub fn session_not_found() -> OutgoingResponse {
233 empty_response(Status::SESSION_NOT_FOUND)
234}
235
236pub fn method_not_allowed<T>(allow: T) -> OutgoingResponse
238where
239 T: Into<HeaderFieldValue>,
240{
241 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#[inline]
254pub fn method_not_valid_in_this_state() -> OutgoingResponse {
255 empty_response(Status::METHOD_NOT_VALID_IN_THIS_STATE)
256}
257
258#[inline]
260pub fn invalid_range() -> OutgoingResponse {
261 empty_response(Status::INVALID_RANGE)
262}
263
264#[inline]
266pub fn unsupported_transport() -> OutgoingResponse {
267 empty_response(Status::UNSUPPORTED_TRANSPORT)
268}
269
270#[inline]
272pub fn destination_prohibited() -> OutgoingResponse {
273 empty_response(Status::DESTINATION_PROHIBITED)
274}
275
276#[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
285pub fn not_implemented<T>(public: T) -> OutgoingResponse
287where
288 T: Into<HeaderFieldValue>,
289{
290 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
301pub fn bad_gateway<T>(msg: T) -> OutgoingResponse
303where
304 T: Into<String>,
305{
306 plain_text_response(Status::BAD_GATEWAY, msg)
307}
308
309#[inline]
311pub fn rtsp_version_not_supported() -> OutgoingResponse {
312 empty_response(Status::RTSP_VERSION_NOT_SUPPORTED)
313}
314
315pub fn option_not_supported<T, I>(unsupported: T) -> OutgoingResponse
317where
318 T: IntoIterator<Item = I> + Clone,
319 I: Display,
320{
321 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}