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