为什么Apache/WSGI映射HEAD到GET?如何在Flask中加速HEAD



这是一个可以从命令行或通过Apache/WSGI运行的Flask应用程序:

import flask
app = flask.Flask(__name__)
LENGTH = 1000000                # one million
@app.route('/', methods=['HEAD'])
def head():
    return 'x' * LENGTH         # response body isn't actually sent
@app.route('/', methods=['GET'])
def get():
    import random
    return ''.join(str(random.randint(0,9)) for x in range(LENGTH))
if __name__ == '__main__':
    app.run()                   # from command-line
else:
    application = app           # via Apache and WSGI

。,这个应用程序返回一百万个随机数字。GET请求花费了大量的时间,但是HEAD请求应该能够几乎立即返回。这当然是一个说明性的例子;实际的应用程序可能涉及到为GET请求生成较慢的大型响应,但也有预先确定的大小,可以通过HEAD请求快速查询。(另一种情况:我试图将请求重定向到预签名的Amazon S3 url,必须对HEAD和GET方法进行不同的签名。)

问题#1)当我从命令行运行Flask应用程序时,HEAD请求激活head函数,正如预期的那样;但是当我通过Apache/WSGI运行它时,它激活了get功能。为什么会这样,我该如何解决这个问题来获得我想要的行为?

问题#2)而不是为HEAD请求创建一个虚拟响应(分配一堆内存),为什么我不能返回app.make_response('', 200, {'Content-Length':LENGTH}) ?

我的猜测是,这是由于一个善意的尝试,以确保一个HEAD请求应该始终与相应的GET一致。所以:

猜测#1)Apache或WSGI内部重写HEAD到GET。

猜想#2)Flask不相信我手动设置Content-Length头,并且用响应体的实际长度重写它…即使对于HEAD请求,它实际上应该是空的。

我误解了什么吗?关于如何更快地处理HEAD请求,有什么建议吗?理想情况下,不必缓慢地生成仅用于设置Content-Length报头的大型响应体。

如前所述,mod_wsgi为什么将HEAD重新映射为GET的问题在下面有很好的描述:

  • http://blog.dscpl.com.au/2009/10/wsgi-issues-with-http-head-requests.html

特别是,正如在那篇博客文章中解释的那样,如果你有一个Apache输出过滤器设置,并且有可能因此需要从你的WSGI应用程序中看到相同的输出,对于同一个URL,无论GET还是HEAD,那么mod_wsgi将不相信你的应用程序做了正确的事情,并将HEAD重新映射为GET,以确保Apache输出过滤器将正常工作。

如果你不关心你没有为HEAD请求返回与GET请求相同的响应头,从而打破了HTTP RFC指定的HEAD要求,那么只需确保你没有配置Apache输出过滤器,你可以像mod_wsgi那样打破东西,然后不会重新映射请求方法类型。

要从Flask创建一个完整的响应,您需要这样做:

@app.route('/', methods=['HEAD'])
def head():
    response = Response()
    response.headers.add('content-length', LENGTH)
    return response

结果是这样的:

Connected to localhost.
Escape character is '^]'.
HEAD / HTTP/1.1
Host: localhost
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
content-length: 1000000
Server: Werkzeug/0.9.4 Python/2.7.6
Date: Sun, 16 Mar 2014 22:59:16 GMT

这是在标准运行器上测试的,而不是通过wsgi,但这应该不会有什么不同。

对于Apache/WSGI强制使用get处理程序,这篇博客文章有一些提示,说明为什么会发生这种情况。

参见:Flask/Werkzeug如何将HTTP内容长度头附加到文件下载

最新更新