Skip to content

Commit b7d935f

Browse files
committed
Merge branch 'rc3.0.17'
2 parents 76cef4a + 5f402f9 commit b7d935f

File tree

12 files changed

+111
-81
lines changed

12 files changed

+111
-81
lines changed

Changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Echo JS SDK CHANGELOG:
22

3+
##v3.0.17 - February 27, 2014
4+
5+
* **Canvas config loading machinery was updated** to avoid race conditions when a browser tries to retrieve config which was cached by a browser previously. We updated the logic to make sure that an execution order always remains the same.
6+
7+
* **WebSockets connection reestablishment logic** was improved to be more fault tolerant to socket connection closing delays. Previously it caused a delay (up to a few minutes) to switchover to Polling method. Now if the socket is not closed within 10 seconds, we drop the connection and switch to Polling immediately to avoid any delays in live update items appearance.
8+
39
##v3.0.16 - January 14, 2014
410

511
* **StreamServer live updates connection logic was updated** and now if WebSockets are enabled, we try to establish a connection using WS first and fall back to Polling if our attempt failed or timed out (within 5 seconds). Previously both Polling and WebSockets were initialized in parallel and Polling got disabled when WS connection is established. This new connection logic is more efficient from both server-side and client-side standpoints.

NOTICE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
=========================================================================
44

55
Echo Javascript SDK
6-
Copyright 2012-2013, Echo
6+
Copyright 2012-2014, Echo
77

88
This product was initially developed at Echo (AboutEcho.com) with
99
contributions by the wider community.

Readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Release notes are posted to our [Developer Forum](https://groups.google.com/foru
3636
We welcome your contributions. Please submit all pull requests to the master branch and the Echo team will help coordinate.
3737

3838
# License
39-
Copyright 2013 Echo
39+
Copyright 2014 Echo
4040

4141
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with the License. You may obtain a copy of the License in the LICENSE file, or at:
4242

config/jsduck/config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"--title": "Echo JS SDK Documentation Center",
3-
"--footer": "<a class=\"policy\" target=\"_blank\" href=\"http://aboutecho.com/Privacy\">Privacy Policy</a> | © 2013 Echo. All rights reserved.",
3+
"--footer": "<a class=\"policy\" target=\"_blank\" href=\"http://aboutecho.com/Privacy\">Privacy Policy</a> | © 2014 Echo. All rights reserved.",
44
"--": ["../../build"],
55
"--output": "../../web/docs",
66
"--tags": "../../tools/jsduck/custom-tags.rb",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "js-sdk",
33
"title": "Echo JS SDK",
44
"description": "Javascript SDK for Echo applications",
5-
"version": "3.0.16",
5+
"version": "3.0.17",
66
"homepage": "http://echoappsteam.github.io/js-sdk/",
77
"author": {
88
"name": "Echo",

src/api.js

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,16 @@ Echo.API.Transports.WebSockets = utils.inherit(Echo.API.Transport, function(conf
6060
config = $.extend(true, {
6161
"settings": {
6262
"maxConnectRetries": 3,
63-
"serverPingInterval": 30, // client-server ping-pong interval
63+
"serverPingInterval": 30, // client-server ping-pong interval (in seconds)
64+
"closeSocketTimeout": 10, // time (in seconds) we can give WS to close connection
6465
"protocols": ["liveupdate.ws.echoenabled.com"]
6566
}
6667
}, config || {});
67-
this.timers = {};
68+
this.timers = {
69+
"ping": null,
70+
"pong": null,
71+
"close": null
72+
};
6873
this.subscriptionIds = {};
6974
this.unique = Echo.Utils.getUniqueString();
7075
Echo.API.Transports.WebSockets.parent.constructor.call(this, config);
@@ -129,14 +134,24 @@ Echo.API.Transports.WebSockets.prototype.unsubscribe = function unsubscribe(arg)
129134
Echo.API.Transports.WebSockets.prototype.abort = function(force) {
130135
var self = this;
131136
var socket = Echo.API.Transports.WebSockets.socketByURI[this.config.get("uri")];
132-
$.map(["onData", "onOpen", "onError"], $.proxy(this.unsubscribe, this));
133137
if (socket) {
134138
delete socket.subscribers[this.unique];
135-
// close socket connection if the last subscriber left
136-
if ($.isEmptyObject(socket.subscribers) || force) {
137-
if (this.connected()) {
138-
this.transportObject.close();
139+
this._clearTimers();
140+
this.subscribe("onClose", {
141+
"once": true,
142+
"handler": function() {
143+
self.unsubscribe();
139144
}
145+
});
146+
// close socket connection if the last subscriber left
147+
if (($.isEmptyObject(socket.subscribers) || force) && this.connected()) {
148+
// if closing a coonection to WS takes more time than
149+
// setting "closeSocketTimeout" is - we force switchover to Polling
150+
this.timers.close = setTimeout(
151+
$.proxy(this._onCloseHandler, this),
152+
this.config.get("settings.closeSocketTimeout") * 1000
153+
);
154+
this.transportObject.close();
140155
}
141156
}
142157
};
@@ -173,6 +188,9 @@ Echo.API.Transports.WebSockets.prototype._getTransportObject = function() {
173188
self.subscribe(topic, {
174189
"handler": function(_, data) {
175190
self.config.get(topic)(data);
191+
if (topic === "onClose") {
192+
clearTimeout(self.timers.close);
193+
}
176194
},
177195
// when we receive data - send it to the appropriate
178196
// subscribers only (do not send it to all subscribers)
@@ -214,8 +232,7 @@ Echo.API.Transports.WebSockets.prototype._prepareTransportObject = function() {
214232
self._publish("onData", data, data && data.subscription);
215233
};
216234
socket.onclose = function() {
217-
self._publish("onClose");
218-
self._clear();
235+
self._onCloseHandler();
219236
};
220237
socket.onerror = function(error) {
221238
self._publish("onError", self._wrapErrorResponse(error));
@@ -233,18 +250,13 @@ Echo.API.Transports.WebSockets.prototype._clearTimers = function() {
233250
this.timers = {};
234251
};
235252

253+
Echo.API.Transports.WebSockets.prototype._onCloseHandler = function() {
254+
this._publish("onClose");
255+
this._clear();
256+
};
257+
236258
Echo.API.Transports.WebSockets.prototype._clear = function() {
237-
var self = this;
238-
var socket = Echo.API.Transports.WebSockets.socketByURI[this.config.get("uri")];
239259
var context = this.config.get("uri").replace(/\//g, "-");
240-
this._clearTimers();
241-
this.unsubscribe();
242-
$.map(["onData", "onOpen", "onClose", "onError"], function(name) {
243-
Echo.Events.unsubscribe({
244-
"topic": "Echo.API.Transports.WebSockets." + name,
245-
"context": context
246-
});
247-
});
248260
delete Echo.API.Transports.WebSockets.socketByURI[this.config.get("uri")];
249261
};
250262

@@ -290,15 +302,10 @@ Echo.API.Transports.WebSockets.prototype._tryReconnect = function() {
290302
// exit when the connection attempt is scheduled (to prevent
291303
// multiple connections) or if no connection attempts left
292304
if (this.attemptsRemaining === 0) {
293-
this._reconnect();
305+
this.abort(true);
294306
}
295307
};
296308

297-
Echo.API.Transports.WebSockets.prototype._reconnect = function() {
298-
this.abort(true);
299-
this._connect();
300-
};
301-
302309
Echo.API.Transports.WebSockets.available = function() {
303310
return !!(window.WebSocket || window.MozWebSocket);
304311
};

src/canvas.js

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -419,34 +419,40 @@ canvas.methods._fetchConfig = function(callback) {
419419
})
420420
});
421421

422-
$.ajax({
423-
"url": URL,
424-
"crossDomain": true,
425-
"dataType": "script",
426-
"cache": mode !== "dev",
427-
"timeout": Echo.Loader.config.errorTimeout,
428-
"success": function() {
429-
var config = getConfig();
430-
if (!config || !config.apps || !config.apps.length) {
431-
var message = self.labels.get("error_no_" + (config ? "apps" : "config"));
422+
// We rely on asynchronous behavior of the $.ajax method call
423+
// to implement store/fetch mechanics. But it can occasionally
424+
// be synchronous while fetching cached response. In order to
425+
// avoid this issue, we always make $.ajax call asynchronously.
426+
setTimeout(function() {
427+
$.ajax({
428+
"url": URL,
429+
"crossDomain": true,
430+
"dataType": "script",
431+
"cache": mode !== "dev",
432+
"timeout": Echo.Loader.config.errorTimeout,
433+
"success": function() {
434+
var config = getConfig();
435+
if (!config || !config.apps || !config.apps.length) {
436+
var message = self.labels.get("error_no_" + (config ? "apps" : "config"));
437+
self._error({
438+
"args": {"config": config, "target": target},
439+
"code": "invalid_canvas_config",
440+
"message": message
441+
});
442+
return;
443+
}
444+
self.set("data", config); // store Canvas data into the instance
445+
callback.call(self);
446+
},
447+
"error": function() {
432448
self._error({
433-
"args": {"config": config, "target": target},
434-
"code": "invalid_canvas_config",
435-
"message": message
449+
"args": arguments,
450+
"code": "unable_to_retrieve_app_config",
451+
"renderError": true
436452
});
437-
return;
438453
}
439-
self.set("data", config); // store Canvas data into the instance
440-
callback.call(self);
441-
},
442-
"error": function() {
443-
self._error({
444-
"args": arguments,
445-
"code": "unable_to_retrieve_app_config",
446-
"renderError": true
447-
});
448-
}
449-
});
454+
});
455+
}, 0);
450456
};
451457

452458
Echo.Control.create(canvas);

src/streamserver/api.js

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,13 @@ Echo.StreamServer.API.Request = Echo.Utils.inherit(Echo.API.Request, function(co
178178
* Method to stop live updates requests.
179179
*/
180180
Echo.StreamServer.API.Request.prototype.abort = function() {
181+
var self = this;
181182
Echo.StreamServer.API.Request.parent.abort.call(this);
182183
if (this.liveUpdates) {
184+
this.liveUpdates.on("close", function() {
185+
delete self.liveUpdates;
186+
});
183187
this.liveUpdates.stop();
184-
delete this.liveUpdates;
185188
}
186189
};
187190

@@ -320,13 +323,14 @@ Echo.StreamServer.API.Request.prototype._getLiveUpdatesConfig = function(name) {
320323
"resubscribe.resetPeriod": "liveUpdates.websockets.resetPeriod",
321324
"resubscribe.maxResetsPerPeriod": "liveUpdates.websockets.maxResetsPerPeriod",
322325
"request.settings.maxConnectRetries": "liveUpdates.websockets.maxConnectRetries",
323-
"request.settings.serverPingInterval": "liveUpdates.websockets.serverPingInterval"
326+
"request.settings.serverPingInterval": "liveUpdates.websockets.serverPingInterval",
327+
"request.settings.closeSocketTimeout": "liveUpdates.websockets.closeSocketTimeout"
324328
}
325329
};
326330

327331
var mapped = Echo.Utils.foldl({}, map[name], function(from, acc, to) {
328332
var value = function fetch(key) {
329-
var parts, val = self.config.get(key);
333+
var val = self.config.get(key);
330334
if (typeof val === "undefined" && key) {
331335
return fetch(key.split(".").slice(1).join("."));
332336
}
@@ -352,13 +356,13 @@ Echo.StreamServer.API.Request.prototype._liveUpdatesWatcher = function(polling,
352356
var fallbackTimeout, waitingForConnectionTimeout;
353357
ws.on("close", function() {
354358
var timeout, fallback = config.fallback;
355-
if (self.liveUpdates.closeReason !== "abort") {
359+
clearTimeout(waitingForConnectionTimeout);
360+
if (self.liveUpdates && self.liveUpdates.closeReason !== "abort") {
356361
timeout = Echo.Utils.random(
357362
fallback.timeout - fallback.divergence,
358363
fallback.timeout + fallback.divergence
359364
) * 1000;
360365
fallbackTimeout = setTimeout(function() {
361-
clearTimeout(waitingForConnectionTimeout);
362366
switchTo(polling)();
363367
}, timeout);
364368
}
@@ -370,7 +374,7 @@ Echo.StreamServer.API.Request.prototype._liveUpdatesWatcher = function(polling,
370374
switchTo(polling)();
371375
}, config.waitingForConnection * 1000);
372376
if (ws.connected()) {
373-
clearTimeout(waitingForConnectionTimeout)
377+
clearTimeout(waitingForConnectionTimeout);
374378
return;
375379
}
376380
ws.on("open", function() {
@@ -709,7 +713,7 @@ Echo.StreamServer.API.WebSockets = Echo.Utils.inherit(Echo.StreamServer.API.Poll
709713
});
710714
this.queue = [];
711715
this.subscribed = false;
712-
this.subscriptionIds = [];
716+
this.closeReason = "unknown";
713717
this.subscriptionResets = {
714718
"count": 0,
715719
"time": (new Date()).getTime()
@@ -786,7 +790,6 @@ Echo.StreamServer.API.WebSockets.prototype.on = function(event, fn, params) {
786790
"handler": fn
787791
}
788792
);
789-
this.subscriptionIds.push(id);
790793
return id;
791794
};
792795

@@ -798,13 +801,12 @@ Echo.StreamServer.API.WebSockets.prototype.connected = function() {
798801

799802
Echo.StreamServer.API.WebSockets.prototype.stop = function() {
800803
var self = this;
801-
this._clearSubscriptions();
804+
this.closeReason = "abort";
802805
if (this.connected()) {
803806
this.queue.push(function() {
804807
if (self.subscribed) {
805808
self.requestObject.request({"event": "unsubscribe/request"});
806809
}
807-
self.closeReason = "abort";
808810
self.requestObject.abort();
809811
});
810812
this._runQueue();
@@ -852,7 +854,6 @@ Echo.StreamServer.API.WebSockets.prototype._reconnect = function() {
852854
};
853855
this.closeReason = "reconnect";
854856
this.requestObject.abort();
855-
this._clearSubscriptions();
856857
if (this.requestObject.transport.closing()) {
857858
var id = this.on("close", function() {
858859
closeHandler();
@@ -905,11 +906,6 @@ Echo.StreamServer.API.WebSockets.prototype._resubscribeAllowed = function() {
905906
}
906907
};
907908

908-
Echo.StreamServer.API.WebSockets.prototype._clearSubscriptions = function() {
909-
$.map(this.subscriptionIds, $.proxy(this.requestObject.transport.unsubscribe, this.requestObject.transport));
910-
this.subscriptionIds = [];
911-
};
912-
913909
Echo.StreamServer.API.WebSockets.prototype._runQueue = function() {
914910
while (this.queue.length) {
915911
this.queue.shift().call(this);

src/streamserver/controls/facepile.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ pile.config = {
166166
* @cfg {Boolean} [liveUpdates.enabled=true]
167167
* Parameter to enable/disable live updates.
168168
*
169-
* @cfg {String} [liveUpdates.transport="polling"]
169+
* @cfg {String} [liveUpdates.transport="websockets"]
170170
* Preferred live updates receiveing machinery transport.
171171
* The following transports are supported:
172172
*
@@ -199,7 +199,7 @@ pile.config = {
199199
* to keep the connection alive.
200200
*/
201201
"liveUpdates": {
202-
"transport": "polling", // or "websockets"
202+
"transport": "websockets", // or "polling"
203203
"enabled": true,
204204
"polling": {
205205
"timeout": 10

src/streamserver/controls/stream.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ stream.config = {
251251
* @cfg {Boolean} [liveUpdates.enabled=true]
252252
* Parameter to enable/disable live updates.
253253
*
254-
* @cfg {String} [liveUpdates.transport="polling"]
254+
* @cfg {String} [liveUpdates.transport="websockets"]
255255
* Preferred live updates receiveing machinery transport.
256256
* The following transports are supported:
257257
*
@@ -284,7 +284,7 @@ stream.config = {
284284
* to keep the connection alive.
285285
*/
286286
"liveUpdates": {
287-
"transport": "polling", // or "websockets"
287+
"transport": "websockets", // or "polling"
288288
"enabled": true,
289289
"polling": {
290290
"timeout": 10

tests/unit/loader.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,9 @@ Echo.Tests.asyncTest("canvases initialization", function() {
759759
$.map(["stream", "submit"], function(app) {
760760
var def = Echo.jQuery.Deferred();
761761
Echo.Loader.override(id + extra, app, {
762+
"liveUpdates": {
763+
"enabled": false
764+
},
762765
"ready": function() {
763766
def.resolve();
764767
}

0 commit comments

Comments
 (0)