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

Python 当WSGI应用程序在使用environ[';WSGI.input';]之前响应时,TCP连接重置发生,python,http,tcp,paste,wsgiref,Python,Http,Tcp,Paste,Wsgiref,对于我们的Web服务,我编写了一些逻辑来防止多部分/表单数据大于4mb的帖子 它可以归结为以下几点(我去掉了所有WebOb的用法,只是将其简化为普通的WSGI代码): import paste.httpserver form=”“”\ """ 极限=4*1024*1024 def上传应用程序(环境,启动响应): 如果环境['REQUEST_METHOD']=“POST”: 如果int(environ.get('CONTENT_LENGTH','0'))>限制: 开始响应('400ouch',[(

对于我们的Web服务,我编写了一些逻辑来防止
多部分/表单数据
大于4mb的帖子

它可以归结为以下几点(我去掉了所有WebOb的用法,只是将其简化为普通的WSGI代码):

import paste.httpserver
form=”“”\
"""
极限=4*1024*1024
def上传应用程序(环境,启动响应):
如果环境['REQUEST_METHOD']=“POST”:
如果int(environ.get('CONTENT_LENGTH','0'))>限制:
开始响应('400ouch',[('content-type','text/plain'))
return[“上传太大!”]
#省略:适当地使用该文件
开始响应('200OK',[('content-type','text/html'))
报税表[表格]
粘贴.httpserver.service(上传应用程序,端口=7007)
所示逻辑在单元测试时正常工作。但当我尝试将大于4mb的实际文件发送到此端点时,我在客户端遇到了如下错误:

  • Error 101(net::ERR\u CONNECTION\u RESET):未知错误。
    来自谷歌浏览器
  • 在加载页面时重置了与服务器的连接。
    来自Firefox
使用Python内置的
wsgiref
HTTP服务器时也会发生相同的错误

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

我仔细阅读并发现了一些有趣的指导原则,说明在实现HTTP服务器和客户端时仔细管理TCP连接的重要性。它接着说,与其关闭套接字,不如关闭套接字,这样客户端才有机会做出反应,停止向服务器发送更多数据

也许我遗漏了一些关键的实现细节,这些细节可以防止这种连接重置。有人吗


请参见

之所以发生这种情况,是因为您在不读取输入流的情况下丢弃了输入流,而这正迫使它关闭。浏览器已将要发送的文件的大部分排队,然后由于服务器强制关闭连接而导致写入错误

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


我建议使用一些Javascript在发送文件之前测试文件的大小。那么,只有那些因为没有Javascript或故意恶意而忽略客户端检查的人才会出错。

不幸的是,如果客户端没有ActiveX、Java或Flash,即使在查找诸如所选文件大小之类的无辜内容时,也不能查询表单上的输入元素。@Pavel Repin,我不知道Javascript是这样的。那太不幸了。尽管我会指出,我的答案基本上仍然是正确的,即使这不是一个令人满意的答案。:-)我在网上做了更多的调查。事实证明,优雅地限制身体尺寸并没有得到很好的解决。Nginx作者Igor Sysoev对此有一篇评论:总结一下:您必须执行一个“延迟关闭”舞蹈,服务器发送错误响应,关闭套接字进行写入,并等待客户端一段时间,以避免直接终止连接。另一个相关链接:它建议HTTP服务器应该关闭“连接的一半”。
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)