NodeJS - 如何在不缓冲的情况下流请求主体

时间:2013-07-31 01:58:39

标签: node.js http stream pipe epipe

在下面的代码中我无法弄清楚为什么req.pipe(res)不起作用,但是也没有抛出错误。预感告诉我这是由于nodejs的异步行为,但这是一个非常简单的情况,没有回调。

我错过了什么?

http.createServer(function (req, res) {

  res.writeHead(200, { 'Content-Type': 'text/plain' });

  res.write('Echo service: \nUrl:  ' + req.url);
  res.write('\nHeaders:\n' + JSON.stringify(req.headers, true, 2));

  res.write('\nBody:\n'); 

  req.pipe(res); // does not work

  res.end();

}).listen(8000);

这是卷曲:

➜  ldap-auth-gateway git:(master) ✗ curl -v -X POST --data "test.payload" --header "Cookie:  token=12345678" --header "Content-Type:text/plain" localhost:9002 

这是调试输出(请参阅上传主体):

  About to connect() to localhost port 9002 (#0)
  Trying 127.0.0.1...
    connected
    Connected to localhost (127.0.0.1) port 9002 (#0)
  POST / HTTP/1.1
  User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8x zlib/1.2.5
  Host: localhost:9002
  Accept: */*
  Cookie:  token=12345678
  Content-Type:text/plain
  Content-Length: 243360
  Expect: 100-continue

  HTTP/1.1 100 Continue
  HTTP/1.1 200 OK
  Content-Type: text/plain
  Date: Sun, 04 Aug 2013 17:12:39 GMT
  Connection: keep-alive
  Transfer-Encoding: chunked

服务响应而不回应请求正文:

Echo service: 
Url:  /
Headers:
{
  "user-agent": "curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8x zlib/1.2.5",
  "host": "localhost:9002",
  "accept": "*/*",
  "cookie": "token=12345678",
  "content-type": "text/plain",
  "content-length": "243360",
  "expect": "100-continue"
}

...最后的卷曲调试是

Body:
 Connection #0 to host localhost left intact
 Closing connection #0

此外,当我使用大型请求主体进行压力测试时,我收到EPIPE错误。我怎么能避免这个?

- 编辑:通过反复试验我确实得到了这个,它仍然指向一个时间问题。虽然它仍然很奇怪,因为超时导致返回有效负载,但超时持续时间没有意义。换句话说,无论是将超时设置为5秒还是500秒,都会将有效负载正确传送回请求并终止连接。

这是编辑:

http.createServer(function (req, res) {

    try {
      res.writeHead(200, { 'Content-Type': 'text/plain' });
      res.write('Echo service: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
      res.write('\nBody:"\n');
      req.pipe(res);
    } catch(ex) {
      console.log(ex);
      // how to change response code to error here?  since headers have already been written?
    } finally {
      setTimeout((function() {
        res.end();
      }), 500000);
    }

}).listen(TARGET_SERVER.port);

2 个答案:

答案 0 :(得分:6)

管道要求res。 Req是可读流,响应是可写流。它应该工作

   http.createServer(function (req, res) {

       res.writeHead(200, { 'Content-Type': 'text/plain' });    
       res.write('Echo service: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));

       // pipe request body directly into the response body
       req.pipe(res);       

   }).listen(9002);

答案 1 :(得分:6)

首先,看起来您的卷曲已关闭,发布数据的文件名前面应该是@ as shown here。你只需要发布文件名。

除此之外,Chandu在说res.end()的问题是这里的问题是正确的。

由于IO在节点中是异步的,因此当您发出.pipe命令时,控件会立即返回到当前上下文,而管道在后台运行。当您下次致电res.end()时,您关闭了广告资源preventing any more data to be written

此处的解决方案是让.pipe结束流本身which is the default

我认为时间发挥作用是因为在不同的机器和不同的数据大小上,异步IO理论上可以在可写流上的结束事件被完全处理之前完成(小数据集的快速IO)。

我建议this blog post提供更多背景信息。