“连接”上的ENOENT错误

时间:2014-06-01 13:47:56

标签: node.js http https proxy mitmproxy

我正在尝试使用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

1 个答案:

答案 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

希望有所帮助:)