sheave_core/messages/
connect.rs

1use std::io::Result as IOResult;
2use super::{
3    Channel,
4    ChunkData,
5    Command,
6    headers::MessageType
7};
8use crate::{
9    Decoder,
10    Encoder,
11    ByteBuffer,
12    messages::amf::v0::Object,
13};
14
15/// The command to exchange an information about application using.
16///
17/// Following format is required.
18///
19/// |Setting Data|AMF Type|Value|
20/// | :- | :- | :- |
21/// |Command Object|[`Object`]|See [Command Object](#command-object).|
22///
23/// # Command Object
24///
25/// The Command Object is written detailed informations of both applications.
26/// Concretely, several of following pair is exchanged:
27///
28/// ## Publisher side
29///
30/// |Key|AMF Type|Description|
31/// | :- | :- | :- |
32/// |`app`|[`String`]|Server application name.|
33/// |`type`|[`String`]|`"nonprivate"`|
34/// |`flashVer`|[`String`]|Either a flash player version(client) or an FLV encoder version(server).|
35/// |`tcUrl`|[`String`]|The URL to connect server as the TCP. `app` value is included in this value.|
36///
37/// ## Subscriber side
38///
39/// |Key|AMF Type|Description|
40/// | :- | :- | :- |
41/// |`fpad`|[`Boolean`]|Whether a proxy is used.|
42/// |`audioCodecs`|[`Number`]|Supported audio codecs.|
43/// |`videoCodecs`|[`Number`]|Supported video codecs.|
44/// |`videoFunctions`|[`Number`]|Supported video functions.|
45/// |`objectEncoding`|[`Number`]|A version of the Action Message Format.|
46///
47/// [`Number`]: crate::messages::amf::v0::Number
48/// [`Boolean`]: crate::messages::amf::v0::Boolean
49/// [`String`]: crate::messages::amf::v0::AmfString
50/// [`Object`]: crate::messages::amf::v0::Object
51#[derive(Debug, Clone, Default, PartialEq)]
52pub struct Connect(Object);
53
54impl Connect {
55    /// Constructs a Connect command.
56    pub fn new(command_object: Object) -> Self {
57        Self(command_object)
58    }
59
60    /// Gets the command object in this request.
61    pub fn get_command_object(&self) -> &Object {
62        &self.0
63    }
64}
65
66impl From<Connect> for Object {
67    fn from(connect: Connect) -> Self {
68        connect.0
69    }
70}
71
72impl Decoder<Connect> for ByteBuffer {
73    /// Decodes bytes into a Connect command.
74    ///
75    /// # Errors
76    ///
77    /// * [`InsufficientBufferLength`]
78    ///
79    /// When some field misses.
80    ///
81    /// * [`InconsistentMarker`]
82    ///
83    /// When some value is inconsistent with its marker.
84    ///
85    /// # Examples
86    ///
87    /// ```rust
88    /// use sheave_core::{
89    ///     ByteBuffer,
90    ///     Decoder,
91    ///     Encoder,
92    ///     messages::{
93    ///         Connect,
94    ///         amf::v0::Object,
95    ///     }
96    /// };
97    ///
98    /// let mut buffer = ByteBuffer::default();
99    /// buffer.encode(&Object::default());
100    /// assert!(Decoder::<Connect>::decode(&mut buffer).is_ok());
101    ///
102    /// let mut buffer = ByteBuffer::default();
103    /// assert!(Decoder::<Connect>::decode(&mut buffer).is_err())
104    /// ```
105    ///
106    /// [`InsufficientBufferLength`]: crate::byte_buffer::InsufficientBufferLength
107    /// [`InconsistentMarker`]: crate::messages::amf::InconsistentMarker
108    fn decode(&mut self) -> IOResult<Connect> {
109        let command_object: Object = self.decode()?;
110        Ok(Connect(command_object))
111    }
112}
113
114impl Encoder<Connect> for ByteBuffer {
115    /// Encodes a Connect command into bytes.
116    fn encode(&mut self, connect: &Connect) {
117        self.encode(connect.get_command_object());
118    }
119}
120
121impl ChunkData for Connect {
122    const CHANNEL: Channel = Channel::System;
123    const MESSAGE_TYPE: MessageType = MessageType::Command;
124}
125
126impl Command for Connect {}
127
128#[cfg(test)]
129mod tests {
130    use crate::{
131        messages::amf::v0::AmfString,
132        object
133    };
134    use super::*;
135
136    #[test]
137    fn decode_connect_input() {
138        let mut buffer = ByteBuffer::default();
139        buffer.encode(
140            &object!(
141                "app" => AmfString::from("ondemand"),
142                "type" => AmfString::from("nonprivate"),
143                "flashVer" => AmfString::from("FMLE/3.0 (compatible; Lavf 60.10.100)"),
144                "tcUrl" => AmfString::from("rtmp://localhost")
145            )
146        );
147        let result: IOResult<Connect> = buffer.decode();
148        assert!(result.is_ok());
149        let actual = result.unwrap();
150        let expected = Connect::new(
151            object!(
152                "app" => AmfString::from("ondemand"),
153                "type" => AmfString::from("nonprivate"),
154                "flashVer" => AmfString::from("FMLE/3.0 (compatible; Lavf 60.10.100)"),
155                "tcUrl" => AmfString::from("rtmp://localhost")
156            )
157        );
158        assert_eq!(expected, actual)
159    }
160
161    #[test]
162    fn encode_connect_input() {
163        let mut buffer = ByteBuffer::default();
164        let expected_command_object = object!(
165            "app" => AmfString::from("ondemand"),
166            "type" => AmfString::from("nonprivate"),
167            "flashVer" => AmfString::from("FMLE/3.0 (compatible; Lavf 60.10.100)"),
168            "tcUrl" => AmfString::from("rtmp://localhost")
169        );
170        let expected = Connect::new(expected_command_object.clone());
171        buffer.encode(&expected);
172        let actual_command_object: Object = buffer.decode().unwrap();
173        assert_eq!(expected_command_object, actual_command_object)
174    }
175}