Skip to content

Instantly share code, notes, and snippets.

@starstuck
Last active December 30, 2015 12:19
Show Gist options
  • Save starstuck/7828316 to your computer and use it in GitHub Desktop.
Save starstuck/7828316 to your computer and use it in GitHub Desktop.
Proxy setup for developing and debugging web applications. It creates node.js servercombining express + http-proxy to provide uncompressed JS and CSS over proxied live content. It works very well with source maps.
/**
* Web application debug proxy server.
*
* It is suited for hosting uncompressed sources of front-end assets and proxy dynamic
* requests to servers preparing data. I find similar setup also useful for investigating
* issues on production servers by going through the proxy, which use local unminified
* JS and CSS on top of production html pages.
*
* To run following example you will need to have http-proxy, express and send packages installed.
*
* @author Tomasz Szarstuk <szarsti@gmail.com>
* @license MIT
*/
var path = require('path'),
http = require('http'),
httpProxy = require('http-proxy'),
express = require('express'),
send = require('send'),
stream = require('stream'),
util = require('util'),
app = express(),
proxy = new httpProxy.HttpProxy({
changeOrigin: true, // required if target proxy is using name based virtual hosts
target: {
host: 'localhost',
port: 8080
// Use following when connecting to https server with self signed cert.
//https: true,
//rejectUnauthorized: false,
// I found that sometimes one may need to repeat hostname.
// I guest it must be due to name based virtual hosts.
//hostname: 'somename'
}
}),
server;
// Simple filtering stream to process files
// function will be called with chunk, encoding and callback arguments
function Filter(fn) {
this._fn = fn;
this._boundPush = this.push.bind(this);
stream.Transform.call(this, {
decodeStrings: true
});
}
util.inherits(Filter, stream.Transform);
Filter.prototype._transform = function(chunk, encoding, cb) {
this._fn(chunk, this._boundPush);
cb();
};
Filter.prototype.pipe = function(dest, options) {
this._res = dest;
return stream.Transform.prototype.pipe.call(this, dest, options);
};
['getHeader', 'setHeader'].forEach(function(name) {
Filter.prototype[name] = function() {
return this._res[name].apply(this, arguments);
};
});
// Setup express application routing
app
// Usually you will want to have paths with FE assets available.
// Try avoiding to complex path mapping, because it will make setting up
// source maps so much harder.
.use('/src', express.static(path.join(__dirname, 'src')))
.use('/css', express.static(path.join(__dirname, 'css')))
// If your app is expecting certain layout, it is easier to set up redirects
// to structure you have in your projects. This way all tools will report
// paths tat are easy to map to physical location
.get(/^\/lang\/(.*)$/, function(req, res) {
res.redirect('/src/lang/' + req.params[0]);
})
// If your app comes with static html, you can transform paths to match
// structure of your project. If you need to do similar filtering on
// html pages coming back from server, have a look at gist describing
// filtering proxy setup: https://gist.github.com/szarsti/7266806
.get('/index.html', function(req, res) {
var file = send(req, 'asset/modules/' + req.params.page),
brand = req.params.brand,
proxy = getProxy(brand),
filter = new Filter(function(chunk, forward) {
forward(
chunk.toString()
.replace(/(<link[^>]*href=")assets\/([^"]*\/less-master.css")/, '$1/asset/brands/' + brand + '/$2')
.replace(/(<script[^>]*src=")config(\/[^"]*")/, "$1/config$2")
.replace(/(<script[^>]*src=")[^/]*(\/(deplibs|libs|utils|core|bingo)\.js")/g, "$1/src/debug$2")
.replace(/(<script[^>]*src=")(libs\/globalize\/[^"]*")/g, "$1/src/$2")
);
});
file.pipe(filter).pipe(res);
})
// Example forward all data request to back-end server. You could
// not bind it to any path if you want all paths not handled by
// rules above to be forwarded to original server. The second setup
// may be useful if you want to play with local js on top of html
// from production servers.
.use('/data', function(req, res, next) {
// Hack to use original url instead of once stripped by express
req.oldUrl = req.url;
req.url = req.originalUrl;
console.log('Forwarding:', brand + req.url);
proxy.proxyRequest(req, res);
});
// Setup server
server = http.createServer(app)
.on('close', function() {
proxy.close();
})
.on('listening', function() {
console.log('Debug server started at http://localhost:' + server.address().port);
});
module.exports = server;
module.exports.app = app;
// If called from command line, start server immediately
if (require.main === module) {
server.listen(80);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment