1 use crate::error::{Error, Result};
2 use crate::peer_connection::sdp::sdp_type::RTCSdpType;
3 
4 use std::fmt;
5 
6 #[derive(Default, Debug, Copy, Clone, PartialEq)]
7 pub(crate) enum StateChangeOp {
8     #[default]
9     SetLocal,
10     SetRemote,
11 }
12 
13 impl fmt::Display for StateChangeOp {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result14     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15         match *self {
16             StateChangeOp::SetLocal => write!(f, "SetLocal"),
17             StateChangeOp::SetRemote => write!(f, "SetRemote"),
18             //_ => write!(f, UNSPECIFIED_STR),
19         }
20     }
21 }
22 
23 /// SignalingState indicates the signaling state of the offer/answer process.
24 #[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
25 pub enum RTCSignalingState {
26     #[default]
27     Unspecified = 0,
28 
29     /// SignalingStateStable indicates there is no offer/answer exchange in
30     /// progress. This is also the initial state, in which case the local and
31     /// remote descriptions are nil.
32     Stable,
33 
34     /// SignalingStateHaveLocalOffer indicates that a local description, of
35     /// type "offer", has been successfully applied.
36     HaveLocalOffer,
37 
38     /// SignalingStateHaveRemoteOffer indicates that a remote description, of
39     /// type "offer", has been successfully applied.
40     HaveRemoteOffer,
41 
42     /// SignalingStateHaveLocalPranswer indicates that a remote description
43     /// of type "offer" has been successfully applied and a local description
44     /// of type "pranswer" has been successfully applied.
45     HaveLocalPranswer,
46 
47     /// SignalingStateHaveRemotePranswer indicates that a local description
48     /// of type "offer" has been successfully applied and a remote description
49     /// of type "pranswer" has been successfully applied.
50     HaveRemotePranswer,
51 
52     /// SignalingStateClosed indicates The PeerConnection has been closed.
53     Closed,
54 }
55 
56 const SIGNALING_STATE_STABLE_STR: &str = "stable";
57 const SIGNALING_STATE_HAVE_LOCAL_OFFER_STR: &str = "have-local-offer";
58 const SIGNALING_STATE_HAVE_REMOTE_OFFER_STR: &str = "have-remote-offer";
59 const SIGNALING_STATE_HAVE_LOCAL_PRANSWER_STR: &str = "have-local-pranswer";
60 const SIGNALING_STATE_HAVE_REMOTE_PRANSWER_STR: &str = "have-remote-pranswer";
61 const SIGNALING_STATE_CLOSED_STR: &str = "closed";
62 
63 impl From<&str> for RTCSignalingState {
from(raw: &str) -> Self64     fn from(raw: &str) -> Self {
65         match raw {
66             SIGNALING_STATE_STABLE_STR => RTCSignalingState::Stable,
67             SIGNALING_STATE_HAVE_LOCAL_OFFER_STR => RTCSignalingState::HaveLocalOffer,
68             SIGNALING_STATE_HAVE_REMOTE_OFFER_STR => RTCSignalingState::HaveRemoteOffer,
69             SIGNALING_STATE_HAVE_LOCAL_PRANSWER_STR => RTCSignalingState::HaveLocalPranswer,
70             SIGNALING_STATE_HAVE_REMOTE_PRANSWER_STR => RTCSignalingState::HaveRemotePranswer,
71             SIGNALING_STATE_CLOSED_STR => RTCSignalingState::Closed,
72             _ => RTCSignalingState::Unspecified,
73         }
74     }
75 }
76 
77 impl fmt::Display for RTCSignalingState {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result78     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79         match *self {
80             RTCSignalingState::Stable => write!(f, "{SIGNALING_STATE_STABLE_STR}"),
81             RTCSignalingState::HaveLocalOffer => {
82                 write!(f, "{SIGNALING_STATE_HAVE_LOCAL_OFFER_STR}")
83             }
84             RTCSignalingState::HaveRemoteOffer => {
85                 write!(f, "{SIGNALING_STATE_HAVE_REMOTE_OFFER_STR}")
86             }
87             RTCSignalingState::HaveLocalPranswer => {
88                 write!(f, "{SIGNALING_STATE_HAVE_LOCAL_PRANSWER_STR}")
89             }
90             RTCSignalingState::HaveRemotePranswer => {
91                 write!(f, "{SIGNALING_STATE_HAVE_REMOTE_PRANSWER_STR}")
92             }
93             RTCSignalingState::Closed => write!(f, "{SIGNALING_STATE_CLOSED_STR}"),
94             _ => write!(f, "{}", crate::UNSPECIFIED_STR),
95         }
96     }
97 }
98 
99 impl From<u8> for RTCSignalingState {
from(v: u8) -> Self100     fn from(v: u8) -> Self {
101         match v {
102             1 => RTCSignalingState::Stable,
103             2 => RTCSignalingState::HaveLocalOffer,
104             3 => RTCSignalingState::HaveRemoteOffer,
105             4 => RTCSignalingState::HaveLocalPranswer,
106             5 => RTCSignalingState::HaveRemotePranswer,
107             6 => RTCSignalingState::Closed,
108             _ => RTCSignalingState::Unspecified,
109         }
110     }
111 }
112 
check_next_signaling_state( cur: RTCSignalingState, next: RTCSignalingState, op: StateChangeOp, sdp_type: RTCSdpType, ) -> Result<RTCSignalingState>113 pub(crate) fn check_next_signaling_state(
114     cur: RTCSignalingState,
115     next: RTCSignalingState,
116     op: StateChangeOp,
117     sdp_type: RTCSdpType,
118 ) -> Result<RTCSignalingState> {
119     // Special case for rollbacks
120     if sdp_type == RTCSdpType::Rollback && cur == RTCSignalingState::Stable {
121         return Err(Error::ErrSignalingStateCannotRollback);
122     }
123 
124     // 4.3.1 valid state transitions
125     match cur {
126         RTCSignalingState::Stable => {
127             match op {
128                 StateChangeOp::SetLocal => {
129                     // stable->SetLocal(offer)->have-local-offer
130                     if sdp_type == RTCSdpType::Offer && next == RTCSignalingState::HaveLocalOffer {
131                         return Ok(next);
132                     }
133                 }
134                 StateChangeOp::SetRemote => {
135                     // stable->SetRemote(offer)->have-remote-offer
136                     if sdp_type == RTCSdpType::Offer && next == RTCSignalingState::HaveRemoteOffer {
137                         return Ok(next);
138                     }
139                 }
140             }
141         }
142         RTCSignalingState::HaveLocalOffer => {
143             if op == StateChangeOp::SetRemote {
144                 match sdp_type {
145                     // have-local-offer->SetRemote(answer)->stable
146                     RTCSdpType::Answer => {
147                         if next == RTCSignalingState::Stable {
148                             return Ok(next);
149                         }
150                     }
151                     // have-local-offer->SetRemote(pranswer)->have-remote-pranswer
152                     RTCSdpType::Pranswer => {
153                         if next == RTCSignalingState::HaveRemotePranswer {
154                             return Ok(next);
155                         }
156                     }
157                     _ => {}
158                 }
159             } else if op == StateChangeOp::SetLocal
160                 && sdp_type == RTCSdpType::Offer
161                 && next == RTCSignalingState::HaveLocalOffer
162             {
163                 return Ok(next);
164             }
165         }
166         RTCSignalingState::HaveRemotePranswer => {
167             if op == StateChangeOp::SetRemote && sdp_type == RTCSdpType::Answer {
168                 // have-remote-pranswer->SetRemote(answer)->stable
169                 if next == RTCSignalingState::Stable {
170                     return Ok(next);
171                 }
172             }
173         }
174         RTCSignalingState::HaveRemoteOffer => {
175             if op == StateChangeOp::SetLocal {
176                 match sdp_type {
177                     // have-remote-offer->SetLocal(answer)->stable
178                     RTCSdpType::Answer => {
179                         if next == RTCSignalingState::Stable {
180                             return Ok(next);
181                         }
182                     }
183                     // have-remote-offer->SetLocal(pranswer)->have-local-pranswer
184                     RTCSdpType::Pranswer => {
185                         if next == RTCSignalingState::HaveLocalPranswer {
186                             return Ok(next);
187                         }
188                     }
189                     _ => {}
190                 }
191             }
192         }
193         RTCSignalingState::HaveLocalPranswer => {
194             if op == StateChangeOp::SetLocal && sdp_type == RTCSdpType::Answer {
195                 // have-local-pranswer->SetLocal(answer)->stable
196                 if next == RTCSignalingState::Stable {
197                     return Ok(next);
198                 }
199             }
200         }
201         _ => {
202             return Err(Error::ErrSignalingStateProposedTransitionInvalid {
203                 from: cur,
204                 applying: sdp_type,
205                 is_local: op == StateChangeOp::SetLocal,
206             });
207         }
208     };
209 
210     Err(Error::ErrSignalingStateProposedTransitionInvalid {
211         from: cur,
212         is_local: op == StateChangeOp::SetLocal,
213         applying: sdp_type,
214     })
215 }
216 
217 #[cfg(test)]
218 mod test {
219     use super::*;
220 
221     #[test]
test_new_signaling_state()222     fn test_new_signaling_state() {
223         let tests = vec![
224             ("Unspecified", RTCSignalingState::Unspecified),
225             ("stable", RTCSignalingState::Stable),
226             ("have-local-offer", RTCSignalingState::HaveLocalOffer),
227             ("have-remote-offer", RTCSignalingState::HaveRemoteOffer),
228             ("have-local-pranswer", RTCSignalingState::HaveLocalPranswer),
229             (
230                 "have-remote-pranswer",
231                 RTCSignalingState::HaveRemotePranswer,
232             ),
233             ("closed", RTCSignalingState::Closed),
234         ];
235 
236         for (state_string, expected_state) in tests {
237             assert_eq!(RTCSignalingState::from(state_string), expected_state);
238         }
239     }
240 
241     #[test]
test_signaling_state_string()242     fn test_signaling_state_string() {
243         let tests = vec![
244             (RTCSignalingState::Unspecified, "Unspecified"),
245             (RTCSignalingState::Stable, "stable"),
246             (RTCSignalingState::HaveLocalOffer, "have-local-offer"),
247             (RTCSignalingState::HaveRemoteOffer, "have-remote-offer"),
248             (RTCSignalingState::HaveLocalPranswer, "have-local-pranswer"),
249             (
250                 RTCSignalingState::HaveRemotePranswer,
251                 "have-remote-pranswer",
252             ),
253             (RTCSignalingState::Closed, "closed"),
254         ];
255 
256         for (state, expected_string) in tests {
257             assert_eq!(state.to_string(), expected_string);
258         }
259     }
260 
261     #[test]
test_signaling_state_transitions()262     fn test_signaling_state_transitions() {
263         let tests = vec![
264             (
265                 "stable->SetLocal(offer)->have-local-offer",
266                 RTCSignalingState::Stable,
267                 RTCSignalingState::HaveLocalOffer,
268                 StateChangeOp::SetLocal,
269                 RTCSdpType::Offer,
270                 None,
271             ),
272             (
273                 "stable->SetRemote(offer)->have-remote-offer",
274                 RTCSignalingState::Stable,
275                 RTCSignalingState::HaveRemoteOffer,
276                 StateChangeOp::SetRemote,
277                 RTCSdpType::Offer,
278                 None,
279             ),
280             (
281                 "have-local-offer->SetRemote(answer)->stable",
282                 RTCSignalingState::HaveLocalOffer,
283                 RTCSignalingState::Stable,
284                 StateChangeOp::SetRemote,
285                 RTCSdpType::Answer,
286                 None,
287             ),
288             (
289                 "have-local-offer->SetRemote(pranswer)->have-remote-pranswer",
290                 RTCSignalingState::HaveLocalOffer,
291                 RTCSignalingState::HaveRemotePranswer,
292                 StateChangeOp::SetRemote,
293                 RTCSdpType::Pranswer,
294                 None,
295             ),
296             (
297                 "have-remote-pranswer->SetRemote(answer)->stable",
298                 RTCSignalingState::HaveRemotePranswer,
299                 RTCSignalingState::Stable,
300                 StateChangeOp::SetRemote,
301                 RTCSdpType::Answer,
302                 None,
303             ),
304             (
305                 "have-remote-offer->SetLocal(answer)->stable",
306                 RTCSignalingState::HaveRemoteOffer,
307                 RTCSignalingState::Stable,
308                 StateChangeOp::SetLocal,
309                 RTCSdpType::Answer,
310                 None,
311             ),
312             (
313                 "have-remote-offer->SetLocal(pranswer)->have-local-pranswer",
314                 RTCSignalingState::HaveRemoteOffer,
315                 RTCSignalingState::HaveLocalPranswer,
316                 StateChangeOp::SetLocal,
317                 RTCSdpType::Pranswer,
318                 None,
319             ),
320             (
321                 "have-local-pranswer->SetLocal(answer)->stable",
322                 RTCSignalingState::HaveLocalPranswer,
323                 RTCSignalingState::Stable,
324                 StateChangeOp::SetLocal,
325                 RTCSdpType::Answer,
326                 None,
327             ),
328             (
329                 "(invalid) stable->SetRemote(pranswer)->have-remote-pranswer",
330                 RTCSignalingState::Stable,
331                 RTCSignalingState::HaveRemotePranswer,
332                 StateChangeOp::SetRemote,
333                 RTCSdpType::Pranswer,
334                 Some(Error::ErrSignalingStateProposedTransitionInvalid {
335                     from: RTCSignalingState::Stable,
336                     is_local: false,
337                     applying: RTCSdpType::Pranswer,
338                 }),
339             ),
340             (
341                 "(invalid) stable->SetRemote(rollback)->have-local-offer",
342                 RTCSignalingState::Stable,
343                 RTCSignalingState::HaveLocalOffer,
344                 StateChangeOp::SetRemote,
345                 RTCSdpType::Rollback,
346                 Some(Error::ErrSignalingStateCannotRollback),
347             ),
348         ];
349 
350         for (desc, cur, next, op, sdp_type, expected_err) in tests {
351             let result = check_next_signaling_state(cur, next, op, sdp_type);
352             match (&result, &expected_err) {
353                 (Ok(got), None) => {
354                     assert_eq!(*got, next, "{desc} state mismatch");
355                 }
356                 (Err(got), Some(err)) => {
357                     assert_eq!(got.to_string(), err.to_string(), "{desc} error mismatch");
358                 }
359                 _ => {
360                     panic!("{desc}: expected {expected_err:?}, but got {result:?}");
361                 }
362             };
363         }
364     }
365 }
366