rtsp_types/
lib.rs

1// Copyright (C) 2020 Sebastian Dröge <sebastian@centricular.com>
2//
3// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>
4
5//! Crate for handling RTSP ([RFC 7826](https://tools.ietf.org/html/rfc7826))
6//! messages, including a parser and serializer and support for parsing and generating
7//! well-known headers.
8//!
9//! ## Creating an `OPTIONS` request
10//!
11//! ```rust
12//! let request = rtsp_types::Request::builder(
13//!         rtsp_types::Method::Options,
14//!         rtsp_types::Version::V2_0
15//!     )
16//!     .header(rtsp_types::headers::CSEQ, "1")
17//!     .empty();
18//! ```
19//!
20//! This request contains an empty body.
21//!
22//! ## Creating a `SET_PARAMETER` request with a request body
23//!
24//! ```rust
25//! let request = rtsp_types::Request::builder(
26//!         rtsp_types::Method::SetParameter,
27//!         rtsp_types::Version::V2_0
28//!     )
29//!     .request_uri(rtsp_types::Url::parse("rtsp://example.com/test").expect("Invalid URI"))
30//!     .header(rtsp_types::headers::CSEQ, "2")
31//!     .header(rtsp_types::headers::CONTENT_TYPE, "text/parameters")
32//!     .build(Vec::from(&b"barparam: barstuff"[..]));
33//! ```
34//!
35//! The body is passed to the `build()` function and a `Content-Length` header is automatically
36//! inserted into the request headers.
37//!
38//! ## Creating an `OK` response
39//!
40//! ```rust
41//! let response = rtsp_types::Response::builder(
42//!         rtsp_types::Version::V2_0,
43//!         rtsp_types::StatusCode::Ok,
44//!     )
45//!     .header(rtsp_types::headers::CSEQ, "1")
46//!     .empty();
47//! ```
48//!
49//! This response contains an empty body. A non-empty body can be added in the same way as for
50//! requests.
51//!
52//! ## Creating a data message
53//!
54//! ```rust
55//! let data = rtsp_types::Data::new(0, vec![0, 1, 2, 3, 4]);
56//! ```
57//!
58//! This creates a new data message for channel id 0 with the given `Vec<u8>`.
59//!
60//! ## Parsing an RTSP message
61//!
62//! ```rust
63//! let data = b"OPTIONS * RTSP/2.0\r\n\
64//!              CSeq: 1\r\n\
65//!              Supported: play.basic, play.scale\r\n\
66//!              User-Agent: PhonyClient/1.2\r\n\
67//!              \r\n";
68//!
69//! let (message, consumed): (rtsp_types::Message<Vec<u8>>, _) =
70//!     rtsp_types::Message::parse(data).expect("Failed to parse data");
71//!
72//! assert_eq!(consumed, data.len());
73//! match message {
74//!     rtsp_types::Message::Request(ref request) => {
75//!         assert_eq!(request.method(), rtsp_types::Method::Options);
76//!     },
77//!     _ => unreachable!(),
78//! }
79//! ```
80//!
81//! Messages can be parsed from any `AsRef<[u8]>` and to any borrowed or owned body type that
82//! implements `From<&[u8]>`.
83//!
84//! More details about parsing can be found at [`Message::parse`](enum.Message.html#method.parse).
85//!
86//! ## Serializing an RTSP message
87//!
88//! ```rust
89//! let request = rtsp_types::Request::builder(
90//!         rtsp_types::Method::SetParameter,
91//!         rtsp_types::Version::V2_0
92//!     )
93//!     .request_uri(rtsp_types::Url::parse("rtsp://example.com/test").expect("Invalid URI"))
94//!     .header(rtsp_types::headers::CSEQ, "2")
95//!     .header(rtsp_types::headers::CONTENT_TYPE, "text/parameters")
96//!     .build(Vec::from(&b"barparam: barstuff"[..]));
97//!
98//!  let mut data = Vec::new();
99//!  request.write(&mut data).expect("Failed to serialize request");
100//!
101//!  assert_eq!(
102//!     data,
103//!     b"SET_PARAMETER rtsp://example.com/test RTSP/2.0\r\n\
104//!       Content-Length: 18\r\n\
105//!       Content-Type: text/parameters\r\n\
106//!       CSeq: 2\r\n\
107//!       \r\n\
108//!       barparam: barstuff",
109//!  );
110//! ```
111//!
112//! Serializing can be done to any type that implements `std::io::Write`.
113//!
114//! More details about serializing can be found at [`Message::write`](enum.Message.html#method.write).
115
116mod message;
117pub use message::*;
118// TODO: Maybe make this public at a later time
119mod message_ref;
120pub(crate) use message_ref::*;
121mod nom_extensions;
122mod parser;
123mod serializer;
124
125pub mod headers;
126pub use headers::{HeaderName, HeaderValue, Headers};
127
128pub use url::{Host, Url};
129
130use std::{fmt, num::NonZeroUsize};
131use tinyvec::TinyVec;
132
133/// RTSP protocol version of the message.
134///
135/// RTSP 1.0 is defined in [RFC 2326](https://tools.ietf.org/html/rfc2326), RTSP 2.0 is defined in
136/// [RFC 7826](https://tools.ietf.org/html/rfc7826). Check the RFCs for the differences between the
137/// two versions.
138#[derive(Copy, Clone, PartialEq, Eq, Debug)]
139#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
140pub enum Version {
141    /// RTSP/1.0
142    V1_0,
143    /// RTSP/2.0
144    V2_0,
145}
146
147/// RTSP response status codes.
148///
149/// These are defined in [RFC 7826 section 17](https://tools.ietf.org/html/rfc7826#section-17)
150/// together with their semantics for the different requests.
151#[derive(Clone, Copy, Debug, PartialEq, Eq)]
152#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
153pub enum StatusCode {
154    /// Continue
155    Continue,
156    /// Ok
157    Ok,
158    /// Moved permanently
159    MovedPermanently,
160    /// Found
161    Found,
162    /// See other
163    SeeOther,
164    /// Not modified
165    NotModified,
166    /// Use proxy
167    UseProxy,
168    /// Bad request
169    BadRequest,
170    /// Unauthorized
171    Unauthorized,
172    /// Payment required
173    PaymentRequired,
174    /// Forbidden
175    Forbidden,
176    /// Not found
177    NotFound,
178    /// Method not allowed
179    MethodNotAllowed,
180    /// Not acceptable
181    NotAcceptable,
182    /// Proxy authentication required
183    ProxyAuthenticationRequired,
184    /// Request timeout
185    RequestTimeout,
186    /// Gone
187    Gone,
188    /// Precondition failed
189    PreconditionFailed,
190    /// Request message body too large
191    RequestMessageBodyTooLarge,
192    /// Request URI too long
193    RequestURITooLong,
194    /// Unsupported media type
195    UnsupportedMediaType,
196    /// Parameter not understood
197    ParameterNotUnderstood,
198    /// Reserved
199    Reserved,
200    /// Not enough bandwidth
201    NotEnoughBandwidth,
202    /// Session not found
203    SessionNotFound,
204    /// Method not valid in this state
205    MethodNotValidInThisState,
206    /// Header field not valid for resource
207    HeaderFieldNotValidForResource,
208    /// Invalid range
209    InvalidRange,
210    /// Parameter is read-only
211    ParameterIsReadOnly,
212    /// Aggregate operation not allowed
213    AggregateOperationNotAllowed,
214    /// Only aggregate operation allowed
215    OnlyAggregateOperationAllowed,
216    /// Unsupported transport
217    UnsupportedTransport,
218    /// Destination unreachable
219    DestinationUnreachable,
220    /// Destination prohibited
221    DestinationProhibited,
222    /// Data transport not ready yet
223    DataTransportNotReadyYet,
224    /// Notification reason unknown
225    NotificationReasonUnknown,
226    /// Key management error
227    KeyManagementError,
228    /// Connection authorization required
229    ConnectionAuthorizationRequired,
230    /// Connection credentials not accepted
231    ConnectionCredentialsNotAccepted,
232    /// Failure to establish secure connection
233    FailureToEstablishSecureConnection,
234    /// Internal server error
235    InternalServerError,
236    /// Not implemented
237    NotImplemented,
238    /// Bad gateway
239    BadGateway,
240    /// Service unavailable
241    ServiceUnavailable,
242    /// Gateway timeout
243    GatewayTimeout,
244    /// RTSP version not supported
245    RTSPVersionNotSupported,
246    /// Option not supported
247    OptionNotSupported,
248    /// Proxy unavailable
249    ProxyUnavailable,
250    /// Extension status code
251    Extension(u16),
252}
253
254impl StatusCode {
255    /// Returns `true` if the status code is `1xx`.
256    pub fn is_informational(self) -> bool {
257        let val = u16::from(self);
258
259        (100..200).contains(&val)
260    }
261
262    /// Returns `true` if the status code is `2xx`.
263    pub fn is_success(self) -> bool {
264        let val = u16::from(self);
265
266        (200..300).contains(&val)
267    }
268
269    /// Returns `true` if the status code is `3xx`.
270    pub fn is_redirection(self) -> bool {
271        let val = u16::from(self);
272
273        (300..400).contains(&val)
274    }
275
276    /// Returns `true` if the status code is `4xx`.
277    pub fn is_client_error(self) -> bool {
278        let val = u16::from(self);
279
280        (400..500).contains(&val)
281    }
282
283    /// Returns `true` if the status code is `5xx`.
284    pub fn is_server_error(self) -> bool {
285        let val = u16::from(self);
286
287        (500..600).contains(&val)
288    }
289}
290
291/// Converts from the numeric value of a `StatusCode`.
292impl From<u16> for StatusCode {
293    fn from(v: u16) -> Self {
294        match v {
295            100 => StatusCode::Continue,
296            200 => StatusCode::Ok,
297            301 => StatusCode::MovedPermanently,
298            302 => StatusCode::Found,
299            303 => StatusCode::SeeOther,
300            304 => StatusCode::NotModified,
301            305 => StatusCode::UseProxy,
302            400 => StatusCode::BadRequest,
303            401 => StatusCode::Unauthorized,
304            402 => StatusCode::PaymentRequired,
305            403 => StatusCode::Forbidden,
306            404 => StatusCode::NotFound,
307            405 => StatusCode::MethodNotAllowed,
308            406 => StatusCode::NotAcceptable,
309            407 => StatusCode::ProxyAuthenticationRequired,
310            408 => StatusCode::RequestTimeout,
311            410 => StatusCode::Gone,
312            412 => StatusCode::PreconditionFailed,
313            413 => StatusCode::RequestMessageBodyTooLarge,
314            414 => StatusCode::RequestURITooLong,
315            415 => StatusCode::UnsupportedMediaType,
316            451 => StatusCode::ParameterNotUnderstood,
317            452 => StatusCode::Reserved,
318            453 => StatusCode::NotEnoughBandwidth,
319            454 => StatusCode::SessionNotFound,
320            455 => StatusCode::MethodNotValidInThisState,
321            456 => StatusCode::HeaderFieldNotValidForResource,
322            457 => StatusCode::InvalidRange,
323            458 => StatusCode::ParameterIsReadOnly,
324            459 => StatusCode::AggregateOperationNotAllowed,
325            460 => StatusCode::OnlyAggregateOperationAllowed,
326            461 => StatusCode::UnsupportedTransport,
327            462 => StatusCode::DestinationUnreachable,
328            463 => StatusCode::DestinationProhibited,
329            464 => StatusCode::DataTransportNotReadyYet,
330            465 => StatusCode::NotificationReasonUnknown,
331            466 => StatusCode::KeyManagementError,
332            470 => StatusCode::ConnectionAuthorizationRequired,
333            471 => StatusCode::ConnectionCredentialsNotAccepted,
334            472 => StatusCode::FailureToEstablishSecureConnection,
335            500 => StatusCode::InternalServerError,
336            501 => StatusCode::NotImplemented,
337            502 => StatusCode::BadGateway,
338            503 => StatusCode::ServiceUnavailable,
339            504 => StatusCode::GatewayTimeout,
340            505 => StatusCode::RTSPVersionNotSupported,
341            551 => StatusCode::OptionNotSupported,
342            553 => StatusCode::ProxyUnavailable,
343            v => StatusCode::Extension(v),
344        }
345    }
346}
347
348/// Converts to the numeric value of a `StatusCode`.
349impl From<StatusCode> for u16 {
350    fn from(v: StatusCode) -> Self {
351        match v {
352            StatusCode::Continue => 100,
353            StatusCode::Ok => 200,
354            StatusCode::MovedPermanently => 301,
355            StatusCode::Found => 302,
356            StatusCode::SeeOther => 303,
357            StatusCode::NotModified => 304,
358            StatusCode::UseProxy => 305,
359            StatusCode::BadRequest => 400,
360            StatusCode::Unauthorized => 401,
361            StatusCode::PaymentRequired => 402,
362            StatusCode::Forbidden => 403,
363            StatusCode::NotFound => 404,
364            StatusCode::MethodNotAllowed => 405,
365            StatusCode::NotAcceptable => 406,
366            StatusCode::ProxyAuthenticationRequired => 407,
367            StatusCode::RequestTimeout => 408,
368            StatusCode::Gone => 410,
369            StatusCode::PreconditionFailed => 412,
370            StatusCode::RequestMessageBodyTooLarge => 413,
371            StatusCode::RequestURITooLong => 414,
372            StatusCode::UnsupportedMediaType => 415,
373            StatusCode::ParameterNotUnderstood => 451,
374            StatusCode::Reserved => 452,
375            StatusCode::NotEnoughBandwidth => 453,
376            StatusCode::SessionNotFound => 454,
377            StatusCode::MethodNotValidInThisState => 455,
378            StatusCode::HeaderFieldNotValidForResource => 456,
379            StatusCode::InvalidRange => 457,
380            StatusCode::ParameterIsReadOnly => 458,
381            StatusCode::AggregateOperationNotAllowed => 459,
382            StatusCode::OnlyAggregateOperationAllowed => 460,
383            StatusCode::UnsupportedTransport => 461,
384            StatusCode::DestinationUnreachable => 462,
385            StatusCode::DestinationProhibited => 463,
386            StatusCode::DataTransportNotReadyYet => 464,
387            StatusCode::NotificationReasonUnknown => 465,
388            StatusCode::KeyManagementError => 466,
389            StatusCode::ConnectionAuthorizationRequired => 470,
390            StatusCode::ConnectionCredentialsNotAccepted => 471,
391            StatusCode::FailureToEstablishSecureConnection => 472,
392            StatusCode::InternalServerError => 500,
393            StatusCode::NotImplemented => 501,
394            StatusCode::BadGateway => 502,
395            StatusCode::ServiceUnavailable => 503,
396            StatusCode::GatewayTimeout => 504,
397            StatusCode::RTSPVersionNotSupported => 505,
398            StatusCode::OptionNotSupported => 551,
399            StatusCode::ProxyUnavailable => 553,
400            StatusCode::Extension(v) => v,
401        }
402    }
403}
404
405/// Provides the default reason phrase for the `StatusCode`.
406impl fmt::Display for StatusCode {
407    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
408        match *self {
409            StatusCode::Continue => write!(fmt, "Continue"),
410            StatusCode::Ok => write!(fmt, "Ok"),
411            StatusCode::MovedPermanently => write!(fmt, "Moved Permanently"),
412            StatusCode::Found => write!(fmt, "Found"),
413            StatusCode::SeeOther => write!(fmt, "See Other"),
414            StatusCode::NotModified => write!(fmt, "Not Modified"),
415            StatusCode::UseProxy => write!(fmt, "Use Proxy"),
416            StatusCode::BadRequest => write!(fmt, "Bad Request"),
417            StatusCode::Unauthorized => write!(fmt, "Unauthorized"),
418            StatusCode::PaymentRequired => write!(fmt, "Payment Required"),
419            StatusCode::Forbidden => write!(fmt, "Forbidden"),
420            StatusCode::NotFound => write!(fmt, "Not Found"),
421            StatusCode::MethodNotAllowed => write!(fmt, "Method Not Allowed"),
422            StatusCode::NotAcceptable => write!(fmt, "Not Acceptable"),
423            StatusCode::ProxyAuthenticationRequired => write!(fmt, "Proxy Authentication Required"),
424            StatusCode::RequestTimeout => write!(fmt, "Request Timeout"),
425            StatusCode::Gone => write!(fmt, "Gone"),
426            StatusCode::PreconditionFailed => write!(fmt, "Precondition Failed"),
427            StatusCode::RequestMessageBodyTooLarge => write!(fmt, "Request Message Body Too Large"),
428            StatusCode::RequestURITooLong => write!(fmt, "Request URI Too Long"),
429            StatusCode::UnsupportedMediaType => write!(fmt, "Unsupported Media Type"),
430            StatusCode::ParameterNotUnderstood => write!(fmt, "Parameter Not Understood"),
431            StatusCode::Reserved => write!(fmt, "Reserved"),
432            StatusCode::NotEnoughBandwidth => write!(fmt, "Not Enough Bandwidth"),
433            StatusCode::SessionNotFound => write!(fmt, "Session Not Found"),
434            StatusCode::MethodNotValidInThisState => write!(fmt, "Method Not Valid In This State"),
435            StatusCode::HeaderFieldNotValidForResource => {
436                write!(fmt, "Header Field Not Valid For Resource")
437            }
438            StatusCode::InvalidRange => write!(fmt, "Invalid Range"),
439            StatusCode::ParameterIsReadOnly => write!(fmt, "Parameter Is Read-Only"),
440            StatusCode::AggregateOperationNotAllowed => {
441                write!(fmt, "Aggregate Operation Not Allowed")
442            }
443            StatusCode::OnlyAggregateOperationAllowed => {
444                write!(fmt, "Only Aggregate Operation ALlowed")
445            }
446            StatusCode::UnsupportedTransport => write!(fmt, "Unsupported Transport"),
447            StatusCode::DestinationUnreachable => write!(fmt, "Destination Unreachable"),
448            StatusCode::DestinationProhibited => write!(fmt, "Destination Prohibited"),
449            StatusCode::DataTransportNotReadyYet => write!(fmt, "Data Transport Not Ready Yet"),
450            StatusCode::NotificationReasonUnknown => write!(fmt, "Notification Reason Unknown"),
451            StatusCode::KeyManagementError => write!(fmt, "Key Management Error"),
452            StatusCode::ConnectionAuthorizationRequired => {
453                write!(fmt, "Connection Authorization Required")
454            }
455            StatusCode::ConnectionCredentialsNotAccepted => {
456                write!(fmt, "Connection Credentials Not Accepted")
457            }
458            StatusCode::FailureToEstablishSecureConnection => {
459                write!(fmt, "Failure To Establish Secure Connection")
460            }
461            StatusCode::InternalServerError => write!(fmt, "Internal Server Error"),
462            StatusCode::NotImplemented => write!(fmt, "Not Implemented"),
463            StatusCode::BadGateway => write!(fmt, "Bad Gateway"),
464            StatusCode::ServiceUnavailable => write!(fmt, "Service Unavailable"),
465            StatusCode::GatewayTimeout => write!(fmt, "Gateway Timeout"),
466            StatusCode::RTSPVersionNotSupported => write!(fmt, "RTSP Version Not Supported"),
467            StatusCode::OptionNotSupported => write!(fmt, "Option Not Supported"),
468            StatusCode::ProxyUnavailable => write!(fmt, "Proxy Unavailable"),
469            StatusCode::Extension(v) => write!(fmt, "Extension {v}"),
470        }
471    }
472}
473
474/// Empty body.
475///
476/// This can be used as the `Response` or `Request` body in place of a `&[]`
477/// to signal an empty body.
478#[derive(Debug, PartialEq, Eq, Clone)]
479#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
480pub struct Empty;
481
482impl AsRef<[u8]> for Empty {
483    fn as_ref(&self) -> &[u8] {
484        &[]
485    }
486}
487
488/// Message parsing error.
489// TODO: Distinguish more errors and provide more information!
490#[derive(Debug)]
491pub enum ParseError {
492    /// Parsing failed irrecoverably.
493    Error,
494    /// Message was not complete and more data is required.
495    Incomplete(Option<NonZeroUsize>),
496}
497
498impl std::error::Error for ParseError {}
499
500impl std::fmt::Display for ParseError {
501    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
502        match *self {
503            ParseError::Error => write!(f, "Parse Error"),
504            ParseError::Incomplete(needed) => write!(f, "Incomplete message: {:?}", needed),
505        }
506    }
507}
508
509/// Serialization write error.
510// TODO: Distinguish more errors and provide more information!
511#[derive(Debug)]
512pub enum WriteError {
513    /// Error reported by the underlying IO type
514    IoError(std::io::Error),
515}
516
517impl std::error::Error for WriteError {
518    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
519        match self {
520            WriteError::IoError(ref err) => Some(err),
521        }
522    }
523}
524
525impl std::fmt::Display for WriteError {
526    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
527        match *self {
528            WriteError::IoError(ref error) => write!(f, "Write IO error: {error}"),
529        }
530    }
531}
532
533impl From<std::io::Error> for WriteError {
534    fn from(v: std::io::Error) -> Self {
535        WriteError::IoError(v)
536    }
537}