当WSGI应用在消费environ['wsgi.input']之前响应时发生TCP连接重置
为了我们的网络服务,我写了一些逻辑来防止发送超过4MB的multipart/form-data
POST请求。
简单来说,就是下面这些代码(我去掉了所有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.
这是来自谷歌浏览器的错误信息。The connection to the server was reset while the page was loading.
这是来自火狐浏览器的错误信息。
使用Python内置的wsgiref
HTTP服务器时也会出现同样的错误。
事实是:当我在返回HTTP 400之前加上environ['wsgi.input'].read()
时,连接重置的问题就消失了。当然,这并不是一个好的解决办法。它只是表明当你完全读取输入时会发生什么。
我查阅了《HTTP: The Definitive Guide》,发现了一些有趣的指导原则,强调在实现HTTP服务器和客户端时,管理TCP连接是非常重要的。书中提到,与其直接close
套接字,更推荐使用shutdown
,这样客户端就有机会反应并停止向服务器发送更多数据。
也许我遗漏了一些关键的实现细节,导致了这种连接重置的问题。有没有人能提供一些见解?
可以查看这个链接。
1 个回答
2
这个问题发生的原因是你没有读取输入流就把它丢弃了,这样就导致它被强制关闭了。浏览器已经把文件的一部分放入了发送队列,但因为服务器强行关闭了连接,所以就出现了写入错误。
我所知道的,解决这个问题的方法就是必须读取所有的输入。
我建议你使用一些JavaScript来在发送文件之前检查文件的大小。这样,只有那些忽视客户端检查的人会遇到错误,比如他们没有开启JavaScript,或者故意想搞事情的人。