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(); });