neo-proxy
Version:
A man-in-the-middle (mitm) proxy that impersonates the target site and allows you to modify the target's http response before sending it back to the client.
122 lines (100 loc) • 3.57 kB
JavaScript
var http = require('http')
var url = require('url')
var connect = require('connect')
var httpProxy = require('http-proxy')
module.exports = neoProxy
function neoProxy(options) {
console.assert(options.port >= 0, "Please specify a port")
console.assert(options.target, "Please specify a target")
console.assert(options.needle, "Please pass in a needle")
console.assert(options.customHTML, "Please provide customHTML")
options.rewriteUrlsHost = options.rewriteUrlsHost || 'localhost'
//
// Rewrite URLs of HTML link elements with your local host IP
//
var rewriteUrlsScript = ''
if (options.rewriteUrlsHost) {
rewriteUrlsScript = "<script>"
rewriteUrlsScript += rewriteUrlsFunction + "; rewriteUrlsFunction('"
+ options.target + "', '" + options.rewriteUrlsHost + "', "
+ options.port + ");"
rewriteUrlsScript += "</script>"
}
//
// properly configure the proxy to pretend that the request came from the
// original host.
//
var proxy = httpProxy.createProxyServer()
var dest = url.parse(options.target)
var proxyOptions = {
target: dest.href
, hostRewrite: false // not super sure what this does.
}
proxy.on('proxyReq', function(proxyReq, req, res, options) {
proxyReq.setHeader('host', dest.host)
proxyReq.removeHeader('accept-encoding')
})
//
// Basic Connect App w/ harmon
//
var app = connect()
app.use(replacer)
app.use(function (req, res) {
proxy.web(req, res, proxyOptions)
})
app.listen(options.port)
console.log('http://localhost:%s -> %s, hrefs %s'
, options.port , options.target, options.rewriteUrlsHost)
// via http://ghub.io/harmon
function replacer(req, res, next) {
if (req.url.indexOf('.js') > -1 || req.url.indexOf('.css') > -1) {
return next()
}
var _write = res.write
var _end = res.end
var _writeHead = res.writeHead
// Assume response is binary by default
res.isHtml = false
res.writeHead = function (code, headers) {
var contentType = this.getHeader('content-type')
/* Sniff out the content-type header.
* If the response is HTML, we're safe to modify it.
*/
if ((typeof contentType != 'undefined') && (contentType.indexOf('text/html') == 0)) {
res.isHtml = true
// Strip off the content length since it will change.
res.removeHeader('Content-Length')
}
_writeHead.apply(res, arguments)
}
res.write = function (data, encoding) {
// Only run data through replacer if we have HTML, and needle is present
var chunk = ''
if (res.isHtml && (chunk = data.toString(encoding)) && chunk.indexOf(options.needle) > -1) {
chunk = chunk.replace(options.needle, options.customHTML + rewriteUrlsScript + options.needle)
console.log('%s: Found `%s` and prepended `%s`'
, options.target, options.needle, options.customHTML)
_write.call(res, new Buffer(chunk))
} else {
_write.apply(res, arguments)
}
}
res.end = function (data, encoding) {
_end.call(res)
}
next()
}
options.app = app
options.proxy = proxy
return options
}
function rewriteUrlsFunction (host, localHost, port) {
var links = document.getElementsByTagName('a');
for (var i = links.length - 1; i >= 0; i--) {
var link = links[i]
var href = link.getAttribute('href')
if (href && href.indexOf(host) > -1 ) {
link.setAttribute('href', href.replace(host, 'http://' + localHost + ':' + port) + '?host=' + host)
};
};
}