OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 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 {function( | |
16 * function(remoting.SignalStrategy.State) | |
17 * ):remoting.SignalStrategy} primaryFactory | |
18 * @param {function( | |
19 * function(remoting.SignalStrategy.State) | |
20 * ):remoting.SignalStrategy} secondaryFactory | |
21 * @param {function(remoting.SignalStrategy.State):void} onStateChangedCallback | |
22 * @param {function(remoting.FallbackSignalStrategy.Result)} onProgressCallback | |
23 * | |
24 * @implements {remoting.SignalStrategy} | |
25 * @constructor | |
26 */ | |
27 remoting.FallbackSignalStrategy = function( | |
28 primaryFactory, secondaryFactory, | |
29 onStateChangedCallback, onProgressCallback) { | |
30 /** | |
31 * @type {remoting.SignalStrategy} | |
32 * @private | |
33 */ | |
34 this.primary_ = primaryFactory(this.onStateChanged_.bind(this, true)); | |
35 | |
36 /** | |
37 * @type {remoting.SignalStrategy} | |
38 * @private | |
39 */ | |
40 this.secondary_ = secondaryFactory(this.onStateChanged_.bind(this, false)); | |
41 | |
42 /** | |
43 * @type {function(remoting.SignalStrategy.State)} | |
44 * @private | |
45 */ | |
46 this.onStateChangedCallback_ = onStateChangedCallback; | |
47 | |
48 /** | |
49 * @type {function(remoting.FallbackSignalStrategy.Result)} | |
50 * @private | |
51 */ | |
52 this.onProgressCallback_ = onProgressCallback; | |
53 | |
54 /** | |
55 * @type {?function(Element):void} | |
56 * @private | |
57 */ | |
58 this.onIncomingStanzaCallback_ = null; | |
59 | |
60 /** | |
61 * @type {number} | |
62 * @private | |
63 * @const | |
64 */ | |
65 this.PRIMARY_CONNECT_TIMEOUT_MS_ = 10 * 1000; | |
66 | |
67 /** | |
68 * @enum {string} | |
69 * @private | |
70 */ | |
71 this.State = { | |
kelvinp
2015/01/13 19:22:47
Enum should be defined on the 'class' remoting.Fal
Jamie
2015/01/14 01:36:26
I considered that, but it ends up being incredibly
| |
72 NOT_CONNECTED: 'not-connected', | |
73 PRIMARY_PENDING: 'primary-pending', | |
74 PRIMARY_SUCCEEDED: 'primary-succeeded', | |
75 SECONDARY_PENDING: 'secondary-pending', | |
76 SECONDARY_SUCCEEDED: 'secondary-succeeded', | |
77 SECONDARY_FAILED: 'secondary-failed', | |
78 CLOSED: 'closed' | |
79 }; | |
80 | |
81 /** | |
82 * @type {string} | |
83 * @private | |
84 */ | |
85 this.state_ = this.State.NOT_CONNECTED; | |
86 | |
87 /** | |
88 * @type {?remoting.SignalStrategy.State} | |
89 * @private | |
90 */ | |
91 this.externalState_ = null; | |
92 | |
93 /** | |
94 * @type {string} | |
95 * @private | |
96 */ | |
97 this.server_ = ''; | |
98 | |
99 /** | |
100 * @type {string} | |
101 * @private | |
102 */ | |
103 this.username_ = ''; | |
104 | |
105 /** | |
106 * @type {string} | |
107 * @private | |
108 */ | |
109 this.authToken_ = ''; | |
110 | |
111 /** | |
112 * @type {number} | |
113 * @private | |
114 */ | |
115 this.primaryConnectTimerId_ = 0; | |
116 }; | |
117 | |
118 /** | |
119 * @enum {string} | |
120 */ | |
121 remoting.FallbackSignalStrategy.Result = { | |
kelvinp
2015/01/13 19:22:47
Nit: ProgressResult. To make it clear that this i
Jamie
2015/01/14 01:36:26
I changed it to Progress, since it's not always a
kelvinp
2015/01/14 19:46:22
Done.
| |
122 PRIMARY_SUCCEEDED: 'primary-succeeded', | |
123 PRIMARY_FAILED: 'primary-failed', | |
124 PRIMARY_TIMED_OUT: 'primary-timed-out', | |
125 PRIMARY_SUCCEEDED_LATE: 'primary-succeeded-late', | |
126 PRIMARY_FAILED_LATE: 'primary-failed-late', | |
127 SECONDARY_SUCCEEDED: 'secondary-succeeded', | |
128 SECONDARY_FAILED: 'secondary-failed' | |
129 }; | |
130 | |
131 remoting.FallbackSignalStrategy.prototype.dispose = function() { | |
132 this.primary_.dispose(); | |
133 this.secondary_.dispose(); | |
134 }; | |
135 | |
136 /** | |
137 * @param {?function(Element):void} onIncomingStanzaCallback Callback to call on | |
138 * incoming messages. | |
139 */ | |
140 remoting.FallbackSignalStrategy.prototype.setIncomingStanzaCallback = | |
141 function(onIncomingStanzaCallback) { | |
142 this.onIncomingStanzaCallback_ = onIncomingStanzaCallback; | |
143 if (this.state_ == this.State.PRIMARY_PENDING || | |
144 this.state_ == this.State.PRIMARY_SUCCEEDED) { | |
145 this.primary_.setIncomingStanzaCallback(onIncomingStanzaCallback); | |
146 } else if (this.state_ == this.State.SECONDARY_PENDING || | |
147 this.state_ == this.State.SECONDARY_SUCCEEDED) { | |
148 this.secondary_.setIncomingStanzaCallback(onIncomingStanzaCallback); | |
149 } | |
150 }; | |
151 | |
152 /** | |
153 * @param {string} server | |
154 * @param {string} username | |
155 * @param {string} authToken | |
156 */ | |
157 remoting.FallbackSignalStrategy.prototype.connect = | |
158 function(server, username, authToken) { | |
159 base.debug.assert(this.state_ == this.State.NOT_CONNECTED); | |
160 this.server_ = server; | |
161 this.username_ = username; | |
162 this.authToken_ = authToken; | |
163 this.state_ = this.State.PRIMARY_PENDING; | |
164 this.primary_.setIncomingStanzaCallback(this.onIncomingStanzaCallback_); | |
165 this.primary_.connect(server, username, authToken); | |
166 this.primaryConnectTimerId_ = | |
167 window.setTimeout(this.onPrimaryTimeout_.bind(this), | |
168 this.PRIMARY_CONNECT_TIMEOUT_MS_); | |
169 }; | |
170 | |
171 /** | |
172 * Sends a message. Can be called only in CONNECTED state. | |
173 * @param {string} message | |
174 */ | |
175 remoting.FallbackSignalStrategy.prototype.sendMessage = function(message) { | |
176 this.getConnectedSignalStrategy_().sendMessage(message); | |
177 }; | |
178 | |
179 /** @return {remoting.SignalStrategy.State} Current state */ | |
180 remoting.FallbackSignalStrategy.prototype.getState = function() { | |
181 return (this.externalState_ === null) | |
182 ? remoting.SignalStrategy.State.NOT_CONNECTED | |
183 : this.externalState_; | |
184 }; | |
185 | |
186 /** @return {remoting.Error} Error when in FAILED state. */ | |
187 remoting.FallbackSignalStrategy.prototype.getError = function() { | |
188 base.debug.assert(this.state_ == this.State.SECONDARY_FAILED); | |
189 base.debug.assert( | |
190 this.secondary_.getState() == remoting.SignalStrategy.State.FAILED); | |
191 return this.secondary_.getError(); | |
192 }; | |
193 | |
194 /** @return {string} Current JID when in CONNECTED state. */ | |
195 remoting.FallbackSignalStrategy.prototype.getJid = function() { | |
196 return this.getConnectedSignalStrategy_().getJid(); | |
197 }; | |
198 | |
199 /** | |
200 * @return {remoting.SignalStrategy} The active signal strategy, if the | |
201 * connection has succeeded. | |
202 * @private | |
203 */ | |
204 remoting.FallbackSignalStrategy.prototype.getConnectedSignalStrategy_ = | |
205 function() { | |
206 if (this.state_ == this.State.PRIMARY_SUCCEEDED) { | |
207 base.debug.assert( | |
208 this.primary_.getState() == remoting.SignalStrategy.State.CONNECTED); | |
209 return this.primary_; | |
210 } else if (this.state_ == this.State.SECONDARY_SUCCEEDED) { | |
211 base.debug.assert( | |
212 this.secondary_.getState() == remoting.SignalStrategy.State.CONNECTED); | |
213 return this.secondary_; | |
214 } else { | |
215 base.debug.assert( | |
216 false, | |
217 'getConnectedSignalStrategy called in unconnected state'); | |
218 return null; | |
219 } | |
220 }; | |
221 | |
222 /** | |
223 * @param {boolean} isPrimary | |
224 * @param {remoting.SignalStrategy.State} state | |
225 * @private | |
226 */ | |
227 remoting.FallbackSignalStrategy.prototype.onStateChanged_ = | |
Sergey Ulanov
2015/01/13 19:43:51
Maybe separate this into two callbacks, one for pr
Jamie
2015/01/14 01:36:26
Done.
| |
228 function(isPrimary, state) { | |
229 /** | |
230 * @type {boolean} True to notify the external callback of the new state | |
231 * (ie, if it represents a later stage of the connection process than | |
232 * has previously been notified). This ensures that, for example, the | |
233 * external callback won't see a CONNECTING -> NOT_CONNECTED transition | |
234 * when we switch from the primary to the secondary signal strategy. | |
235 */ | |
236 var notifyExternalCallback = (this.externalState_ === null) || | |
237 (state > this.externalState_); | |
238 | |
239 switch (state) { | |
240 case remoting.SignalStrategy.State.CONNECTED: | |
241 if (isPrimary) { | |
242 if (this.state_ == this.State.PRIMARY_PENDING) { | |
243 window.clearTimeout(this.primaryConnectTimerId_); | |
244 this.onProgressCallback_( | |
245 remoting.FallbackSignalStrategy.Result.PRIMARY_SUCCEEDED); | |
246 this.state_ = this.State.PRIMARY_SUCCEEDED; | |
247 } else { | |
248 this.onProgressCallback_( | |
249 remoting.FallbackSignalStrategy.Result.PRIMARY_SUCCEEDED_LATE); | |
250 } | |
251 } else { | |
252 this.onProgressCallback_( | |
kelvinp
2015/01/13 19:22:48
base.debug.assert(this.state_ == this.State.SECOND
Jamie
2015/01/14 01:36:26
I've factored this into two separate functions. LM
kelvinp
2015/01/14 19:46:22
I think the code is much clearer with two separate
| |
253 remoting.FallbackSignalStrategy.Result.SECONDARY_SUCCEEDED); | |
254 this.state_ = this.State.SECONDARY_SUCCEEDED; | |
255 } | |
256 break; | |
257 | |
258 case remoting.SignalStrategy.State.FAILED: | |
259 notifyExternalCallback = !isPrimary; | |
260 if (isPrimary) { | |
261 if (this.state_ == this.State.PRIMARY_PENDING) { | |
262 window.clearTimeout(this.primaryConnectTimerId_); | |
263 this.onProgressCallback_( | |
264 remoting.FallbackSignalStrategy.Result.PRIMARY_FAILED); | |
265 this.reconnect_(); | |
266 } else { | |
267 this.onProgressCallback_( | |
268 remoting.FallbackSignalStrategy.Result.PRIMARY_FAILED_LATE); | |
269 } | |
270 } else { | |
271 this.onProgressCallback_( | |
272 remoting.FallbackSignalStrategy.Result.SECONDARY_FAILED); | |
273 } | |
274 break; | |
275 | |
276 case remoting.SignalStrategy.State.CLOSED: | |
277 this.state_ = this.State.CLOSED; | |
278 break; | |
279 } | |
280 | |
281 if (notifyExternalCallback) { | |
282 this.externalState_ = state; | |
283 this.onStateChangedCallback_(state); | |
284 } | |
285 }; | |
286 | |
287 /** | |
288 * @private | |
289 */ | |
290 remoting.FallbackSignalStrategy.prototype.reconnect_ = function() { | |
Sergey Ulanov
2015/01/13 19:43:51
maybe call it connectSecondary_()?
Jamie
2015/01/14 01:36:26
Done.
| |
291 base.debug.assert(this.state_ == this.State.PRIMARY_PENDING); | |
292 base.debug.assert(this.server_ != ''); | |
293 base.debug.assert(this.username_ != ''); | |
294 base.debug.assert(this.authToken_ != ''); | |
295 | |
296 this.state_ = this.State.SECONDARY_PENDING; | |
297 this.primary_.setIncomingStanzaCallback(null); | |
298 this.secondary_.setIncomingStanzaCallback(this.onIncomingStanzaCallback_); | |
299 this.secondary_.connect(this.server_, this.username_, this.authToken_); | |
kelvinp
2015/01/13 19:22:48
Does the primary and the secondary signal strategy
Sergey Ulanov
2015/01/13 19:43:51
we use the same token for xmpp and wcs
| |
300 }; | |
301 | |
302 /** | |
303 * @private | |
304 */ | |
305 remoting.FallbackSignalStrategy.prototype.onPrimaryTimeout_ = function() { | |
306 this.onProgressCallback_( | |
307 remoting.FallbackSignalStrategy.Result.PRIMARY_TIMED_OUT); | |
308 this.reconnect_(); | |
309 }; | |
310 | |
311 /** | |
312 * @return {remoting.SignalStrategy} The primary signal strategy. | |
313 * @private | |
Sergey Ulanov
2015/01/13 19:43:51
This is called from tests so it isn't really priva
Jamie
2015/01/14 01:36:26
True, but marking it as private means that the com
Sergey Ulanov
2015/01/14 19:42:43
sgtm
| |
314 */ | |
315 remoting.FallbackSignalStrategy.prototype.getPrimaryStrategyForTesting_ = | |
316 function() { | |
317 return this.primary_; | |
318 }; | |
319 | |
320 /** | |
321 * @return {remoting.SignalStrategy} The primary signal strategy. | |
322 * @private | |
323 */ | |
324 remoting.FallbackSignalStrategy.prototype.getSecondaryStrategyForTesting_ = | |
325 function() { | |
326 return this.secondary_; | |
327 }; | |
OLD | NEW |