From 37375a7f1090ceb8ade01ae5d1256672a6530270 Mon Sep 17 00:00:00 2001 From: Konrad Dzwinel Date: Sat, 26 Dec 2015 03:08:20 +0100 Subject: [PATCH] Replace http-proxy with http-mitm-proxy --- lib/captured-connection.js | 40 +++++++++++++------- lib/rdp-message-formatter.js | 4 +- lib/traffic-interceptor.js | 73 ++++++++++++++---------------------- package.json | 4 +- 4 files changed, 58 insertions(+), 63 deletions(-) diff --git a/lib/captured-connection.js b/lib/captured-connection.js index 189cab7..f70875b 100644 --- a/lib/captured-connection.js +++ b/lib/captured-connection.js @@ -1,8 +1,10 @@ 'use strict'; +const url = require('url'); const zlib = require('zlib'); const isTextOrBinary = require('istextorbinary'); const getTime = require('./init-time'); + const generateConnectionId = (() => { let id = 1; return () => id++; @@ -27,25 +29,27 @@ class CapturedConnection { this._responseBody = null; } - setRequest(proxyReq, req) { + setRequest(req, isSSL) { this._request = { - url: req.url, - method: proxyReq.method, + url: fullUrl(req, isSSL), + method: req.method, headers: req.headers }; } - setResponse(proxyRes, res) { + setResponse(res) { this._response = { url: res.url, - statusCode: proxyRes.statusCode, - statusMessage: proxyRes.statusMessage, - headers: adaptHeaders(proxyRes.headers), - rawHeaders: recreateRawResponseHeaders(proxyRes), - connectionId: res.connection.remotePort + statusCode: res.statusCode, + statusMessage: res.statusMessage, + headers: adaptHeaders(res.headers), + rawHeaders: recreateRawResponseHeaders(res), + connectionId: res.connection.localPort, + remoteAddress: res.connection.remoteAddress, + remotePort: res.connection.remotePort }; - this._resourceType = getResourceType(proxyRes.headers['content-type']); + this._resourceType = getResourceType(res.headers['content-type']); } getId() { @@ -129,6 +133,14 @@ class CapturedConnection { } } +function fullUrl(req, isSSL) { + let parsedUrl = url.parse(req.url); + parsedUrl.protocol = isSSL ? 'https' : 'http'; + parsedUrl.host = req.headers.host; + + return url.format(parsedUrl); +} + //TODO should be async function unpackBody(buffer, encoding) { if (encoding === 'gzip') { @@ -194,14 +206,14 @@ function getResourceType(contentType) { } //TODO try getting real raw response headers instead of this thing -function recreateRawResponseHeaders(proxyRes) { +function recreateRawResponseHeaders(res) { let headerString = ''; - for (let i = 0, l = proxyRes.rawHeaders.length; i < l; i += 2) { - headerString += proxyRes.rawHeaders[i] + ': ' + proxyRes.rawHeaders[i + 1] + '\n'; + for (let i = 0, l = res.rawHeaders.length; i < l; i += 2) { + headerString += res.rawHeaders[i] + ': ' + res.rawHeaders[i + 1] + '\n'; } - return `HTTP/${proxyRes.httpVersion} ${proxyRes.statusCode} ${proxyRes.statusMessage} + return `HTTP/${res.httpVersion} ${res.statusCode} ${res.statusMessage} ${headerString}`; } diff --git a/lib/rdp-message-formatter.js b/lib/rdp-message-formatter.js index d81e95b..0f5fd5b 100644 --- a/lib/rdp-message-formatter.js +++ b/lib/rdp-message-formatter.js @@ -67,8 +67,8 @@ function responseReceived(connection) { receiveHeadersEnd: (connection.getTiming().responseReceived - connection.getTiming().start) * 1000 }, requestHeaders: connection.getRequest().headers, - remoteIPAddress: '127.0.0.1', - remotePort: 80 + remoteIPAddress: response.remoteAddress, + remotePort: response.remotePort } }; } diff --git a/lib/traffic-interceptor.js b/lib/traffic-interceptor.js index 2516857..2a24307 100644 --- a/lib/traffic-interceptor.js +++ b/lib/traffic-interceptor.js @@ -1,10 +1,7 @@ 'use strict'; -const http = require('http'); -const httpProxy = require('http-proxy'); -const url = require('url'); +const MITMProxy = require('http-mitm-proxy'); const EventEmitter = require('events'); -const Agent = require('agentkeepalive'); const CapturedConnection = require('./captured-connection'); const getTime = require('./init-time'); @@ -18,20 +15,13 @@ class TrafficInterceptor extends EventEmitter { //TODO we never remove requests from here - see #8 this._connections = new Map(); - let agent = new Agent({ - keepAlive: true - }); + let proxy = new MITMProxy(); - this._proxy = httpProxy.createProxyServer({agent}) - .on('proxyReq', handleIncomingRequest.bind(this)) - .on('proxyRes', handleIncomingResponse.bind(this)) - .on('error', handleProxyError.bind(this)); + proxy.onRequest(handleIncomingRequest.bind(this)); + proxy.onResponse(handleIncomingResponse.bind(this)); + proxy.onError(handleProxyError.bind(this)); - this._server = http.createServer(handleHTTPRequest.bind(this)) - .on('error', (error) => { - this.emit('error', error); - }) - .listen(port); + proxy.listen({port: port}); } getConnection(id) { @@ -39,32 +29,27 @@ class TrafficInterceptor extends EventEmitter { } } -function handleHTTPRequest(req, res) { - this.emit('log', 'Request recorded: ' + req.method + ' ' + req.url); - - let target = url.parse(req.url); - - this._proxy.web(req, res, { - target: target.protocol + '//' + target.host - }); -} - -function handleIncomingRequest(proxyReq, req, res, options) { +function handleIncomingRequest(ctx, callback) { let connection = new CapturedConnection(); - connection.setRequest(proxyReq, req); + connection.setRequest(ctx.clientToProxyRequest, ctx.isSSL); this._connections.set(connection.getId(), connection); - req.log = { + ctx.log = { id: connection.getId() }; this.emit('request', connection); + + return callback(); } -function handleIncomingResponse(proxyRes, req, res) { - this.emit('log', 'Response arrived: ' + req.method + ' ' + req.url + ' ' + res.statusCode); +function handleIncomingResponse(ctx, callback) { + let request = ctx.clientToProxyRequest; + let response = ctx.serverToProxyResponse; + + this.emit('log', 'Response arrived: ' + request.method + ' ' + request.url + ' ' + response.statusCode); - let connectionId = req.log && req.log.id; + let connectionId = ctx.log && ctx.log.id; let connection = null; if (connectionId) { @@ -76,27 +61,23 @@ function handleIncomingResponse(proxyRes, req, res) { return; } - connection.setResponse(proxyRes, res); + connection.setResponse(response); connection.registerResponseReceived(); this.emit('response-received', connection); - let _end = res.end; - let _write = res.write; let trafficInterceptor = this; - res.write = function(chunk) { - _write.apply(res, arguments); + ctx.onResponseData(function(ctx, chunk, callback) { connection.registerDataReceived(chunk); trafficInterceptor.emit('response-data', connection, { - time: getTime, + time: getTime(), encodedLength: chunk.length }); - }; - - res.end = function() { - _end.apply(res, arguments); + return callback(null, chunk); + }); + ctx.onResponseEnd(function(ctx, callback) { connection.registerResponseFinished(); //after whole body was received and unpacked we can push its size to the front-end @@ -107,10 +88,14 @@ function handleIncomingResponse(proxyRes, req, res) { }); trafficInterceptor.emit('response-finished', connection); - }; + + return callback(); + }); + + return callback(); } -function handleProxyError(error) { +function handleProxyError(ctx, error) { this.emit('error', 'Proxy error:', error); } diff --git a/package.json b/package.json index dca56fa..b659281 100644 --- a/package.json +++ b/package.json @@ -29,9 +29,7 @@ "dependencies": { "agentkeepalive": "^2.0.3", "chalk": "^1.1.1", - "http-mitm-proxy": "^0.2.0", - "http-proxy": "^1.12.0", - "istextorbinary": "^1.0.2" + "http-mitm-proxy": "^0.2.0" }, "devDependencies": { "electron-packager": "^5.1.1",