Node.js代理,带有自定义的Http(s)代理和Connect.js中间件。

6
我在Node中编写了一个代理服务器,需要能够通过tls隧道传输https请求,并且一切都正常。使用以下两个软件包非常容易设置:proxyhttps-proxy-agent。我的问题是,我正在尝试使用connect作为中间件层捕获HAR文件,但我遇到了以下错误:

_http_outgoing.js:357
throw new Error('Can\'t set headers after they are sent.');
^
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:357:11)
at ServerResponse.writeHead (_http_server.js:180:21)
at ClientRequest.<anonymous> (/.../node_modules/proxy/proxy.js:233:11)
at emitOne (events.js:96:13)
at ClientRequest.emit (events.js:188:7)
at HTTPParser.parserOnIncomingClient [as onIncoming] (_http_client.js:473:21)
at HTTPParser.parserOnHeadersComplete (_http_common.js:99:23)
at Socket.socketOnData (_http_client.js:362:20)
at emitOne (events.js:96:13)
at Socket.emit (events.js:188:7)

这个问题很简单,只有在我使用connect并通过我的本地浏览器代理时才会出现(这个代理实际上是与BrowserStackLocal一起使用的)。当我从除了本地机器浏览器以外的任何东西通过代理时,就好像它根本不知道中间件的存在。

所以基本上,我只需要让connect在这种情况下工作,我不确定是否需要暂停某些内容并恢复,或者其他什么...任何想法都将不胜感激。以下是基本代码:

const path = require('path');
const http = require('http');
const proxy = require('proxy');
const Agent = require('https-proxy-agent');
const connect = require('connect');
const har = require('./har');

const middleware = connect();

middleware.use(har({
  harOutputDir: path.resolve(process.cwd(), 'har/')
}));

const server = proxy(http.createServer(middleware));
server.agent = new Agent('http://localhost:8081');

server.listen(8081)

谢谢!

编辑:只是一个注意事项:har中间件根本不会修改头部。


./har中间件是否在响应对象上发送数据?如果是,则会设置一个标头,第二个setHeader将导致错误。 - Myonara
@Myonara 不是的- 它只是从响应中读取数据并“next()”,即使没有"./har"中间件,问题仍然存在。 - mike
在客户端,你能否识别出第二个头部响应发送的内容? - Myonara
@Myonara 我不确定你的意思... 看起来好像 connect 或代理服务器会在另一个执行任何操作之前发送标头。如果我必须猜测,我会说代理服务器先发送它的标头,因为它在使用 connect 时失败了。我想知道是否可以暂停响应并等待 connect 完成它的任务。我正在尝试这样做,而不必修改代理并提交 PR。谢谢,迈克。 - mike
1个回答

2

proxy已经有一段时间没有维护了。构建失败,最后提交的测试不通过。你提供的堆栈跟踪源代码来自这里的233行 - 这是一个有缺陷的库代码。

编写类似的代理很容易。以下代码演示如何创建一个。

const http = require('http');
const urlP = require('url');

const proxiedServer = 'http://localhost:8888';

// Proxy server
http.createServer((req, res) => {
  console.log(`Proxy: Got ${req.url}`);

  const _r = http.request(
    Object.assign(
      {},
      urlP.parse(proxiedServer),
      {
        method: req.method,
        path: req.url
      }
    ),
    _res => {
      res.writeHead(_res.statusCode, _res.headers);
      _res.pipe(res);
    }
  );

  req.pipe(_r);

}).listen(3124, () => {
  console.log("Listening on 3124");
});


// Regular server. Could be Express
http.createServer((req, res) => {
  console.log('Proxied server: ', req.url);
  let b = '';
  req.on('data', c => {
    b += c;
  });
  req.on('end', () => {
    console.log('Proxied server: ', b);
  });
  res.writeHead(200);
  res.end('ok');
}).listen(8888, () => {
  console.log('proxied server listening on 8888');
});

您使用自定义代理的代码将如下所示:

const urlP = require('url');
const path = require('path');
const http = require('http');
const connect = require('connect');
const har = require('./har');

const proxiedServer = 'http://localhost:8888';

// Proxy server
http.createServer((req, res) => {
  console.log(`Proxy: Got ${req.url}`);

  const _r = http.request(
    Object.assign(
      {},
      urlP.parse(proxiedServer),
      {
        method: req.method,
        path: req.url
      }
    ),
    _res => {
      res.writeHead(_res.statusCode, _res.headers);
      _res.pipe(res);
    }
  );

  req.pipe(_r);

}).listen(3124, () => {
  console.log("Listening on 3124");
});

const middleware = connect();
middleware.use(har({
  harOutputDir: path.resolve(process.cwd(), 'har/')
}));

middleware.use((req, res) => {
  console.log('Proxied server: ', req.url);
  let b = '';
  req.on('data', c => {
    b += c;
  });
  req.on('end', () => {
    console.log('Proxied server: ', b);
  });
  res.writeHead(200);
  res.end('ok');
});


http.createServer(middleware).listen(8888, () => {
  console.log('proxied server listening on 8888');
});

好的,谢谢你的帮助...我实际上开始写自己的代码,但在通过http隧道传输https时遇到了太多问题。我发现代理是解决这个问题的方法,而不是尝试为通过代理的任何内容创建证书。感谢你的帮助! - mike
答案中的代码片段有帮助吗? - user2065736
是的,我确实研究过它,并计划稍微整理一下并提交PR,但最终还是像你建议的那样开始编写自己的代码...由于我需要代理和必要时切换到TLS,所以这有点复杂,但希望能对某些人有所帮助。我基本上正在编写一个JavaScript解决方案来替换BrowserMobb Procy。感谢您的帮助! - mike

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接