app: Map HEAD requests to GET

After much discussion, the WSGI community has mostly agreed that
applications should not treat GET and HEAD requests differently. The
HTTP specification practically demands that they are handled the same
way by applications, with the exception that responses to HEAD requests
do not send a body. When they are handled differently, there is an
unfortunate tendency to over-optimize the HEAD request, causing
discrepancies between the headers it returns and those returned by GET.
Further, WSGI middleware may need access to the response body in order
to properly manipulate the response (e.g. to rewrite links, etc.), which
could effect the response to HEAD requests as well.

This commit changes the behavior of `BaseApplication` to change the
method of all `HEAD` requests to `GET` prior to dispatching them.
`Request` objects now have a `real_method` attribute, which indicates
the original method of the request.

http://blog.dscpl.com.au/2009/10/wsgi-issues-with-http-head-requests.html
master
Dustin 2016-07-10 20:28:26 -05:00
parent ff27f3a917
commit 807a487639
1 changed files with 6 additions and 5 deletions

View File

@ -75,6 +75,11 @@ class BaseApplication(object):
allowed_methods = self._find_attr(func, 'allowed_methods') allowed_methods = self._find_attr(func, 'allowed_methods')
except AttributeError: except AttributeError:
allowed_methods = milla.DEFAULT_METHODS allowed_methods = milla.DEFAULT_METHODS
if request.method == 'HEAD':
request.real_method = 'HEAD'
request.method = 'GET'
else:
request.real_method = request.method
if request.method not in allowed_methods: if request.method not in allowed_methods:
allow_header = {'Allow': ', '.join(allowed_methods)} allow_header = {'Allow': ', '.join(allowed_methods)}
if request.method == 'OPTIONS': if request.method == 'OPTIONS':
@ -99,10 +104,6 @@ class BaseApplication(object):
if isinstance(response, _string) or not response: if isinstance(response, _string) or not response:
response = request.ResponseClass(response) response = request.ResponseClass(response)
if environ['REQUEST_METHOD'] == 'HEAD':
start_response(response.status, response.headerlist)
return ''
else:
return response(environ, start_response) return response(environ, start_response)
def _call_after(self, func): def _call_after(self, func):