diff --git a/node_modules/c9/urls.js b/node_modules/c9/urls.js new file mode 100644 index 00000000..d3dabbfa --- /dev/null +++ b/node_modules/c9/urls.js @@ -0,0 +1,78 @@ +/** + * Base URL utilities for the Ajax.org Cloud IDE + * + * Access via require("c9/urls"). + * + * @copyright 2015, Ajax.org B.V. + */ + +var plugin = module.exports = main; +var assert = require("assert"); + +plugin.consumes = ["error.logger"]; +plugin.provides = ["urls"]; + +var errorLogger = mockErrorLogger; + +function main(options, imports, register) { + errorLogger = imports["error.logger"]; + + register(null, { + urls: plugin + }); +} + +/** + * Get a desired base URL, given some context. + * + * @param req The current request object or URL + * @param {String} sourceUrlPattern The source URL pattern, e.g. https://ide.$DOMAIN + * @param {String} targetBaseUrlPattern The target URL pattern, e.g. https://$DOMAIN + */ +plugin.getBaseUrl = function(req, sourceBaseUrlPattern, targetBaseUrlPattern) { + var sourceHost = req.host || req; + var sourceHostMatcher = sourceBaseUrlPattern + .replace(/^https?:\/\//, "") + .replace(/\/.*/, "") + .replace("$DOMAIN", "([^/]+)"); + var hostMatch = sourceHost.match(sourceHostMatcher); + if (!hostMatch) { + errorLogger.log(new Error("Could not construct URL: request host " + + sourceHost + " should match " + sourceBaseUrlPattern)); + return replaceDomain(targetBaseUrlPattern, "c9.io"); + } + + return replaceDomain(targetBaseUrlPattern, hostMatch[1]); +}; + +plugin.replaceDomains = function(settings, domains) { + domains = Array.isArray(domains) ? domains : domains.split(","); + var primaryDomain = domains[0]; + settings.domains = domains; + settings.primaryDomain = replaceDomain(settings.primaryDomain || "$DOMAIN", primaryDomain); + settings.primaryBaseUrl = replaceDomain(settings.primaryBaseUrl || "https://$DOMAIN", primaryDomain); + for (var s in settings) { + if (!settings[s]) + continue; + if (settings[s].baseUrl) + settings[s].baseUrl = replaceDomain(settings[s].baseUrl, primaryDomain); + if (settings[s].primaryBaseUrl) + settings[s].primaryBaseUrl = replaceDomain(settings[s].primaryBaseUrl, primaryDomain); + if (settings[s].baseUrls) { + assert(settings[s].baseUrls.length === 1); + settings[s].baseUrls = domains.map(function(d) { + return replaceDomain(settings[s].baseUrls[0], d); + }); + } + } +}; + +function replaceDomain(url, domain) { + return url.replace("$DOMAIN", domain); +} + +function mockErrorLogger() {} +mockErrorLogger.log = function(err) { + console.error(err.stack); +}; +plugin.mockErrorLogger = mockErrorLogger; diff --git a/node_modules/c9/urls_test.js b/node_modules/c9/urls_test.js new file mode 100644 index 00000000..ed7dde66 --- /dev/null +++ b/node_modules/c9/urls_test.js @@ -0,0 +1,123 @@ +/*global describe it beforeEach afterEach*/ +"use strict"; +"use server"; +"use mocha"; + +require("c9/inline-mocha")(module); +if (typeof define === "undefined") { + require("amd-loader"); + require("../../test/setup_paths"); +} + +var assert = require("assert"); +var urls = require("./urls"); +var url = require("url"); +var sinon = require("sinon"); + +describe("urls", function() { + + this.timeout(15000); + + it("can do basic domain substitution in settings", function() { + var settings = { + domains: ["c9.io"], + preview: { + baseUrl: "https://preview.$DOMAIN" + }, + ide: { + baseUrlPattern: "https://ide.$DOMAIN" + } + }; + urls.replaceDomains(settings, "cloud9beta.com"); + assert.equal(settings.primaryDomain, "cloud9beta.com"); + assert.equal(settings.preview.baseUrl, "https://preview.cloud9beta.com"); + assert.equal(settings.ide.baseUrlPattern, "https://ide.$DOMAIN"); + }); + + it("can do basic domain substitution in settings a list of domains", function() { + var settings = { + domains: ["c9.io"], + preview: { + baseUrl: "https://preview.$DOMAIN" + }, + ide: { + baseUrlPattern: "https://ide.$DOMAIN" + } + }; + urls.replaceDomains(settings, "cloud9beta.com,cs50.me"); + assert.equal(settings.primaryDomain, "cloud9beta.com"); + assert.deepEqual(settings.domains, ["cloud9beta.com", "cs50.me"]); + assert.equal(settings.preview.baseUrl, "https://preview.cloud9beta.com"); + assert.equal(settings.ide.baseUrlPattern, "https://ide.$DOMAIN"); + }); + + it("behaves like an architect plugin", function(next) { + urls({}, { "error.logger": urls.mockErrorLogger }, function(err, result) { + assert(!err, err); + assert(result.urls.getBaseUrl); + next(); + }); + }); + + it("can get the base url for a request", function() { + var mockRequest = { + host: "preview.c9.io" + }; + var baseUrl = urls.getBaseUrl(mockRequest, "https://preview.$DOMAIN", "https://ide.$DOMAIN"); + assert.equal(baseUrl, "https://ide.c9.io"); + }); + + it("can get the base url for a request with root domain source", function() { + var mockRequest = { + host: "c9.io" + }; + var baseUrl = urls.getBaseUrl(mockRequest, "https://$DOMAIN", "https://ide.$DOMAIN"); + assert.equal(baseUrl, "https://ide.c9.io"); + }); + + it("can get the base url for a request with root domain target", function() { + var mockRequest = { + host: "preview.cloud9beta.com" + }; + var baseUrl = urls.getBaseUrl(mockRequest, "https://preview.$DOMAIN", "https://$DOMAIN"); + assert.equal(baseUrl, "https://cloud9beta.com"); + }); + + it("gracefully copes with source domain mismatch", function() { + var mockRequest = { + host: "preview.cloud9beta.com" + }; + urls.mockErrorLogger.log = sinon.spy(); + var baseUrl = urls.getBaseUrl(mockRequest, "https://ide.$DOMAIN", "https://$DOMAIN"); + assert.equal(baseUrl, "https://c9.io"); + assert(urls.mockErrorLogger.log.called); + }); + + it("can get the base url in dogfooding mode", function() { + var mockRequest = { + host: "newclient-lennartcl.c9.io" + }; + var baseUrl = urls.getBaseUrl(mockRequest, "https://$DOMAIN", "https://ide.$DOMAIN"); + assert.equal(baseUrl, "https://ide.newclient-lennartcl.c9.io"); + }); + + it("can get the base url in dogfooding mode (2)", function() { + var mockRequest = { + host: "preview.newclient-lennartcl.c9.io" + }; + var baseUrl = urls.getBaseUrl(mockRequest, "https://preview.$DOMAIN", "https://ide.$DOMAIN"); + assert.equal(baseUrl, "https://ide.newclient-lennartcl.c9.io"); + }); + + it("even works with URL objects", function() { + var input = url.parse("https://preview.newclient-lennartcl.c9.io"); + var baseUrl = urls.getBaseUrl(input, "https://preview.$DOMAIN", "https://ide.$DOMAIN"); + assert.equal(baseUrl, "https://ide.newclient-lennartcl.c9.io"); + }); + + it("even works with strings", function() { + var input = "https://preview.newclient-lennartcl.c9.io"; + var baseUrl = urls.getBaseUrl(input, "https://preview.$DOMAIN", "https://ide.$DOMAIN"); + assert.equal(baseUrl, "https://ide.newclient-lennartcl.c9.io"); + }); +}); \ No newline at end of file diff --git a/server.js b/server.js index 52a7b2e8..6cb21bb1 100755 --- a/server.js +++ b/server.js @@ -11,7 +11,7 @@ var architect = require("architect"); var optimist = require("optimist"); var async = require("async"); var os = require("os"); -var urls = require("./plugins/c9.core/urls"); +var urls = require("c9/urls"); require("c9/setup_paths.js"); if (process.version.match(/^v0/) && parseFloat(process.version.substr(3)) < 10) {