Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(174)

Side by Side Diff: remoting/webapp/crd/js/fallback_signal_strategy.js

Issue 815943004: Implement FallbackSignalStrategy. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698