nodejs / nodejs/node

No keepAliveTimeout for HTTP server after answering a POST request synchronously

未关闭
#39,137 9 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

help wanted http
主要语言
JavaScript
星标
122k
派生
37.3k
平均合并
4 天 2 小时
30 天内合并 PR
283

描述

  • Version: v16.4.0
  • Platform: Linux test01 4.19.0-13-amd64 # 1 SMP Debian 4.19.160-2 (2020-11-28) x86_64 GNU/Linux
  • Subsystem: http
What steps will reproduce the bug?

When using the "normal" NodeJS "hello world" HTTP server example, which answers any request synchronously, and sending it a POST request with a body, server.keepAliveTimeout (default 5 sec) does not work. The HTTP connection remains open ("forever"?).

Complete Testcase

Here we test all 4 combinations of sync/async response and get/post request to a simple HTTP server, and expect a connection close from server after 1 sec - which works fine for all combinations except "synchronous response for a POST request". If the connection is still open, we try another request - to prove, the connection still works:

'use strict';
const http = require('http');
const net = require('net');

const server = http.createServer((req, res) => {
  if (req.url === '/async') {
    req.on('data', () => {});
    req.on('end', () => {
      res.end('test-body');
      console.log('Server: Sent response asynchronously');
    });
  } else {
    res.end('test-body');
    console.log('Server: Sent response synchronously');
  }
});

server.keepAliveTimeout = 1000;

server.listen(0, async () => {
  await sendRequests('async', 'get');
  await sendRequests('async', 'post');
  await sendRequests('sync', 'get');
  await sendRequests('sync', 'post'); // keepAliveTimeout does not work here!!

  server.close();
});

async function sendRequests(type, method) {
  console.log(`=== Testing ${type} response for method "${method}" and wait for keep-alive-close`);

  return new Promise((resolve) => {
    const client = new net.Socket();
    let nextReqTimeout = null;

    client.connect(server.address().port, '127.0.0.1', () => {
      console.log('Client: Connected to server');

      httpRequest();

      nextReqTimeout = setTimeout(() => {
        console.log('Client: ERROR: HTTP-Connection is still open - trying another request');
        httpRequest();

        nextReqTimeout = setTimeout(() => {
          console.log('Client: ERROR: HTTP-Connection is still open - closing from client side now');
          nextReqTimeout = null;
          client.end();
        }, server.keepAliveTimeout * 2);
      }, server.keepAliveTimeout * 2);
    });

    client.on('data', function(data) {
      console.log('Client: Got response data');
    });

    client.on('close', function() {
      if (nextReqTimeout) {
        console.log('Client: Server closed connection as expected');
        clearTimeout(nextReqTimeout);
      }
      resolve();
    });

    function httpRequest() {
      const rawRequests = {
        'post': 'POST /' + type + ' HTTP/1.1\r\n' +
          'Connection: keep-alive\r\n' +
          'Content-Type: application/x-www-form-urlencoded\r\n' +
          'Content-Length: 10\r\n' +
          '\r\n' +
          'Test=67890',
        'get': 'GET /' + type + ' HTTP/1.1\r\n' +
          'Connection: keep-alive\r\n' +
          '\r\n'
      };

      console.log('Client: Sending request');
      client.write(rawRequests[method]);
    }
  });
}
What is the expected behavior?

The keepAliveTimeout should also work after answering a POST request synchronously.

Possible cause

Maybe this is a race condition here:

  • In resOnFinish the server.keepAliveTimeout is set on the socket, after sending the response
  • After that, socketOnData is called internally for the remaining POST body, which calls onParserExecuteCommon, which resets the socket timeout first
  • Normally socketOnData is called before sending the response, so in most cases it works as expected

Maybe there should be a swicth, to not reset the socket-timeout when socketOnData is called after the response was sent?

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

先运行提供的 JavaScript 重现代码,然后阅读 lib/_http_server.js 中 resOnFinish、socketOnData 和 onParserExecuteCommon 附近的代码。跟踪同步响应之后剩余的 POST body 如何重置 socket 超时。完成的标准是:同步 POST 情况与其他三个情况一样,在 server.keepAliveTimeout 之后关闭,并为报告的行为提供回归覆盖。

由索引模型根据 Issue 内容生成。

评估

技术栈
javascript
领域
api, backend
Issue 类型
缺陷
难度
3/5
预计耗时
1-2 天
活跃度
活跃
描述清晰度
描述清楚
新手友好度
68/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。