Skip to content

Commit 40017b4

Browse files
committed
fix(web-sockets): Use separate server for web sockets in proxy mode - fixes #625
2 parents f35d034 + 1c9d929 commit 40017b4

11 files changed

+146
-15
lines changed

lib/async-tasks.js

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ module.exports = [
55
step: "Finding an empty port",
66
fn: async.getEmptyPort
77
},
8+
{
9+
step: "Getting an extra port for Proxy",
10+
fn: async.getExtraPortForProxy
11+
},
812
{
913
step: "Checking online status",
1014
fn: async.getOnlineStatus

lib/async.js

+41
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,47 @@ module.exports = {
2727
});
2828
});
2929
},
30+
/**
31+
* If the running mode is proxy, we'll use a separate port
32+
* for the Browsersync web-socket server. This is to eliminate any issues
33+
* with trying to proxy web sockets through to the users server.
34+
* @param bs
35+
* @param done
36+
*/
37+
getExtraPortForProxy: function (bs, done) {
38+
/**
39+
* An extra port is not needed in snippet/server mode
40+
*/
41+
if (bs.options.get("mode") !== "proxy") {
42+
return done();
43+
}
44+
45+
/**
46+
* Use 1 higher than server port by default...
47+
*/
48+
var socketPort = bs.options.get("port") + 1;
49+
50+
/**
51+
* Or use the user-defined socket.port option instead
52+
*/
53+
if (bs.options.hasIn(["socket", "port"])) {
54+
socketPort = bs.options.getIn(["socket", "port"]);
55+
}
56+
57+
utils.getPort(socketPort, null, function (err, port) {
58+
if (err) {
59+
return done(err);
60+
}
61+
done(null, {
62+
optionsIn: [
63+
{
64+
path: ["socket", "port"],
65+
value: port
66+
}
67+
]
68+
});
69+
});
70+
},
3071
/**
3172
* Some features require an internet connection.
3273
* If the user did not provide either `true` or `false`

lib/cli/command.reload.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,13 @@ module.exports = function (opts) {
1717
}
1818
var proto = require("../http-protocol");
1919
var scheme = flags.url.match(/^https/) ? "https" : "http";
20+
var args = {method: "reload"};
2021

21-
var url = proto.getUrl({method: "reload", args: flags.files}, flags.url);
22+
if (flags.files) {
23+
args.args = flags.files;
24+
}
25+
26+
var url = proto.getUrl(args, flags.url);
2227

2328
require(scheme).get(url, function (res) {
2429
res.on("data", function () {

lib/connect-utils.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -91,15 +91,20 @@ var connectUtils = {
9191
var withHostnamePort = "'{protocol}' + location.hostname + ':{port}{ns}'";
9292
var withHost = "'{protocol}' + location.host + '{ns}'";
9393
var withDomain = "'{domain}{ns}'";
94+
var port = options.get("port");
9495

9596
// default use-case is server/proxy
9697
var string = withHost;
9798

98-
if (options.get("mode") === "snippet") {
99+
if (options.get("mode") !== "server") {
99100
protocol = options.get("scheme") + "://";
100101
string = withHostnamePort;
101102
}
102103

104+
if (options.get("mode") === "proxy") {
105+
port = options.getIn(["socket", "port"]);
106+
}
107+
103108
if (socketOpts.domain) {
104109
string = withDomain;
105110
if (typeof socketOpts.domain === "function") {
@@ -109,7 +114,7 @@ var connectUtils = {
109114

110115
return string
111116
.replace("{protocol}", protocol)
112-
.replace("{port}", options.get("port"))
117+
.replace("{port}", port)
113118
.replace("{domain}", socketOpts.domain)
114119
.replace("{ns}", namespace);
115120
},

lib/default-config.js

+4
Original file line numberDiff line numberDiff line change
@@ -378,11 +378,15 @@ module.exports = {
378378
* @param {String} [clientPath="/browser-sync"]
379379
* @param {String|Function} [namespace="/browser-sync"]
380380
* @param {String|Function} [domain=undefined]
381+
* @param {String|Function} [port=undefined]
381382
* @param {Object} [clients.heartbeatTimeout=5000]
382383
* @since 1.6.2
383384
* @type Object
384385
*/
385386
socket: {
387+
socketIoOptions: {
388+
log: false,
389+
},
386390
path: "/browser-sync/socket.io",
387391
clientPath: "/browser-sync",
388392
namespace: "/browser-sync",

lib/server/proxy-server.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ module.exports = function createProxyServer (bs, scripts) {
2929
bs.proxy.app.use(bs.options.getIn(["scriptPaths", "path"]), scripts);
3030

3131
/**
32-
* How best to handle websockets going forward?
32+
* Also proxy upgrades for Web Socket support
3333
*/
34-
//proxy.server.on("upgrade", app.handleUpgrade);
34+
proxy.server.on("upgrade", bs.proxy.app.handleUpgrade);
3535

3636
return proxy;
3737
};

lib/sockets.js

+18-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use strict";
22

33
var socket = require("socket.io");
4+
var utils = require("./server/utils");
45
var Steward = require("emitter-steward");
56

67
/**
@@ -18,10 +19,24 @@ module.exports.plugin = function (server, clientEvents, bs) {
1819
*/
1920
module.exports.init = function (server, clientEvents, bs) {
2021

21-
var emitter = bs.events;
22-
var socketConfig = bs.options.get("socket").toJS();
22+
var emitter = bs.events;
2323

24-
var io = socket.listen(server, {log: false, path: socketConfig.path});
24+
var socketConfig = bs.options
25+
.get("socket")
26+
.toJS();
27+
28+
if (bs.options.get("mode") === "proxy") {
29+
server = utils.getServer(null, bs.options).server;
30+
server.listen(bs.options.getIn(["socket", "port"]));
31+
bs.registerCleanupTask(function () {
32+
server.close();
33+
});
34+
}
35+
36+
var socketIoConfig = socketConfig.socketIoOptions;
37+
socketIoConfig.path = socketConfig.path;
38+
39+
var io = socket(server, socketIoConfig);
2540

2641
// Override default namespace.
2742
io.sockets = io.of(socketConfig.namespace);

lib/utils.js

+3
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ var utils = {
119119
max = ports.get("max") || null;
120120
}
121121

122+
utils.getPort(port, max, cb);
123+
},
124+
getPort: function (port, max, cb) {
122125
portScanner.findAPortNotInUse(port, max, {
123126
host: "localhost",
124127
timeout: 1000

package.json

+5-5
Original file line numberDiff line numberDiff line change
@@ -34,23 +34,23 @@
3434
"async-each-series": "^0.1.1",
3535
"browser-sync-client": "^2.2.1",
3636
"browser-sync-ui": "^0.5.12",
37-
"chokidar": "^1.0.4",
37+
"chokidar": "^1.0.5",
3838
"connect": "^3.4.0",
3939
"dev-ip": "^1.0.1",
4040
"easy-extender": "^2.3.1",
4141
"eazy-logger": "^2.1.2",
4242
"emitter-steward": "^0.0.1",
4343
"foxy": "^11.1.1",
4444
"immutable": "^3.7.4",
45-
"localtunnel": "^1.6.0",
45+
"localtunnel": "^1.7.0",
4646
"lodash": "^3.9.3",
4747
"longest": "^1.0.1",
4848
"meow": "^3.3.0",
4949
"opn": "^2.0.1",
5050
"pad-left": "^1.0.2",
5151
"portscanner": "^1.0.0",
52-
"query-string": "^2.3.0",
53-
"resp-modifier": "^4.0.3",
52+
"query-string": "^2.4.0",
53+
"resp-modifier": "^4.0.4",
5454
"serve-index": "^1.7.0",
5555
"serve-static": "^1.10.0",
5656
"socket.io": "^1.3.6",
@@ -75,7 +75,7 @@
7575
"istanbul-coveralls": "^1.0.3",
7676
"mocha": "^2.2.5",
7777
"q": "^1.4.1",
78-
"request": "^2.59.0",
78+
"request": "^2.60.0",
7979
"sinon": "^1.15.4",
8080
"slugify": "^0.1.1",
8181
"socket.io-client": "^1.3.6",

test/specs/e2e/e2e.options.port.js

+51
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,55 @@ describe("E2E `port` option", function () {
2727
done();
2828
});
2929
});
30+
it("sets extra port option for socket in proxy mode", function (done) {
31+
browserSync.reset();
32+
33+
var stub = sinon.stub(utils, "getPort");
34+
35+
stub.onCall(0).yields(null, 3000);
36+
stub.onCall(1).yields(null, 3001);
37+
38+
var config = {
39+
logLevel: "silent",
40+
proxy: "localhost",
41+
online: false,
42+
open: false
43+
};
44+
45+
browserSync(config, function (err, bs) {
46+
bs.cleanup();
47+
assert.equal(bs.options.get("port"), 3000);
48+
assert.equal(stub.getCall(1).args[0], 3001);
49+
assert.equal(bs.options.getIn(["socket", "port"]), 3001);
50+
utils.getPort.restore();
51+
done();
52+
});
53+
});
54+
it("uses user-given extra port option for socket in proxy mode", function (done) {
55+
browserSync.reset();
56+
57+
var stub = sinon.stub(utils, "getPort");
58+
59+
stub.onCall(0).yields(null, 3000);
60+
stub.onCall(1).yields(null, 8001);
61+
62+
var config = {
63+
logLevel: "silent",
64+
proxy: "localhost",
65+
socket: {
66+
port: 8001
67+
},
68+
online: false,
69+
open: false
70+
};
71+
72+
browserSync(config, function (err, bs) {
73+
bs.cleanup();
74+
assert.equal(bs.options.get("port"), 3000);
75+
assert.equal(stub.getCall(1).args[0], 8001);
76+
assert.equal(bs.options.getIn(["socket", "port"]), 8001);
77+
utils.getPort.restore();
78+
done();
79+
});
80+
});
3081
});

test/specs/utils/utils.connect.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,13 @@ describe("Connection utils", function () {
3636
var options = merge({
3737
port: 3002,
3838
scheme: "http",
39-
mode: "proxy"
39+
mode: "proxy",
40+
socket: {
41+
port: 4000
42+
}
4043
});
4144
var actual = utils.socketConnector(options);
42-
assert.include(actual, "'' + location.host + '/browser-sync'");
45+
assert.include(actual, "'http://' + location.hostname + ':4000/browser-sync'");
4346
});
4447
it("should return a connection url for server mode", function () {
4548
var options = merge({

0 commit comments

Comments
 (0)