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}