【问题标题】:Why does a NodeJS http server close socket on timeout without response?为什么NodeJS http服务器在超时时关闭套接字而没有响应?
【发布时间】:2020-05-07 22:31:53
【问题描述】:

给定一个超时为 10 秒的 NodeJS http 服务器:

const httpServer = require('http').createServer(app);
httpServer.timeout = 10 * 1000;

在超时时,邮递员会在没有任何响应代码的情况下显示此内容:

Error: socket hang up
Warning: This request did not get sent completely and might not have all the required system headers

如果 NodeJS 服务器位于 nginx 反向代理之后,则 nginx 返回 502 响应 (upstream prematurely closed connection while reading response header from upstream)。但这里只是在 localhost 上运行的 NodeJS/express。仍然有人期望得到正确的 http 响应。

根据this answer,这是预期的行为,套接字被简单地破坏了。

在带有 nginx 反向代理的架构中,服务器是否通常只销毁套接字而不向代理发送超时响应?

【问题讨论】:

    标签: node.js express httpserver nginx-reverse-proxy


    【解决方案1】:

    您正在设置socket timeout when you're setting the http server timeout。套接字超时可防止可能希望挂在您与 DOS 之间的连接的客户端滥用。它还有其他好处,例如确保一定水平的服务(尽管当您是客户时这些通常更重要)。

    它使用套接字超时而不是发送408 状态代码(请求超时)的原因是因为状态代码可能已经发送成功消息。

    如果您想在后端实现响应超时并优雅地处理它,您可以自己超时响应。请注意,您可能应该回复 408502 用于 http 代理 (nginx) 等网关,以指示下游连接失败。

    这是处理该问题的简单稻草人实现。

    const httpServer = require('http').createServer((req, res) => {
        setTimeout(()=>{
            res.statusCode = 200;
            res.statusMessage = "Ok";
            res.end("Done"); // I'm never called because the timeout will be called instead;
        }, 10000)
    });
    
    httpServer.on('request', (req, res) => {
        setTimeout(()=>{
            res.statusCode = 408;
            res.statusMessage = 'Request Timeout';
            res.end();
        }, 1000)
    });
    
    httpServer.listen(8080);
    

    【讨论】:

    • 谢谢,您能否详细说明“状态码可能已发送成功消息。”?据我了解,如果已经发送了响应,则套接字不会超时。
    • 客户端可能会发出请求,然后从不尝试读取响应,从而保持连接。响应首先发送状态码。 (请参阅这些MSDN HTTP docs 以获得良好的概述)。所以你不能关闭套接字并改变已经通过网络发送的状态。在 HTTP 1.x 中,首先是状态,然后是标头,然后是正文。
    • 有一篇关于不同 DDOS 攻击的好维基百科文章。其中之一是slow read attack,以及其他类似的攻击,这些攻击在一定程度上通过超时底层套接字得到缓解,但无法在 HTTP 层本身进行防御。
    • 如果我没记错的话,setTimeout的回调只在请求超时时调用,而不是在keepalive超时时调用。在这两种情况下,套接字都会被破坏,但只有在第一种情况下才会发送代码示例中的 408。所以这与 Slowloris 攻击无关,并且响应不可能已经发送,还是我错了?
    • 正确,setTimeout 只是用来使请求超时,但它本身并不处理套接字超时。如果需要,您可以通过获取请求的套接字并为on:timeout 事件添加自己的处理程序来处理此问题,但您仍应确保关闭套接字。你是对的,如果套接字超时小于其中任何一个,你只会得到相同的旧套接字超时,所以套接字超时应该大于你的请求超时。套接字超时只会在发生异常情况时发生。
    猜你喜欢
    • 2021-12-12
    • 2020-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-20
    • 2019-01-31
    • 1970-01-01
    • 2018-07-09
    相关资源
    最近更新 更多