为什么environ['wsgi.input'].read()会阻塞,尽管PEP-3333允许?
问题
这里有一个简单的WSGI应用程序,它应该在头部打印内容的长度和请求体。
def application(environ, start_response):
start_response('200 OK', [('Content-Type','text/plain')])
content_length = int(environ['CONTENT_LENGTH'])
print('---- Begin ----')
print('CONTENT_LENGTH:', content_length)
print('wsgi.input:', environ['wsgi.input'].read())
print('---- End ----')
return [b'Foo\n']
if __name__ == '__main__':
from wsgiref import simple_server
server = simple_server.make_server('0.0.0.0', 8080, application)
server.serve_forever()
当我运行这个应用程序时,它在以下调用处被阻塞:environ['wsgi.input'].read()
。
我使用Python 3解释器运行这个应用程序,并通过curl提交HTTP POST请求。
lone@debian:~$ curl --data "a=1&b=2" http://localhost:8080/
curl命令在等待输出时被阻塞。Python解释器在environ['wsgi.input'].read()
这个调用处被阻塞。
lone@debian:~$ python3 foo.py
---- Begin ----
CONTENT_LENGTH: 7
从上面的输出可以看到,application()
函数在打印CONTENT_LENGTH
后被阻塞了。
解决方法
我知道如何解决这个问题:通过将Content-Length头的值传递给read()
调用。
修改后的代码来解决这个问题:
def application(environ, start_response):
start_response('200 OK', [('Content-Type','text/plain')])
content_length = int(environ['CONTENT_LENGTH'])
print('---- Begin ----')
print('CONTENT_LENGTH:', content_length)
print('wsgi.input:', environ['wsgi.input'].read(content_length))
print('---- End ----')
return [b'Foo\n']
if __name__ == '__main__':
from wsgiref import simple_server
server = simple_server.make_server('0.0.0.0', 8080, application)
server.serve_forever()
现在curl命令得到了有效的HTTP响应。
lone@debian:~$ curl --data "a=1&b=2" http://localhost:8080/
Foo
lone@debian:~$
application()
函数也完成了它的执行。
lone@debian:~$ python3 foo.py
---- Begin ----
CONTENT_LENGTH: 7
wsgi.input: b'a=1&b=2'
---- End ----
127.0.0.1 - - [06/Apr/2014 17:53:21] "POST / HTTP/1.1" 200 4
疑问
为什么在没有任何参数的情况下调用environ['wsgi.input'].read()
会被阻塞呢?
PEP-3333文档似乎暗示它应该可以工作。这里是相关的内容。
服务器不需要读取超过客户端指定的
Content-Length
,并且应该模拟文件结束的状态,如果应用程序尝试读取超过这个点。应用程序不应该尝试读取超过CONTENT_LENGTH
变量指定的数据。服务器应该允许在没有参数的情况下调用
read()
,并返回客户端输入流的剩余部分。
我明白应用程序不应该尝试读取超过CONTENT_LENGTH
变量指定的数据。我没有遵守这个指令。但是服务器应该允许在没有参数的情况下调用read()
并返回整个输入流,为什么它没有这样做呢?
1 个回答
因为它只实现了PEP 333,而没有实现PEP 3333。
PEP 333没有规定通过返回空字符串来模拟流的结束。
在PEP 333中,如果WSGI服务器支持HTTP 1.1,并且使用了请求管道(保持连接),那么如果你尝试读取超过CONTENT_LENGTH的内容,就会遇到问题。
我建议你阅读一下PEP 333,并把它和PEP 3333进行对比。
另外,建议你看看:
在这里我详细描述了整个问题,并为PEP在更新到Python 3时的变化做出了贡献。