我正在尝试使用Node.js创建HTTP / S MitM转发代理。
我正在处理此项目的方法是重复使用@runk在NPM Proxy Cache project问题跟踪器上提出问题后./lib/proxy.js
{@ 1}}文件中找到的解决方案。
我的Proxy()
课程如下:
var request = require('request')
, https = require('https')
, http = require('http')
, net = require('net')
, url = require('url')
, os = require('os')
, fs = require('fs');
var SOCKET_PATH = os.tmpdir() + 'mitm.sock';
console.log('[SOCKET PATH] ' + SOCKET_PATH);
function Proxy (config) {
config = config || {};
if(fs.existsSync(SOCKET_PATH)) {
fs.unlinkSync(SOCKET_PATH);
}
var options = {
key: fs.readFileSync('./certs/dummy.key', 'utf8'),
cert: fs.readFileSync('./certs/dummy.crt', 'utf8')
};
// HTTPS Server
https.createServer(options, this.handler).listen(config.port + 1, this.hostname, function (e) {
if(e) {
console.log('[HTTPS] Server listen() error !');
throw e;
}
});
// HTTP Server
var server = http.createServer(this.handler);
server.listen(config.port, this.hostname, function (e) {
if(e) {
console.log('[HTTP] Server listen() error !');
throw e;
}
});
// Intercept CONNECT requests for HTTPS handshake
server.addListener('connect', this.httpsHandler);
}
Proxy.prototype.handler = function (req, res) {
var schema = !!req.client.pair ? 'https' : 'http'
, path = url.parse(req.url).path;
var dest = schema + '://' + req.headers['host'] + path;
console.log('(1) - [' + schema.toUpperCase() + '] ' + req.method + ' ' + req.url);
var params = {
rejectUnauthorized: false,
url: dest
};
if(req.method.toUpperCase() !== 'GET') {
return console.log('[HTTP] Request is not HTTP GET.');
}
var onResponse = function (e, response) {
if(e == null && response.statusCode === 200) {
return r.pipe(res);
}
var body = 'Status ' + response.statusCode + ' returned';
if(e) {
body = e.toString();
}
res.end(body);
};
var r = request(params);
r.on('response', onResponse.bind(null, null));
r.on('error', onResponse.bind(null));
};
Proxy.prototype.httpsHandler = function (request, socketRequest, bodyHead) {
var httpVersion = request['httpVersion']
, url = request['url'];
console.log('(2) - [HTTPS] ' + request['method'] + ' ' + request['url']);
var proxySocket = new net.Socket();
// ProxySocket event handlers
proxySocket.connect(SOCKET_PATH, function () {
proxySocket.write(bodyHead);
proxySocket.write('HTTP/' + httpVersion + ' 200 Connection established\r\n\r\n');
});
proxySocket.on('data', function (chunk) {
console.log('ProxySocket - "data"');
socketRequest.write(chunk);
});
proxySocket.on('end', function () {
console.log('ProxySocket - "end"');
socketRequest.end();
});
proxySocket.on('error', function (e) {
console.log('ProxySocket - "error"');
console.log(e);
console.log(e.stack);
socketRequest.write('HTTP/' + httpVersion + ' 500 Connection error\r\n\r\n');
socketRequest.end();
});
// SocketRequest event handlers
socketRequest.on('data', function (chunk) {
console.log('SocketRequest - "data"');
proxySocket.write(chunk);
});
socketRequest.on('end', function () {
console.log('SocketRequest - "end"');
proxySocket.end();
});
socketRequest.on('error', function (e) {
console.log('socketRequest - "error"');
console.log(e);
console.log(e.stack);
proxySocket.end();
});
};
module.exports = Proxy;
启动程序的Index.js
文件如下所示:
var Proxy = require('./lib/proxy');
var proxy = new Proxy({
hostname: '127.0.0.1',
port: 8000
});
这是我的目录/文件结构:
/my_project
/certs
dummy.crt // Copied from the NPM Proxy Cache project
dummy.csr // Copied from the NPM Proxy Cache project
dummy.key // Copied from the NPM Proxy Cache project
/lib
proxy.js
index.js
我正在通过将(Mac OSX Maverick
)HTTP和HTTPS代理设置为IP地址127.0.0.1
和端口8000
来测试我的程序。
浏览仅限HTTP的网站时,一切正常,但如果我浏览HTTPS网站,则会收到以下错误:
{[Error: connect ENOENT] code: 'ENOENT', errno: 'ENOENT', syscall: 'connect'}
Error: connect ENOENT
at errnoException (net.js:904:11)
at Object.afterConnect [as oncomplete] (net.js:895:19)
非常感谢你!
(如果要测试我的代码,NPM模块request
是运行代码所需的唯一依赖项。)
编辑:证书可以从这里下载:Node HTTP Proxy project。
答案 0 :(得分:2)
我是npm-proxy-cache
的作者。实际上我已经创建了另一个名为thin
https://www.npmjs.org/package/thin的项目,我希望将来npm代理缓存会利用它。尽管它仍然非常粗糙,但它可以使用,它可以满足您的需求。
E.g。
var Thin = require('thin')
var proxy = new Thin;
// `req` and `res` params are `http.ClientRequest` and `http.ServerResponse` accordingly
// be sure to check http://nodejs.org/api/http.html for more details
proxy.use(function(req, res, next) {
console.log('Proxying:', req.url);
next();
});
// you can add different layers of "middleware" similar to "connect",
// but with few exclusions
proxy.use(function(req, res, next) {
if (req.url === '/foobar')
return res.end('intercepted');
next();
});
proxy.listen(8081, 'localhost', function(err) {
// .. error handling code ..
});
var express = require('express'); // v3.4
var app = express();
app.use(express.urlencoded({limit: '10mb'}));
app.get('/test', function(req, res){
console.log(req.protocol, 'get req.query', req.query);
res.end('get: hello world');
});
app.post('/test', function(req, res) {
console.log(req.protocol, 'post req.query', req.query);
console.log(req.protocol, 'post req.body', req.body);
res.end('post: hello world');
});
app.listen(3000);
var fs = require('fs');
var https = require('https');
https.createServer({
key: fs.readFileSync('./cert/dummy.key'), // your mitm server keys
cert: fs.readFileSync('./cert/dummy.crt')
}, app).listen(3001);
您需要在两个终端会话中启动代理和服务器,然后
curl -d "foo=baz" -k -x https://localhost:8081 https://localhost:3001/test?foo=bar
curl -d "foo=baz" -x http://localhost:8081 http://localhost:3000/test?foo=bar
之后,您应该能够看到服务器的以下输出
https post req.query { foo: 'bar' }
https post req.body { foo: 'baz' }
http post req.query { foo: 'bar' }
http post req.body { foo: 'baz' }
拦截器的小例子
curl -d "foo=baz" -k -x https://localhost:8081 https://localhost:3001/foobar
它应该返回intercepted
希望有所帮助:)