当 WSGI 应用程序在使用 environ['wsgi.input'] 之前响应时,会发生 TCP 连接重置

发布于 2024-08-18 15:32:46 字数 1651 浏览 3 评论 0原文

对于我们的 Web 服务,我编写了一些逻辑来防止 multipart/form-data POST 大于 4mb。

它可以归结为以下内容(我已经剥离了所有 WebOb 的使用,只是将其简化为普通的 WSGI 代码):

import paste.httpserver

form = """\
<html>
<body>
  <form method="post" enctype="multipart/form-data" action="/">
    <input type="file" name="photopicker" />
    <input type="submit" />
  </form>
</body>
</html>
"""

limit = 4 * 1024 * 1024

def upload_app(environ, start_response):
    if environ['REQUEST_METHOD'] == 'POST':
        if int(environ.get('CONTENT_LENGTH', '0')) > limit:
            start_response('400 Ouch', [('content-type', 'text/plain')])
            return ["Upload is too big!"]
    # elided: consume the file appropriately
    start_response('200 OK', [('content-type', 'text/html')])
    return [form]

paste.httpserver.serve(upload_app, port=7007)

所显示的逻辑在经过单元测试时工作正常。但当我尝试将大于 4mb 的实际文件发送到此端点时,我在客户端收到如下错误:

  • Error 101 (net::ERR_CONNECTION_RESET): Unknown error. from Google Chrome
  • 加载页面时重置了与服务器的连接。来自 Firefox

使用 Python 内置 wsgiref HTTP 服务器时会出现相同的错误。

事实:在使用 HTTP 400 进行响应之前添加 environ['wsgi.input'].read() 后,连接重置问题就消失了。当然,这不是一个好的解决办法。它只是显示当您完全消耗输入时会发生什么。

我仔细阅读了 HTTP:权威指南,发现了一些有趣的指南,说明仔细管理 TCP 连接的重要性当实现 HTTP 服务器和客户端时。它继续讨论如何不关闭关闭套接字,而是首选关闭,以便客户端有机会做出反应并停止向服务器发送更多数据。

也许我遗漏了一些阻止此类连接重置的关键实现细节。有人有见解吗?

请参阅要点。

For our webservice, I wrote some logic to prevent multipart/form-data POSTs larger than, say, 4mb.

It boils down to the following (I've stripped away all WebOb usage and just reduced it to plain vanilla WSGI code):

import paste.httpserver

form = """\
<html>
<body>
  <form method="post" enctype="multipart/form-data" action="/">
    <input type="file" name="photopicker" />
    <input type="submit" />
  </form>
</body>
</html>
"""

limit = 4 * 1024 * 1024

def upload_app(environ, start_response):
    if environ['REQUEST_METHOD'] == 'POST':
        if int(environ.get('CONTENT_LENGTH', '0')) > limit:
            start_response('400 Ouch', [('content-type', 'text/plain')])
            return ["Upload is too big!"]
    # elided: consume the file appropriately
    start_response('200 OK', [('content-type', 'text/html')])
    return [form]

paste.httpserver.serve(upload_app, port=7007)

The logic shown works right when unit tested. But as soon as I tried sending actual files larger than 4mb to this endpoint, I got errors like these on the client side:

  • Error 101 (net::ERR_CONNECTION_RESET): Unknown error. from Google Chrome
  • The connection to the server was reset while the page was loading. from Firefox

Same error occurs when using Python built-in wsgiref HTTP server.

Fact: once I added environ['wsgi.input'].read() just before responding with HTTP 400, the connection reset problem went away. Of course, this is not a good fix. It just shows what happens when you fully consume the input.

I perused HTTP: The Definitive Guide and found some interesting guidelines on how it was important to mangage TCP connections carefully when implementing HTTP servers and clients. It went on about how, instead of close-ing socket, it was preferred to do shutdown, so that the client had chance to react and stop sending more data to server.

Perhaps I am missing some crucial implementation detail that prevents such connection resets. Insights anyone?

See the gist.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

全部不再 2024-08-25 15:32:46

发生这种情况是因为您在不读取输入流的情况下丢弃了它,这会强制它关闭。浏览器已经将要发送的文件的很大一部分排队,然后由于服务器强制关闭连接而收到写入错误。

据我所知,如果不阅读所有输入,就没有办法解决这个问题。

我建议在发送文件之前使用一些 Javascript 来测试文件的大小。然后,唯一收到错误的人是那些忽略客户端检查的人,因为他们没有 Javascript 或者因为他们故意尝试恶意。

This is happening because you are discarding the input stream without reading it, and this is forcing it closed. The browser has queued up a good portion of the file to be sent already and then it gets a write error because the server closes the connection forcefully.

There is no way around this that I know of without reading all the input.

I would recommend some Javascript to test the size of the file before it is sent. Then the only people who get the error are those who are ignoring the client-side check because they don't have Javascript or because they are purposefully trying to be malicious.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文