我正在使用Flask-uploads将文件上传到我的Flask服务器。允许的最大尺寸使用flaskext.uploads.patch_request_class(app, 16 * 1024 * 1024)
设置。
我的客户端应用程序(单元测试)使用requests发布大文件。
我可以看到我的服务器返回状态为413: Request Entity Too Large
的HTTP响应。但是客户端在requests代码
ConnectionError: HTTPConnectionPool(host='api.example.se', port=80): Max retries exceeded with url: /images (Caused by <class 'socket.error'>: [Errno 32] Broken pipe)
我的猜测是服务器断开接收套接字并将响应发送回客户端。但是当客户端获得一个损坏的发送套接字时,它会引发异常并跳过响应。
问题:
更新
这是一个重现我的问题的简单例子。
server.py
from flask import Flask, request
app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 1024
@app.route('/post', methods=('POST',))
def view_post():
return request.data
app.run(debug=True)
client.py
from tempfile import NamedTemporaryFile
import requests
def post(size):
print "Post with size %s" % size,
f = NamedTemporaryFile(delete=False, suffix=".jpg")
for i in range(0, size):
f.write("CoDe")
f.close()
# Post
files = {'file': ("tempfile.jpg", open(f.name, 'rb'))}
r = requests.post("http://127.0.0.1:5000/post", files=files)
print "gives status code = %s" % r.status_code
post(16)
post(40845)
post(40846)
来自客户的结果
Post with size 16 gives status code = 200
Post with size 40845 gives status code = 413
Post with size 40846
Traceback (most recent call last):
File "client.py", line 18, in <module>
post(40846)
File "client.py", line 13, in post
r = requests.post("http://127.0.0.1:5000/post", files=files)
File "/opt/python_env/renter/lib/python2.7/site-packages/requests/api.py", line 88, in post
return request('post', url, data=data, **kwargs)
File "/opt/python_env/renter/lib/python2.7/site-packages/requests/api.py", line 44, in request
return session.request(method=method, url=url, **kwargs)
File "/opt/python_env/renter/lib/python2.7/site-packages/requests/sessions.py", line 357, in request
resp = self.send(prep, **send_kwargs)
File "/opt/python_env/renter/lib/python2.7/site-packages/requests/sessions.py", line 460, in send
r = adapter.send(request, **kwargs)
File "/opt/python_env/renter/lib/python2.7/site-packages/requests/adapters.py", line 354, in send
raise ConnectionError(e)
requests.exceptions.ConnectionError: HTTPConnectionPool(host='127.0.0.1', port=5000): Max retries exceeded with url: /post (Caused by <class 'socket.error'>: [Errno 32] Broken pipe)
我的版本
$ pip freeze
Flask==0.10.1
Flask-Mail==0.9.0
Flask-SQLAlchemy==1.0
Flask-Uploads==0.1.3
Jinja2==2.7.1
MarkupSafe==0.18
MySQL-python==1.2.4
Pillow==2.1.0
SQLAlchemy==0.8.2
Werkzeug==0.9.4
blinker==1.3
itsdangerous==0.23
passlib==1.6.1
python-dateutil==2.1
requests==2.0.0
simplejson==3.3.0
six==1.4.1
virtualenv==1.10.1
voluptuous==0.8.1
wsgiref==0.1.2
答案 0 :(得分:4)
RFC 2616,HTTP 1.1的规范说:
10.4.14 413请求实体太大
服务器因为请求而拒绝处理请求 实体大于服务器愿意或能够处理的实体。的的
服务器可以关闭连接以防止客户端继续 请求。如果条件是暂时的,服务器应该包括重试 - 标题字段后表示它是临时的,之后是什么
客户可以再试一次。
这就是这里发生的事情:烧瓶正在关闭连接,以防止客户端继续上传,这会导致Broken pipe
错误。
答案 1 :(得分:2)
Flask正在关闭连接,您可以为413错误设置错误处理程序:
@app.errorhandler(413)
def request_entity_too_large(error):
return 'File Too Large', 413
现在客户端应该收到413错误,请注意我没有测试此代码。
<强>更新强>
我尝试重新创建413错误,但我没有得到ConnectionError异常。
这是一个简单的例子:
from flask import Flask, request
app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 1024
@app.route('/post', methods=('POST',))
def view_post():
return request.data
app.run(debug=True)
运行文件后,我使用终端测试请求并发送大数据:
>>> import requests
>>> r = requests.post('http://127.0.0.1:5000/post', data={'foo': 'a'})
>>> r
<Response [200]>
>>> r = requests.post('http://127.0.0.1:5000/post', data={'foo': 'a'*10000})
>>> r
<Response [413]>
>>> r.status_code
413
>>> r.content
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\n<title>413 Request Entity Too Large</title
>\n<h1>Request Entity Too Large</h1>\n<p>The data value transmitted exceeds the capacity limit.</p>\n'
正如您所看到的,我们得到了烧瓶413错误的响应,并且请求没有引发异常。
顺便说一下我使用:
答案 2 :(得分:0)
基于此github问题答案(https://github.com/benoitc/gunicorn/issues/1733#issuecomment-377000612)
@app.before_request
def handle_chunking():
"""
Sets the "wsgi.input_terminated" environment flag, thus enabling
Werkzeug to pass chunked requests as streams. The gunicorn server
should set this, but it's not yet been implemented.
"""
transfer_encoding = request.headers.get("Transfer-Encoding", None)
if transfer_encoding == u"chunked":
request.environ["wsgi.input_terminated"] = True