Merge pull request #522 from calzoneman/sioconfig-migration

Migrate socket.io configuration to new API
This commit is contained in:
Calvin Montgomery 2015-10-25 17:25:00 -07:00
commit 535b1d5d3a
9 changed files with 202 additions and 18 deletions

View File

@ -1,3 +1,11 @@
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/<channel name>.json`. See [the
documentation](docs/socketconfig.md) for more information.
2015-10-04 2015-10-04
========== ==========

57
docs/socketconfig.md Normal file
View File

@ -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
better handle increasing traffic.
To get the socket.io configuration for the server hosting a particular channel,
make a `GET` request to `/socketconfig/<channel name>.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.

View File

@ -2,7 +2,7 @@
"author": "Calvin Montgomery", "author": "Calvin Montgomery",
"name": "CyTube", "name": "CyTube",
"description": "Online media synchronizer and chat", "description": "Online media synchronizer and chat",
"version": "3.11.2", "version": "3.12.0",
"repository": { "repository": {
"url": "http://github.com/calzoneman/sync" "url": "http://github.com/calzoneman/sync"
}, },

View File

@ -0,0 +1,47 @@
export default class IOConfiguration {
constructor(config) {
this.config = config;
}
getSocketEndpoints() {
return this.config.endpoints.slice();
}
}
IOConfiguration.fromOldConfig = function (oldConfig) {
const config = {
endpoints: []
};
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);
};

View File

@ -0,0 +1,14 @@
import Promise from 'bluebird';
export default class NullClusterClient {
constructor(ioConfig) {
this.ioConfig = ioConfig;
}
getSocketConfig(channel) {
const servers = this.ioConfig.getSocketEndpoints();
return Promise.resolve({
servers: servers
});
}
}

View File

@ -0,0 +1,27 @@
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);
const clusterClient = new NullClusterClient(ioConfig);
app.get('/socketconfig/:channel.json', (req, res) => {
if (!req.params.channel || !CyTubeUtil.isValidChannelName(req.params.channel)) {
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
});
});
});
}

View File

@ -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/<channel name>.json (see ./routes/socketconfig.js)
*/ */
function handleSocketConfig(req, res) { function handleSocketConfig(req, res) {
if (/\.json$/.test(req.path)) { if (/\.json$/.test(req.path)) {
@ -243,6 +244,7 @@ module.exports = {
app.get("/r/:channel", handleChannel); app.get("/r/:channel", handleChannel);
app.get("/", handleIndex); app.get("/", handleIndex);
app.get("/sioconfig(.json)?", handleSocketConfig); app.get("/sioconfig(.json)?", handleSocketConfig);
require("./routes/socketconfig")(app);
app.get("/useragreement", handleUserAgreement); app.get("/useragreement", handleUserAgreement);
app.get("/contact", handleContactPage); app.get("/contact", handleContactPage);
require("./auth").init(app); require("./auth").init(app);

View File

@ -239,7 +239,6 @@ html(lang="en")
mixin footer() mixin footer()
script(src=sioSource) script(src=sioSource)
script(src="/js/data.js") script(src="/js/data.js")
script(src="/sioconfig")
script(src="/js/util.js") script(src="/js/util.js")
script(src="/js/player.js") script(src="/js/player.js")
script(src="/js/paginator.js") script(src="/js/paginator.js")

View File

@ -1096,27 +1096,57 @@ setupCallbacks = function() {
}); });
})(key); })(key);
} }
} };
try { (function () {
if (typeof io === "undefined") { if (typeof io === "undefined") {
makeAlert("Uh oh!", "It appears the connection to <code>" + IO_URL + "</code> " + makeAlert("Uh oh!", "It appears the connection to <code>" + IO_URL + "</code> " +
"has failed. If this error persists, a firewall or " + "has failed. If this error persists, a firewall or " +
"antivirus is likely blocking the connection, or the " + "antivirus is likely blocking the connection, or the " +
"server is down.", "alert-danger") "server is down.", "alert-danger")
.appendTo($("#announcements")); .appendTo($("#announcements"));
throw false; Callbacks.disconnect();
return;
} }
var opts = { transports: ["websocket", "polling"] }; $.getJSON("/socketconfig/" + CHANNEL.name + ".json")
if (IO_URL === IO_URLS["ipv4-ssl"] || IO_URL === IO_URLS["ipv6-ssl"]) { .done(function (socketConfig) {
opts.secure = true; if (socketConfig.error) {
socket = io(IO_URL, { secure: true }); makeAlert("Error", "Socket.io configuration returned error: " +
} socketConfig.error, "alert-danger")
socket = io(IO_URL, opts); .appendTo($("#announcements"));
setupCallbacks(); return;
} catch (e) { }
if (e) {
Callbacks.disconnect(); 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: chosenServer.secure
};
socket = io(chosenServer.url, opts);
setupCallbacks();
}).fail(function () {
makeAlert("Error", "Failed to retrieve socket.io configuration",
"alert-danger")
.appendTo($("#announcements"));
Callbacks.disconnect();
});
})();