diff --git a/lib/async.js b/lib/async.js
index 676ed9427..43f693f79 100644
--- a/lib/async.js
+++ b/lib/async.js
@@ -71,16 +71,7 @@ module.exports = {
"files:watch",
bs.options.get("files"),
bs.pluginManager.pluginOptions
- ),
- mode: (function () {
- if (bs.options.get("server")) {
- return "server";
- }
- if (bs.options.get("proxy")) {
- return "proxy";
- }
- return "snippet";
- })()
+ )
}
});
},
diff --git a/lib/browser-sync.js b/lib/browser-sync.js
index d060b35cd..d5be3c892 100644
--- a/lib/browser-sync.js
+++ b/lib/browser-sync.js
@@ -73,7 +73,9 @@ BrowserSync.prototype.init = function (options, cb) {
* Some options are not compatible and will cause us to
* end the process.
*/
- utils.verifyConfig(options, bs.cb);
+ if (!utils.verifyConfig(options, bs.cb)) {
+ return;
+ }
/**
* Save a reference to the original options
diff --git a/lib/config.js b/lib/config.js
index 8326c2978..efc125454 100644
--- a/lib/config.js
+++ b/lib/config.js
@@ -19,5 +19,9 @@ module.exports = {
template: "/cli-template.js",
client: {
shims: "/client/client-shims.js"
+ },
+ errors: {
+ "server+proxy": "Invalid config. You cannot specify both server & proxy options.",
+ "proxy+https": "Invalid config. You set https: true, but your proxy target doesn't reflect this."
}
};
diff --git a/lib/connect-utils.js b/lib/connect-utils.js
index e4a0cb953..4b5321cde 100644
--- a/lib/connect-utils.js
+++ b/lib/connect-utils.js
@@ -13,7 +13,11 @@ var connectUtils = {
scriptTags: function (options) {
function getPath(relative, port) {
- return options.get("scheme") + "://HOST:" + port + relative;
+ if (options.get("mode") === "snippet") {
+ return options.get("scheme") + "://HOST:" + port + relative;
+ } else {
+ return "//HOST:" + port + relative;
+ }
}
var template = fs.readFileSync(config.templates.scriptTag, "utf-8");
@@ -23,14 +27,15 @@ var connectUtils = {
var override = false;
if (_.isFunction(options.get("scriptPath"))) {
- script = options.get("scriptPath")(scriptPath, options.get("port"), options);
+ var args = getScriptArgs(options, scriptPath);
+ script = options.get("scriptPath").apply(null, args);
override = true;
} else {
script = getPath(scriptPath, options.get("port"));
}
if (!override && (options.get("server") || options.get("proxy"))) {
- script = getPath(scriptPath, options.get("port"));
+ script = scriptPath;
}
template = template
@@ -64,7 +69,7 @@ var connectUtils = {
},
getConnectionUrl: function (options) {
- var protocol = options.get("scheme") + "://";
+ var protocol = "";
var namespace = options.getIn(["socket", "namespace"]);
var string = "'%protocol%' + location.%host% + '%ns%'";
var socketOpts = connectUtils.resolveSocketOptions(options);
@@ -73,6 +78,10 @@ var connectUtils = {
return "'%s'".replace("%s", namespace(defaultConfig.socket.namespace, options));
}
+ if (options.get("mode") === "snippet") {
+ protocol = options.get("scheme") + "://";
+ }
+
return string
.replace("%protocol%", protocol)
.replace("%host%", socketOpts.host)
@@ -124,4 +133,17 @@ var connectUtils = {
}
};
+/**
+ * @param options
+ * @returns {*[]}
+ */
+function getScriptArgs (options, scriptPath) {
+ var abspath = options.get("scheme") + "://HOST:" + options.get("port") + scriptPath;
+ return [
+ scriptPath,
+ options.get("port"),
+ options.set("absolute", abspath)
+ ];
+}
+
module.exports = connectUtils;
diff --git a/lib/options.js b/lib/options.js
index f20e259e3..fe44e8523 100644
--- a/lib/options.js
+++ b/lib/options.js
@@ -12,6 +12,7 @@ module.exports.update = function (options) {
return options.withMutations(function (item) {
+ setMode(item);
setScheme(item);
setStartPath(item);
setServerOpts(item);
@@ -33,6 +34,22 @@ module.exports.update = function (options) {
});
};
+/**
+ * Set the running mode
+ * @param item
+ */
+function setMode (item) {
+ item.set("mode", (function () {
+ if (item.get("server")) {
+ return "server";
+ }
+ if (item.get("proxy")) {
+ return "proxy";
+ }
+ return "snippet";
+ })());
+}
+
/**
* @param item
*/
diff --git a/lib/templates/script-tags.tmpl b/lib/templates/script-tags.tmpl
index a0f033596..ec1bdfd8f 100644
--- a/lib/templates/script-tags.tmpl
+++ b/lib/templates/script-tags.tmpl
@@ -1,3 +1,3 @@
\ No newline at end of file
diff --git a/lib/utils.js b/lib/utils.js
index 010acc8b7..04e93e0c8 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -292,9 +292,7 @@ var utils = {
*/
getConfigErrors: function (options) {
- var messages = {
- "server+proxy": "Invalid config. You cannot specify both server & proxy options."
- };
+ var messages = require("./config").errors;
var errors = [];
@@ -302,6 +300,12 @@ var utils = {
errors.push(messages["server+proxy"]);
}
+ if (options.get("https") && options.get("proxy")) {
+ if (options.getIn(["proxy", "url", "protocol"]) !== "https:") {
+ errors.push([messages["proxy+https"], options.getIn(["proxy", "target"])].join(" "));
+ }
+ }
+
return errors;
},
/**
@@ -311,7 +315,8 @@ var utils = {
verifyConfig: function (options, cb) {
var errors = utils.getConfigErrors(options);
if (errors.length) {
- return utils.fail(true, errors.join("\n"), cb);
+ utils.fail(true, errors.join("\n"), cb);
+ return false;
}
return true;
},
diff --git a/test/protractor/_run.js b/test/protractor/_run.js
index ec8700873..48bf9c3f7 100644
--- a/test/protractor/_run.js
+++ b/test/protractor/_run.js
@@ -8,8 +8,10 @@ module.exports = function (logger) {
return function (config, configFile, cb) {
browserSync.reset();
var instance = browserSync(config.bsConfig, function (err, bs) {
- var url = bs.getOptionIn(["urls", "local"]);
+ var url = bs.getOptionIn(["urls", "local"]);
+ var uiurl = bs.getOptionIn(["urls", "ui"]);
process.env["BS_BASE"] = url;
+ process.env["BS_UI"] = uiurl;
process.env["BS_SCRIPT_PATH"] = bs.getOptionIn(["scriptPaths", "path"]);
logger.info("Testing BrowserSync at %s", url);
@@ -28,6 +30,7 @@ module.exports = function (logger) {
function runTests (config, configFile, bs, cb) {
var out = "";
exec("protractor " + configFile, function (err, stdout) {
+ console.log(stdout);
if (err) {
doCallback({
code: 1,
diff --git a/test/protractor/tests.multi.js b/test/protractor/tests.multi.js
index cf48e2f4a..b0d161f97 100644
--- a/test/protractor/tests.multi.js
+++ b/test/protractor/tests.multi.js
@@ -1,10 +1,23 @@
"use strict";
-var fs = require("fs");
-var path = require("path");
var connect = require("connect");
-var serveStatic = require("serve-static");
-var http = require("http");
+var utils = require("../../lib/server/utils");
+var Immutable = require("immutable");
+
+function getApp (bs, options) {
+
+ var html = "
BrowsersyncBS";
+ var app = connect();
+
+ app.use("/", function (req, res) {
+ res.setHeader("content-type", "text/html");
+ res.end(html.replace("BS", bs.getOption("snippet")));
+ });
+
+ var appserver = utils.getServer(app, options);
+
+ return appserver;
+}
module.exports = {
"Proxy Test Laravel App": {
@@ -28,6 +41,13 @@ module.exports = {
logLevel: "silent"
}
},
+ "Secure Proxy": {
+ bsConfig: {
+ proxy: "https://grenade.static:8890",
+ open: false,
+ logLevel: "silent"
+ }
+ },
"Server": {
bsConfig: {
server: "./test/fixtures",
@@ -48,23 +68,35 @@ module.exports = {
logLevel: "silent"
},
before: function (bs, cb) {
- var filepath = path.resolve(__dirname + "/../fixtures/index.html");
- var file = fs.readFileSync(filepath, "utf-8");
- var modded = file.replace("", bs.getOption("snippet"));
- var app = connect();
- app.use(serveStatic(path.resolve(__dirname + "/../fixtures")));
- var server = http.createServer(app).listen();
- var port = server.address().port;
- var url = "http://localhost:" + port;
- process.env["BS_BASE"] = url;
- fs.writeFileSync(filepath, modded);
+ var app = getApp(bs, Immutable.Map({scheme: "http"}));
+ app.server.listen();
+ process.env["BS_BASE"] = "http://localhost:" + app.server.address().port;
+ cb();
+ }
+ },
+ "Secure Snippet on Insecure Website": {
+ bsConfig: {
+ logLevel: "silent",
+ https: true
+ },
+ before: function (bs, cb) {
+
+ var app = getApp(bs, Immutable.Map({scheme: "http"}));
+ app.server.listen();
+ process.env["BS_BASE"] = "http://localhost:" + app.server.address().port;
cb();
+ }
+ },
+ "Secure Snippet on Secure Website": {
+ bsConfig: {
+ logLevel: "silent",
+ https: true
},
- after: function (bs, cb) {
- var filepath = path.resolve(__dirname + "/../fixtures/index.html");
- var file = fs.readFileSync(filepath, "utf-8");
- var modded = file.replace(bs.getOption("snippet"), "");
- fs.writeFileSync(filepath, modded);
+ before: function (bs, cb) {
+
+ var app = getApp(bs, Immutable.Map({scheme: "https"}));
+ app.server.listen();
+ process.env["BS_BASE"] = "https://localhost:" + app.server.address().port;
cb();
}
}
diff --git a/test/protractor/tests/snippet.injection.js b/test/protractor/tests/snippet.injection.js
index ec1de8f7d..a79eb2a94 100644
--- a/test/protractor/tests/snippet.injection.js
+++ b/test/protractor/tests/snippet.injection.js
@@ -4,13 +4,13 @@
*
*/
// abstract writing screen shot to a file
-var fs = require("fs");
-var slugify = require("slugify");
-function writeScreenShot(data, filename) {
- var stream = fs.createWriteStream(filename);
- stream.write(new Buffer(data, "base64"));
- stream.end();
-}
+//var fs = require("fs");
+//var slugify = require("slugify");
+//function writeScreenShot(data, filename) {
+// var stream = fs.createWriteStream(filename);
+// stream.write(new Buffer(data, "base64"));
+// stream.end();
+//}
/**
*
@@ -19,15 +19,17 @@ describe("Section Navigation", function () {
beforeEach(function () {
browser.ignoreSynchronization = true;
browser.get("/");
- // within a test:
- browser.takeScreenshot().then(function (png) {
- writeScreenShot(png, "screenshots/ss_" + slugify(process.env["BS_TEST_NAME"]) + ".png");
- });
});
it("should contain the BS script element", function () {
expect(element(by.id("__bs_script__")).isPresent()).toBeTruthy();
});
it("should contain the BS NOTIFY ELEMENT", function () {
+ //browser.pause();
expect(element(by.id("__bs_notify__")).isPresent()).toBeTruthy();
});
+ it("should launch UI", function () {
+ browser.ignoreSynchronization = false;
+ browser.get(process.env["BS_UI"]);
+ expect(element.all(by.css("[bs-heading]")).get(0).isPresent()).toBeTruthy();
+ });
});
diff --git a/test/protractor/utils.js b/test/protractor/utils.js
new file mode 100644
index 000000000..b1d219385
--- /dev/null
+++ b/test/protractor/utils.js
@@ -0,0 +1,23 @@
+"use strict";
+
+var utils = require("../../lib/server/utils");
+var connect = require("connect");
+
+module.exports = {
+
+ getApp: function getApp (options) {
+
+ var html = "BrowsersyncBS";
+ var app = connect();
+
+ app.use("/", function (req, res) {
+ res.setHeader("content-type", "text/html");
+ res.end(html);
+ });
+
+ var appserver = utils.getServer(app, options);
+ appserver.html = html;
+
+ return appserver;
+ }
+};
diff --git a/test/specs/e2e/proxy/e2e.proxy.secure.js b/test/specs/e2e/proxy/e2e.proxy.secure.js
new file mode 100644
index 000000000..9779ebfdb
--- /dev/null
+++ b/test/specs/e2e/proxy/e2e.proxy.secure.js
@@ -0,0 +1,93 @@
+"use strict";
+
+var browserSync = require("../../../../index");
+var testUtils = require("../../../protractor/utils");
+var Immutable = require("immutable");
+var request = require("supertest");
+var assert = require("chai").assert;
+
+process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0;
+
+describe("E2E TLS proxy test", function () {
+
+ this.timeout(15000);
+
+ var bs, app;
+
+ before(function (done) {
+
+ browserSync.reset();
+
+ app = testUtils.getApp(Immutable.Map({scheme: "https"}));
+
+ app.server.listen();
+
+ var config = {
+ proxy: "https://localhost:" + app.server.address().port,
+ open: false,
+ logLevel: "silent"
+ };
+
+ bs = browserSync.init(config, done).instance;
+ });
+
+ after(function () {
+ bs.cleanup();
+ app.server.close();
+ });
+
+ it("Set's a HTTPS url", function () {
+ var local = bs.options.getIn(["urls", "local"]);
+ assert.equal("https://localhost:" + bs.options.get("port"), local);
+ });
+
+ it("proxies over https and injects snippet", function (done) {
+
+ assert.isString(bs.options.get("snippet"));
+
+ var expected = app.html.replace("BS", bs.options.get("snippet") + "BS");
+
+ request(bs.options.getIn(["urls", "local"]))
+ .get("/index.html")
+ .set("accept", "text/html")
+ .expect(200, expected, done);
+ });
+});
+
+describe("E2E TLS proxy Options test", function () {
+
+ this.timeout(15000);
+
+ var app;
+
+ before(function () {
+
+ browserSync.reset();
+
+ app = testUtils.getApp(Immutable.Map({scheme: "https"}));
+
+ app.server.listen();
+ });
+
+ after(function () {
+ app.server.close();
+ });
+
+ it("Exits if https specified in options, but not in target", function (done) {
+ var utils = require("../../../../lib/utils");
+ var errors = require("../../../../lib/config").errors;
+ var sinon = require("sinon");
+ var config = {
+ proxy: "http://localhost:" + app.server.address().port,
+ https: true,
+ open: false,
+ logLevel: "silent"
+ };
+ var stub = sinon.stub(utils, "fail");
+ browserSync.init(config);
+ sinon.assert.called(stub);
+ assert.include(stub.getCall(0).args[1], errors["proxy+https"]);
+ utils.fail.restore();
+ done();
+ });
+});
diff --git a/test/specs/utils/utils.connect.js b/test/specs/utils/utils.connect.js
index 5f6f4952b..6f80b58d0 100644
--- a/test/specs/utils/utils.connect.js
+++ b/test/specs/utils/utils.connect.js
@@ -15,7 +15,7 @@ describe("Connection utils", function () {
});
it("should return a connection url with http", function () {
var actual = utils.getConnectionUrl(options);
- var expected = "'http://' + location.host + '/browser-sync'";
+ var expected = "'' + location.host + '/browser-sync'";
assert.equal(actual, expected);
});
it("should return a connection url for snippet mode", function () {
@@ -35,7 +35,7 @@ describe("Connection utils", function () {
mode: "proxy"
});
var actual = utils.socketConnector(options);
- assert.include(actual, "'http://' + location.host + '/browser-sync'");
+ assert.include(actual, "'' + location.host + '/browser-sync'");
});
it("should return a connection url for server mode", function () {
var options = merge({
@@ -44,7 +44,7 @@ describe("Connection utils", function () {
mode: "server"
});
var actual = utils.socketConnector(options);
- assert.include(actual, "'http://' + location.host + '/browser-sync'");
+ assert.include(actual, "'' + location.host + '/browser-sync'");
});
it("should return a connection url for server mode, https", function () {
var options = merge({
@@ -53,7 +53,7 @@ describe("Connection utils", function () {
mode: "server"
});
var actual = utils.socketConnector(options);
- assert.include(actual, "'https://' + location.host + '/browser-sync'");
+ assert.include(actual, "'' + location.host + '/browser-sync'");
});
it("should return a connection url for snippet mode", function () {
var options = merge({
@@ -64,7 +64,7 @@ describe("Connection utils", function () {
var actual = utils.socketConnector(options);
assert.include(actual, "'http://' + location.hostname + ':4002/browser-sync'");
});
- it("should return a connection url for snippet mode", function () {
+ it("should return a connection url for secure snippet mode", function () {
var options = merge({
port: 4002,
scheme: "https",