From 40e2a608f6a0520969c9104fc653e6992e6aef16 Mon Sep 17 00:00:00 2001 From: calzoneman Date: Mon, 19 Oct 2015 22:32:00 -0700 Subject: [PATCH 1/5] Initial sioconfig migration work --- NEWS.md | 22 +++++++++++++++ package.json | 2 +- src/configuration/ioconfig.js | 23 ++++++++++++++++ src/io/cluster/nullclusterclient.js | 11 ++++++++ src/web/routes/socketconfig.js | 24 +++++++++++++++++ src/web/webserver.js | 4 ++- templates/channel.jade | 1 - www/js/callbacks.js | 42 ++++++++++++++++++----------- 8 files changed, 111 insertions(+), 18 deletions(-) create mode 100644 src/configuration/ioconfig.js create mode 100644 src/io/cluster/nullclusterclient.js create mode 100644 src/web/routes/socketconfig.js diff --git a/NEWS.md b/NEWS.md index 45446c1b..11cc4792 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,25 @@ +2015-10-19 +========== + +In order to support future clustering support, the legacy `/sioconfig` +endpoint is being deprecated. Instead, you should make a request to +`/socketconfig/.json`. The response will look similar to +these: + +```json +{"url":"https://some-website.com:8443","secure":true} +{"error":"Channel \"!@#$\" does not exist."} +``` + +The `url` key specifies the socket.io URL to connect to, and the `secure` +key indicates whether the connection is secured with TLS. If an `error` key +is present, something went wrong and the value will contain an error +message. + +For now, only one URL is returned, however in the future this may be +extended by adding an `alt` key specifying an array of acceptable URLs to +connect to. + 2015-10-04 ========== diff --git a/package.json b/package.json index 454beddc..16de6baa 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "Calvin Montgomery", "name": "CyTube", "description": "Online media synchronizer and chat", - "version": "3.11.2", + "version": "3.12.0", "repository": { "url": "http://github.com/calzoneman/sync" }, diff --git a/src/configuration/ioconfig.js b/src/configuration/ioconfig.js new file mode 100644 index 00000000..9fa96a0b --- /dev/null +++ b/src/configuration/ioconfig.js @@ -0,0 +1,23 @@ +export default class IOConfiguration { + constructor(config) { + this.config = config; + } + + getSocketURL() { + return this.config.urls[0]; + } +} + +IOConfiguration.fromOldConfig = function (oldConfig) { + const config = { + urls: [] + }; + + ['ipv4-ssl', 'ipv4-nossl', 'ipv6-ssl', 'ipv6-nossl'].forEach(key => { + if (oldConfig.get('io.' + key)) { + config.urls.push(oldConfig.get('io.' + key)); + } + }); + + return new IOConfiguration(config); +}; diff --git a/src/io/cluster/nullclusterclient.js b/src/io/cluster/nullclusterclient.js new file mode 100644 index 00000000..b320e0d0 --- /dev/null +++ b/src/io/cluster/nullclusterclient.js @@ -0,0 +1,11 @@ +import Promise from 'bluebird'; + +export default class NullClusterClient { + constructor(ioConfig) { + this.ioConfig = ioConfig; + } + + getSocketURL(channel) { + return Promise.resolve(this.ioConfig.getSocketURL()); + } +} diff --git a/src/web/routes/socketconfig.js b/src/web/routes/socketconfig.js new file mode 100644 index 00000000..e45437f3 --- /dev/null +++ b/src/web/routes/socketconfig.js @@ -0,0 +1,24 @@ +import IOConfiguration from '../../configuration/ioconfig'; +import NullClusterClient from '../../io/cluster/nullclusterclient'; +import Config from '../../config'; +import CyTubeUtil from '../../utilities'; + +export default function initialize(app) { + const ioConfig = IOConfiguration.fromOldConfig(Config); + const clusterClient = new NullClusterClient(ioConfig); + + app.get('/socketconfig/:channel.json', (req, res) => { + if (!req.params.channel || !CyTubeUtil.isValidChannelName(req.params.channel)) { + return res.status(400).json({ + error: `Channel "${req.params.channel}" does not exist.` + }); + } + + clusterClient.getSocketURL(req.params.channel).then(url => { + res.json({ + url, + secure: /^(https|wss)/.test(url) + }); + }); + }); +} diff --git a/src/web/webserver.js b/src/web/webserver.js index 3bd43647..a15d0d47 100644 --- a/src/web/webserver.js +++ b/src/web/webserver.js @@ -117,7 +117,8 @@ function handleIndex(req, res) { } /** - * Handles a request for the socket.io information + * Legacy socket.io configuration endpoint. This is being migrated to + * /socketconfig/.json (see ./routes/socketconfig.js) */ function handleSocketConfig(req, res) { if (/\.json$/.test(req.path)) { @@ -243,6 +244,7 @@ module.exports = { app.get("/r/:channel", handleChannel); app.get("/", handleIndex); app.get("/sioconfig(.json)?", handleSocketConfig); + require("./routes/socketconfig")(app); app.get("/useragreement", handleUserAgreement); app.get("/contact", handleContactPage); require("./auth").init(app); diff --git a/templates/channel.jade b/templates/channel.jade index 6b9a4c3b..6c8a2c6a 100644 --- a/templates/channel.jade +++ b/templates/channel.jade @@ -239,7 +239,6 @@ html(lang="en") mixin footer() script(src=sioSource) script(src="/js/data.js") - script(src="/sioconfig") script(src="/js/util.js") script(src="/js/player.js") script(src="/js/paginator.js") diff --git a/www/js/callbacks.js b/www/js/callbacks.js index 84efc394..4e409c59 100644 --- a/www/js/callbacks.js +++ b/www/js/callbacks.js @@ -1096,27 +1096,39 @@ setupCallbacks = function() { }); })(key); } -} +}; -try { +(function () { if (typeof io === "undefined") { makeAlert("Uh oh!", "It appears the connection to " + IO_URL + " " + "has failed. If this error persists, a firewall or " + "antivirus is likely blocking the connection, or the " + "server is down.", "alert-danger") .appendTo($("#announcements")); - throw false; + Callbacks.disconnect(); + return; } - var opts = { transports: ["websocket", "polling"] }; - if (IO_URL === IO_URLS["ipv4-ssl"] || IO_URL === IO_URLS["ipv6-ssl"]) { - opts.secure = true; - socket = io(IO_URL, { secure: true }); - } - socket = io(IO_URL, opts); - setupCallbacks(); -} catch (e) { - if (e) { - Callbacks.disconnect(); - } -} + $.getJSON("/socketconfig/" + CHANNEL.name + ".json") + .done(function (socketConfig) { + console.log(socketConfig); + if (socketConfig.error) { + makeAlert("Socket.io configuration returned error: " + + socketConfig.error, "alert-danger") + .appendTo($("#announcements")); + return; + } + + var opts = { + transports: ["websocket", "polling"], + secure: socketConfig.secure + }; + + socket = io(socketConfig.url, opts); + setupCallbacks(); + }).fail(function () { + makeAlert("Failed to retrieve socket.io configuration", "alert-danger") + .appendTo($("#announcements")); + Callbacks.disconnect(); + }); +})(); From 7b5476874d72e4e756b03fdd1431400ed0024fbe Mon Sep 17 00:00:00 2001 From: calzoneman Date: Wed, 21 Oct 2015 20:56:09 -0700 Subject: [PATCH 2/5] Minor function change --- src/io/cluster/nullclusterclient.js | 8 ++++++-- src/web/routes/socketconfig.js | 7 ++----- www/js/callbacks.js | 1 - 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/io/cluster/nullclusterclient.js b/src/io/cluster/nullclusterclient.js index b320e0d0..b5a83655 100644 --- a/src/io/cluster/nullclusterclient.js +++ b/src/io/cluster/nullclusterclient.js @@ -5,7 +5,11 @@ export default class NullClusterClient { this.ioConfig = ioConfig; } - getSocketURL(channel) { - return Promise.resolve(this.ioConfig.getSocketURL()); + getSocketConfig(channel) { + const url = this.ioConfig.getSocketURL(); + return Promise.resolve({ + url: url, + secure: /^(https|wss)/.test(url) + }); } } diff --git a/src/web/routes/socketconfig.js b/src/web/routes/socketconfig.js index e45437f3..501cb670 100644 --- a/src/web/routes/socketconfig.js +++ b/src/web/routes/socketconfig.js @@ -14,11 +14,8 @@ export default function initialize(app) { }); } - clusterClient.getSocketURL(req.params.channel).then(url => { - res.json({ - url, - secure: /^(https|wss)/.test(url) - }); + clusterClient.getSocketConfig(req.params.channel).then(config => { + res.json(config); }); }); } diff --git a/www/js/callbacks.js b/www/js/callbacks.js index 4e409c59..5c9d1a5a 100644 --- a/www/js/callbacks.js +++ b/www/js/callbacks.js @@ -1111,7 +1111,6 @@ setupCallbacks = function() { $.getJSON("/socketconfig/" + CHANNEL.name + ".json") .done(function (socketConfig) { - console.log(socketConfig); if (socketConfig.error) { makeAlert("Socket.io configuration returned error: " + socketConfig.error, "alert-danger") From 21c3a1b3cdd5a42dc47d2b618f758c01a97d86c3 Mon Sep 17 00:00:00 2001 From: calzoneman Date: Sun, 25 Oct 2015 17:20:39 -0700 Subject: [PATCH 3/5] API changes, add documentation --- NEWS.md | 19 ++-------- docs/socketconfig.md | 57 +++++++++++++++++++++++++++++ src/configuration/ioconfig.js | 40 ++++++++++++++++---- src/io/cluster/nullclusterclient.js | 5 +-- src/web/routes/socketconfig.js | 8 +++- www/js/callbacks.js | 27 ++++++++++++-- 6 files changed, 124 insertions(+), 32 deletions(-) create mode 100644 docs/socketconfig.md diff --git a/NEWS.md b/NEWS.md index 11cc4792..2783268e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,25 +1,12 @@ -2015-10-19 +2015-10-25 ========== In order to support future clustering support, the legacy `/sioconfig` endpoint is being deprecated. Instead, you should make a request to -`/socketconfig/.json`. The response will look similar to +`/socketconfig/.json`. See [the +documentation](docs/socketconfig.md) for more information. these: -```json -{"url":"https://some-website.com:8443","secure":true} -{"error":"Channel \"!@#$\" does not exist."} -``` - -The `url` key specifies the socket.io URL to connect to, and the `secure` -key indicates whether the connection is secured with TLS. If an `error` key -is present, something went wrong and the value will contain an error -message. - -For now, only one URL is returned, however in the future this may be -extended by adding an `alt` key specifying an array of acceptable URLs to -connect to. - 2015-10-04 ========== diff --git a/docs/socketconfig.md b/docs/socketconfig.md new file mode 100644 index 00000000..e9dfe467 --- /dev/null +++ b/docs/socketconfig.md @@ -0,0 +1,57 @@ +Socket.IO Client Configuration +============================== + +As of 2015-10-25, the legacy `/sioconfig` JavaScript for retrieving connection +information is being deprecated in favor of a new API. The purpose of this +change is to allow partitioning channels across multiple servers in order to +bettle handle increasing traffic. + +To get the socket.io configuration for the server hosting a particular channel, +make a `GET` request to `/socketconfig/.json`. The response will +be a JSON object containing a list of acceptable servers to connect to, or an +error message. + +Examples: + +``` +GET /socketconfig/test.json +200 OK + +{ + "servers": [ + { + "url": "https://localhost:8443", + "secure": true + }, + { + "url": "http://localhost:1337", + "secure": false + }, + { + "url": "https://local6:8443", + "secure": true, + "ipv6": true + }, + { + "url": "http://local6:1337", + "secure": false, + "ipv6": true + } + ] +} + +GET /socketconfig/$invalid$.json +404 Not Found + +{ + "error": "Channel \"$invalid$\" does not exist." +} +``` + +Each entry in the `servers` array has `"secure":true` if the connection is +secured with TLS, otherwise it it is false. An entry with `"ipv6":true` +indicates that the server is listening on the IPv6 protocol. + +You can pick any URL to connect socket.io to in order to join the specified +channel. I recommend picking one with `"secure":true`, only choosing an +insecure connection if implementing a TLS connection is infeasible. diff --git a/src/configuration/ioconfig.js b/src/configuration/ioconfig.js index 9fa96a0b..edb3093b 100644 --- a/src/configuration/ioconfig.js +++ b/src/configuration/ioconfig.js @@ -3,21 +3,45 @@ export default class IOConfiguration { this.config = config; } - getSocketURL() { - return this.config.urls[0]; + getSocketEndpoints() { + return this.config.endpoints.slice(); } } IOConfiguration.fromOldConfig = function (oldConfig) { const config = { - urls: [] + endpoints: [] }; - ['ipv4-ssl', 'ipv4-nossl', 'ipv6-ssl', 'ipv6-nossl'].forEach(key => { - if (oldConfig.get('io.' + key)) { - config.urls.push(oldConfig.get('io.' + key)); - } - }); + if (oldConfig.get('io.ipv4-ssl')) { + config.endpoints.push({ + url: oldConfig.get('io.ipv4-ssl'), + secure: true + }); + } + + if (oldConfig.get('io.ipv4-nossl')) { + config.endpoints.push({ + url: oldConfig.get('io.ipv4-nossl'), + secure: false + }); + } + + if (oldConfig.get('io.ipv6-ssl')) { + config.endpoints.push({ + url: oldConfig.get('io.ipv4-ssl'), + secure: true, + ipv6: true + }); + } + + if (oldConfig.get('io.ipv6-nossl')) { + config.endpoints.push({ + url: oldConfig.get('io.ipv4-nossl'), + secure: false, + ipv6: true + }); + } return new IOConfiguration(config); }; diff --git a/src/io/cluster/nullclusterclient.js b/src/io/cluster/nullclusterclient.js index b5a83655..89284355 100644 --- a/src/io/cluster/nullclusterclient.js +++ b/src/io/cluster/nullclusterclient.js @@ -6,10 +6,9 @@ export default class NullClusterClient { } getSocketConfig(channel) { - const url = this.ioConfig.getSocketURL(); + const servers = this.ioConfig.getSocketEndpoints(); return Promise.resolve({ - url: url, - secure: /^(https|wss)/.test(url) + servers: servers }); } } diff --git a/src/web/routes/socketconfig.js b/src/web/routes/socketconfig.js index 501cb670..836a6916 100644 --- a/src/web/routes/socketconfig.js +++ b/src/web/routes/socketconfig.js @@ -2,6 +2,7 @@ import IOConfiguration from '../../configuration/ioconfig'; import NullClusterClient from '../../io/cluster/nullclusterclient'; import Config from '../../config'; import CyTubeUtil from '../../utilities'; +import Logger from '../../logger'; export default function initialize(app) { const ioConfig = IOConfiguration.fromOldConfig(Config); @@ -9,13 +10,18 @@ export default function initialize(app) { app.get('/socketconfig/:channel.json', (req, res) => { if (!req.params.channel || !CyTubeUtil.isValidChannelName(req.params.channel)) { - return res.status(400).json({ + return res.status(404).json({ error: `Channel "${req.params.channel}" does not exist.` }); } clusterClient.getSocketConfig(req.params.channel).then(config => { res.json(config); + }).catch(err => { + Logger.errlog.log(err.stack); + return res.status(500).json({ + error: err.message + }); }); }); } diff --git a/www/js/callbacks.js b/www/js/callbacks.js index 5c9d1a5a..3fbf409f 100644 --- a/www/js/callbacks.js +++ b/www/js/callbacks.js @@ -1112,21 +1112,40 @@ setupCallbacks = function() { $.getJSON("/socketconfig/" + CHANNEL.name + ".json") .done(function (socketConfig) { if (socketConfig.error) { - makeAlert("Socket.io configuration returned error: " + + makeAlert("Error", "Socket.io configuration returned error: " + socketConfig.error, "alert-danger") .appendTo($("#announcements")); return; } + var chosenServer = null; + socketConfig.servers.forEach(function (server) { + if (chosenServer === null) { + chosenServer = server; + } else if (server.secure && !chosenServer.secure) { + chosenServer = server; + } else if (!server.ipv6Only && chosenServer.ipv6Only) { + chosenServer = server; + } + }); + + if (chosenServer === null) { + makeAlert("Error", + "Socket.io configuration was unable to find a suitable server", + "alert-danger") + .appendTo($("#announcements")); + } + var opts = { transports: ["websocket", "polling"], - secure: socketConfig.secure + secure: chosenServer.secure }; - socket = io(socketConfig.url, opts); + socket = io(chosenServer.url, opts); setupCallbacks(); }).fail(function () { - makeAlert("Failed to retrieve socket.io configuration", "alert-danger") + makeAlert("Error", "Failed to retrieve socket.io configuration", + "alert-danger") .appendTo($("#announcements")); Callbacks.disconnect(); }); From f5de173feb8d669daf7712920c63a5a3c684ad38 Mon Sep 17 00:00:00 2001 From: calzoneman Date: Sun, 25 Oct 2015 17:21:15 -0700 Subject: [PATCH 4/5] Remove extra word from NEWS.md --- NEWS.md | 1 - 1 file changed, 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 2783268e..4ad698a4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,7 +5,6 @@ In order to support future clustering support, the legacy `/sioconfig` endpoint is being deprecated. Instead, you should make a request to `/socketconfig/.json`. See [the documentation](docs/socketconfig.md) for more information. -these: 2015-10-04 ========== From 1bdea3381798cf3b8574aa5fb58261e6baad2ec3 Mon Sep 17 00:00:00 2001 From: calzoneman Date: Sun, 25 Oct 2015 17:22:50 -0700 Subject: [PATCH 5/5] Correct typo --- docs/socketconfig.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/socketconfig.md b/docs/socketconfig.md index e9dfe467..0c57a5c7 100644 --- a/docs/socketconfig.md +++ b/docs/socketconfig.md @@ -4,7 +4,7 @@ Socket.IO Client Configuration As of 2015-10-25, the legacy `/sioconfig` JavaScript for retrieving connection information is being deprecated in favor of a new API. The purpose of this change is to allow partitioning channels across multiple servers in order to -bettle handle increasing traffic. +better handle increasing traffic. To get the socket.io configuration for the server hosting a particular channel, make a `GET` request to `/socketconfig/.json`. The response will