OLD | NEW |
| (Empty) |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 'use strict'; | |
6 | |
7 /** @suppress {duplicate} */ | |
8 var remoting = remoting || {}; | |
9 | |
10 /** | |
11 * A signal strategy encapsulating a primary and a back-up strategy. If the | |
12 * primary fails or times out, then the secondary is used. Information about | |
13 * which strategy was used, and why, is returned via |onProgressCallback|. | |
14 * | |
15 * @param {remoting.SignalStrategy} primary | |
16 * @param {remoting.SignalStrategy} secondary | |
17 * | |
18 * @implements {remoting.SignalStrategy} | |
19 * @constructor | |
20 */ | |
21 remoting.FallbackSignalStrategy = function(primary, | |
22 secondary) { | |
23 /** @private {remoting.SignalStrategy} */ | |
24 this.primary_ = primary; | |
25 this.primary_.setStateChangedCallback(this.onPrimaryStateChanged_.bind(this)); | |
26 | |
27 /** @private {remoting.SignalStrategy} */ | |
28 this.secondary_ = secondary; | |
29 this.secondary_.setStateChangedCallback( | |
30 this.onSecondaryStateChanged_.bind(this)); | |
31 | |
32 /** @private {?function(remoting.SignalStrategy.State)} */ | |
33 this.onStateChangedCallback_ = null; | |
34 | |
35 /** @private {?function(Element):void} */ | |
36 this.onIncomingStanzaCallback_ = null; | |
37 | |
38 /** | |
39 * @private {number} | |
40 * @const | |
41 */ | |
42 this.PRIMARY_CONNECT_TIMEOUT_MS_ = 10 * 1000; | |
43 | |
44 /** | |
45 * @enum {string} | |
46 * @private | |
47 */ | |
48 this.State = { | |
49 NOT_CONNECTED: 'not-connected', | |
50 PRIMARY_PENDING: 'primary-pending', | |
51 PRIMARY_SUCCEEDED: 'primary-succeeded', | |
52 SECONDARY_PENDING: 'secondary-pending', | |
53 SECONDARY_SUCCEEDED: 'secondary-succeeded', | |
54 SECONDARY_FAILED: 'secondary-failed', | |
55 CLOSED: 'closed' | |
56 }; | |
57 | |
58 /** @private {string} */ | |
59 this.state_ = this.State.NOT_CONNECTED; | |
60 | |
61 /** @private {?remoting.SignalStrategy.State} */ | |
62 this.externalState_ = null; | |
63 | |
64 /** @private {string} */ | |
65 this.server_ = ''; | |
66 | |
67 /** @private {string} */ | |
68 this.username_ = ''; | |
69 | |
70 /** @private {string} */ | |
71 this.authToken_ = ''; | |
72 | |
73 /** @private {number} */ | |
74 this.primaryConnectTimerId_ = 0; | |
75 | |
76 /** @private {remoting.LogToServer} */ | |
77 this.logToServer_ = null; | |
78 | |
79 /** | |
80 * @type {Array<{strategyType: remoting.SignalStrategy.Type, | |
81 progress: remoting.FallbackSignalStrategy.Progress}>} | |
82 */ | |
83 this.connectionSetupResults_ = []; | |
84 | |
85 }; | |
86 | |
87 /** | |
88 * @enum {string} | |
89 */ | |
90 remoting.FallbackSignalStrategy.Progress = { | |
91 SUCCEEDED: 'succeeded', | |
92 FAILED: 'failed', | |
93 TIMED_OUT: 'timed-out', | |
94 SUCCEEDED_LATE: 'succeeded-late', | |
95 FAILED_LATE: 'failed-late', | |
96 }; | |
97 | |
98 remoting.FallbackSignalStrategy.prototype.dispose = function() { | |
99 this.primary_.dispose(); | |
100 this.secondary_.dispose(); | |
101 }; | |
102 | |
103 /** | |
104 * @param {function(remoting.SignalStrategy.State):void} onStateChangedCallback | |
105 * Callback to call on state change. | |
106 */ | |
107 remoting.FallbackSignalStrategy.prototype.setStateChangedCallback = function( | |
108 onStateChangedCallback) { | |
109 this.onStateChangedCallback_ = onStateChangedCallback; | |
110 }; | |
111 | |
112 /** | |
113 * @param {?function(Element):void} onIncomingStanzaCallback Callback to call on | |
114 * incoming messages. | |
115 */ | |
116 remoting.FallbackSignalStrategy.prototype.setIncomingStanzaCallback = | |
117 function(onIncomingStanzaCallback) { | |
118 this.onIncomingStanzaCallback_ = onIncomingStanzaCallback; | |
119 if (this.state_ == this.State.PRIMARY_PENDING || | |
120 this.state_ == this.State.PRIMARY_SUCCEEDED) { | |
121 this.primary_.setIncomingStanzaCallback(onIncomingStanzaCallback); | |
122 } else if (this.state_ == this.State.SECONDARY_PENDING || | |
123 this.state_ == this.State.SECONDARY_SUCCEEDED) { | |
124 this.secondary_.setIncomingStanzaCallback(onIncomingStanzaCallback); | |
125 } | |
126 }; | |
127 | |
128 /** | |
129 * @param {string} server | |
130 * @param {string} username | |
131 * @param {string} authToken | |
132 */ | |
133 remoting.FallbackSignalStrategy.prototype.connect = | |
134 function(server, username, authToken) { | |
135 base.debug.assert(this.state_ == this.State.NOT_CONNECTED); | |
136 base.debug.assert(this.onStateChangedCallback_ != null); | |
137 this.server_ = server; | |
138 this.username_ = username; | |
139 this.authToken_ = authToken; | |
140 this.state_ = this.State.PRIMARY_PENDING; | |
141 this.primary_.setIncomingStanzaCallback(this.onIncomingStanzaCallback_); | |
142 this.primary_.connect(server, username, authToken); | |
143 this.primaryConnectTimerId_ = | |
144 window.setTimeout(this.onPrimaryTimeout_.bind(this), | |
145 this.PRIMARY_CONNECT_TIMEOUT_MS_); | |
146 }; | |
147 | |
148 /** | |
149 * Sends a message. Can be called only in CONNECTED state. | |
150 * @param {string} message | |
151 */ | |
152 remoting.FallbackSignalStrategy.prototype.sendMessage = function(message) { | |
153 this.getConnectedSignalStrategy_().sendMessage(message); | |
154 }; | |
155 | |
156 /** | |
157 * Send any messages accumulated during connection set-up. | |
158 * | |
159 * @param {remoting.LogToServer} logToServer The LogToServer instance for the | |
160 * connection. | |
161 */ | |
162 remoting.FallbackSignalStrategy.prototype.sendConnectionSetupResults = | |
163 function(logToServer) { | |
164 this.logToServer_ = logToServer; | |
165 this.sendConnectionSetupResultsInternal_(); | |
166 } | |
167 | |
168 remoting.FallbackSignalStrategy.prototype.sendConnectionSetupResultsInternal_ = | |
169 function() { | |
170 for (var i = 0; i < this.connectionSetupResults_.length; ++i) { | |
171 var result = this.connectionSetupResults_[i]; | |
172 this.logToServer_.logSignalStrategyProgress(result.strategyType, | |
173 result.progress); | |
174 } | |
175 this.connectionSetupResults_ = []; | |
176 }; | |
177 | |
178 /** @return {remoting.SignalStrategy.State} Current state */ | |
179 remoting.FallbackSignalStrategy.prototype.getState = function() { | |
180 return (this.externalState_ === null) | |
181 ? remoting.SignalStrategy.State.NOT_CONNECTED | |
182 : this.externalState_; | |
183 }; | |
184 | |
185 /** @return {!remoting.Error} Error when in FAILED state. */ | |
186 remoting.FallbackSignalStrategy.prototype.getError = function() { | |
187 base.debug.assert(this.state_ == this.State.SECONDARY_FAILED); | |
188 base.debug.assert( | |
189 this.secondary_.getState() == remoting.SignalStrategy.State.FAILED); | |
190 return this.secondary_.getError(); | |
191 }; | |
192 | |
193 /** @return {string} Current JID when in CONNECTED state. */ | |
194 remoting.FallbackSignalStrategy.prototype.getJid = function() { | |
195 return this.getConnectedSignalStrategy_().getJid(); | |
196 }; | |
197 | |
198 /** @return {remoting.SignalStrategy.Type} The signal strategy type. */ | |
199 remoting.FallbackSignalStrategy.prototype.getType = function() { | |
200 return this.getConnectedSignalStrategy_().getType(); | |
201 }; | |
202 | |
203 /** | |
204 * @return {remoting.SignalStrategy} The active signal strategy, if the | |
205 * connection has succeeded. | |
206 * @private | |
207 */ | |
208 remoting.FallbackSignalStrategy.prototype.getConnectedSignalStrategy_ = | |
209 function() { | |
210 if (this.state_ == this.State.PRIMARY_SUCCEEDED) { | |
211 base.debug.assert( | |
212 this.primary_.getState() == remoting.SignalStrategy.State.CONNECTED); | |
213 return this.primary_; | |
214 } else if (this.state_ == this.State.SECONDARY_SUCCEEDED) { | |
215 base.debug.assert( | |
216 this.secondary_.getState() == remoting.SignalStrategy.State.CONNECTED); | |
217 return this.secondary_; | |
218 } else { | |
219 base.debug.assert( | |
220 false, | |
221 'getConnectedSignalStrategy called in unconnected state'); | |
222 return null; | |
223 } | |
224 }; | |
225 | |
226 /** | |
227 * @param {remoting.SignalStrategy.State} state | |
228 * @private | |
229 */ | |
230 remoting.FallbackSignalStrategy.prototype.onPrimaryStateChanged_ = | |
231 function(state) { | |
232 switch (state) { | |
233 case remoting.SignalStrategy.State.CONNECTED: | |
234 if (this.state_ == this.State.PRIMARY_PENDING) { | |
235 window.clearTimeout(this.primaryConnectTimerId_); | |
236 this.updateProgress_( | |
237 this.primary_, | |
238 remoting.FallbackSignalStrategy.Progress.SUCCEEDED); | |
239 this.state_ = this.State.PRIMARY_SUCCEEDED; | |
240 } else { | |
241 this.updateProgress_( | |
242 this.primary_, | |
243 remoting.FallbackSignalStrategy.Progress.SUCCEEDED_LATE); | |
244 } | |
245 break; | |
246 | |
247 case remoting.SignalStrategy.State.FAILED: | |
248 if (this.state_ == this.State.PRIMARY_PENDING) { | |
249 window.clearTimeout(this.primaryConnectTimerId_); | |
250 this.updateProgress_( | |
251 this.primary_, | |
252 remoting.FallbackSignalStrategy.Progress.FAILED); | |
253 this.connectSecondary_(); | |
254 } else { | |
255 this.updateProgress_( | |
256 this.primary_, | |
257 remoting.FallbackSignalStrategy.Progress.FAILED_LATE); | |
258 } | |
259 return; // Don't notify the external callback | |
260 | |
261 case remoting.SignalStrategy.State.CLOSED: | |
262 this.state_ = this.State.CLOSED; | |
263 break; | |
264 } | |
265 | |
266 this.notifyExternalCallback_(state); | |
267 }; | |
268 | |
269 /** | |
270 * @param {remoting.SignalStrategy.State} state | |
271 * @private | |
272 */ | |
273 remoting.FallbackSignalStrategy.prototype.onSecondaryStateChanged_ = | |
274 function(state) { | |
275 switch (state) { | |
276 case remoting.SignalStrategy.State.CONNECTED: | |
277 this.updateProgress_( | |
278 this.secondary_, | |
279 remoting.FallbackSignalStrategy.Progress.SUCCEEDED); | |
280 this.state_ = this.State.SECONDARY_SUCCEEDED; | |
281 break; | |
282 | |
283 case remoting.SignalStrategy.State.FAILED: | |
284 this.updateProgress_( | |
285 this.secondary_, | |
286 remoting.FallbackSignalStrategy.Progress.FAILED); | |
287 this.state_ = this.State.SECONDARY_FAILED; | |
288 break; | |
289 | |
290 case remoting.SignalStrategy.State.CLOSED: | |
291 this.state_ = this.State.CLOSED; | |
292 break; | |
293 } | |
294 | |
295 this.notifyExternalCallback_(state); | |
296 }; | |
297 | |
298 /** | |
299 * Notify the external callback of a change in state if it's consistent with | |
300 * the allowed state transitions (ie, if it represents a later stage in the | |
301 * connection process). Suppress state transitions that would violate this, | |
302 * for example a CONNECTING -> NOT_CONNECTED transition when we switch from | |
303 * the primary to the secondary signal strategy. | |
304 * | |
305 * @param {remoting.SignalStrategy.State} state | |
306 * @private | |
307 */ | |
308 remoting.FallbackSignalStrategy.prototype.notifyExternalCallback_ = | |
309 function(state) { | |
310 if (this.externalState_ === null || state > this.externalState_) { | |
311 this.externalState_ = state; | |
312 this.onStateChangedCallback_(state); | |
313 } | |
314 }; | |
315 | |
316 /** | |
317 * @private | |
318 */ | |
319 remoting.FallbackSignalStrategy.prototype.connectSecondary_ = function() { | |
320 base.debug.assert(this.state_ == this.State.PRIMARY_PENDING); | |
321 base.debug.assert(this.server_ != ''); | |
322 base.debug.assert(this.username_ != ''); | |
323 base.debug.assert(this.authToken_ != ''); | |
324 | |
325 this.state_ = this.State.SECONDARY_PENDING; | |
326 this.primary_.setIncomingStanzaCallback(null); | |
327 this.secondary_.setIncomingStanzaCallback(this.onIncomingStanzaCallback_); | |
328 this.secondary_.connect(this.server_, this.username_, this.authToken_); | |
329 }; | |
330 | |
331 /** | |
332 * @private | |
333 */ | |
334 remoting.FallbackSignalStrategy.prototype.onPrimaryTimeout_ = function() { | |
335 this.updateProgress_( | |
336 this.primary_, | |
337 remoting.FallbackSignalStrategy.Progress.TIMED_OUT); | |
338 this.connectSecondary_(); | |
339 }; | |
340 | |
341 /** | |
342 * @param {remoting.SignalStrategy} strategy | |
343 * @param {remoting.FallbackSignalStrategy.Progress} progress | |
344 * @private | |
345 */ | |
346 remoting.FallbackSignalStrategy.prototype.updateProgress_ = function( | |
347 strategy, progress) { | |
348 console.log('FallbackSignalStrategy progress: ' + strategy.getType() + ' ' + | |
349 progress); | |
350 this.connectionSetupResults_.push({ | |
351 'strategyType': strategy.getType(), | |
352 'progress': progress | |
353 }); | |
354 if (this.logToServer_) { | |
355 this.sendConnectionSetupResultsInternal_(); | |
356 } | |
357 }; | |
OLD | NEW |