From ac5d258378cd798ad4d7f20341da15038252043c Mon Sep 17 00:00:00 2001 From: Simon Howe Date: Thu, 18 Feb 2016 14:17:51 +0100 Subject: [PATCH] Fixes scope working under a path - Adds proxy server for easy testing of scope under a path. --- client/app/scripts/components/app.js | 8 ++--- client/app/scripts/contrast-main.js | 2 +- client/app/scripts/main.js | 2 +- client/app/scripts/utils/router-utils.js | 5 +-- client/app/scripts/utils/web-api-utils.js | 10 ++++++ client/package.json | 1 + client/server.js | 38 +++++++++++++++++++++++ 7 files changed, 58 insertions(+), 8 deletions(-) diff --git a/client/app/scripts/components/app.js b/client/app/scripts/components/app.js index ea85045b5..7b57180e6 100644 --- a/client/app/scripts/components/app.js +++ b/client/app/scripts/components/app.js @@ -6,7 +6,7 @@ import Sidebar from './sidebar.js'; import Status from './status.js'; import Topologies from './topologies.js'; import TopologyOptions from './topology-options.js'; -import { getApiDetails, getTopologies } from '../utils/web-api-utils'; +import { getApiDetails, getTopologies, basePathSlash } from '../utils/web-api-utils'; import { hitEsc } from '../actions/app-actions'; import Details from './details'; import Nodes from './nodes'; @@ -51,7 +51,7 @@ export default class App extends React.Component { AppStore.addListener(this.onChange); window.addEventListener('keyup', this.onKeyPress); - getRouter(this.props.base).start({hashbang: true}); + getRouter().start({hashbang: true}); if (!AppStore.isRouteSet()) { // dont request topologies when already done via router getTopologies(AppStore.getActiveTopologyOptions()); @@ -75,9 +75,9 @@ export default class App extends React.Component { // width of details panel blocking a view const detailsWidth = showingDetails ? 450 : 0; const topMargin = 100; - const contrastMode = this.props.base.indexOf('contrast') > -1; + const contrastMode = window.location.pathname.indexOf('contrast') > -1; // link url to switch contrast with current UI state - const otherContrastModeUrl = contrastMode ? '/' : '/contrast.html'; + const otherContrastModeUrl = contrastMode ? basePathSlash(window.location.pathname) : 'contrast.html'; const otherContrastModeTitle = contrastMode ? 'Switch to normal contrast' : 'Switch to high contrast'; return ( diff --git a/client/app/scripts/contrast-main.js b/client/app/scripts/contrast-main.js index 29b3d735d..18de464be 100644 --- a/client/app/scripts/contrast-main.js +++ b/client/app/scripts/contrast-main.js @@ -6,4 +6,4 @@ import ReactDOM from 'react-dom'; import App from './components/app.js'; -ReactDOM.render(, document.getElementById('app')); +ReactDOM.render(, document.getElementById('app')); diff --git a/client/app/scripts/main.js b/client/app/scripts/main.js index 10cbcc26a..129e1d6ab 100644 --- a/client/app/scripts/main.js +++ b/client/app/scripts/main.js @@ -6,4 +6,4 @@ import ReactDOM from 'react-dom'; import App from './components/app.js'; -ReactDOM.render(, document.getElementById('app')); +ReactDOM.render(, document.getElementById('app')); diff --git a/client/app/scripts/utils/router-utils.js b/client/app/scripts/utils/router-utils.js index f55a9af2d..b2a4e3208 100644 --- a/client/app/scripts/utils/router-utils.js +++ b/client/app/scripts/utils/router-utils.js @@ -38,7 +38,8 @@ page('/state/:state', function(ctx) { route(state); }); -export function getRouter(base) { - page.base(base); +export function getRouter() { + // strip any trailing '/'s. + page.base(window.location.pathname.replace(/\/$/, '')); return page; } diff --git a/client/app/scripts/utils/web-api-utils.js b/client/app/scripts/utils/web-api-utils.js index aab8056b4..f9a2f977d 100644 --- a/client/app/scripts/utils/web-api-utils.js +++ b/client/app/scripts/utils/web-api-utils.js @@ -45,6 +45,16 @@ export function basePath(urlPath) { return parts.join('/').replace(/\/$/, ''); } +export function basePathSlash(urlPath) { + // + // "/scope/terminal.html" -> "/scope/" + // "/scope/" -> "/scope/" + // "/scope" -> "/scope/" + // "/" -> "/" + // + return basePath(urlPath) + '/'; +} + const wsProto = location.protocol === 'https:' ? 'wss' : 'ws'; const wsUrl = wsProto + '://' + location.host + basePath(location.pathname); diff --git a/client/package.json b/client/package.json index bdb70ca42..824502f96 100644 --- a/client/package.json +++ b/client/package.json @@ -42,6 +42,7 @@ "eslint-plugin-jasmine": "1.6.0", "eslint-plugin-react": "3.8.0", "file-loader": "0.8.4", + "http-proxy-rules": "^1.0.1", "jest-cli": "~0.7.1", "json-loader": "0.5.3", "less": "~2.5.1", diff --git a/client/server.js b/client/server.js index 6efba3ec9..62fb9d7c8 100644 --- a/client/server.js +++ b/client/server.js @@ -1,5 +1,7 @@ var express = require('express'); +var http = require('http'); var httpProxy = require('http-proxy'); +var HttpProxyRules = require('http-proxy-rules'); var url = require('url'); var app = express(); @@ -90,3 +92,39 @@ var server = app.listen(port, function () { }); server.on('upgrade', proxy.ws.bind(proxy)); + + +/************************************************************* + * + * path proxy server + * + *************************************************************/ + +var proxyRules = new HttpProxyRules({ + rules: { + '/scoped/': 'http://localhost:' + port + } +}); + +var pathProxy = httpProxy.createProxy({ws: true}); +var pathProxyPort = port + 1; +const proxyPathServer = http.createServer(function(req, res) { + var target = proxyRules.match(req); + if (!target) { + res.writeHead(500, {'Content-Type': 'text/plain'}); + res.end('No rules matched! Check out /scoped/'); + return; + } + return pathProxy.web(req, res, {target: target}); +}).listen(pathProxyPort, function() { + var pathProxyHost = proxyPathServer.address().address; + console.log('Scope Proxy Path UI listening at http://%s:%s/scoped/', + pathProxyHost, pathProxyPort); +}); + +proxyPathServer.on('upgrade', function(req, socket, head) { + var target = proxyRules.match(req); + if (target) { + return pathProxy.ws(req, socket, head, {target: target}); + } +});