Last active
December 30, 2015 12:19
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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