Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add test for default Expect handler #601

Merged
merged 2 commits into from
Jan 29, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion aiohttp/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def handle_request(self, message, payload):
resp = None
request._match_info = match_info
expect = request.headers.get(hdrs.EXPECT)
if expect and expect.lower() == "100-continue":
if expect:
resp = (
yield from match_info.route.handle_expect_header(request))

Expand Down
15 changes: 12 additions & 3 deletions aiohttp/web_urldispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
from . import hdrs
from .abc import AbstractRouter, AbstractMatchInfo
from .protocol import HttpVersion11
from .web_exceptions import HTTPMethodNotAllowed, HTTPNotFound, HTTPNotModified
from .web_exceptions import (HTTPMethodNotAllowed, HTTPNotFound,
HTTPNotModified, HTTPExpectationFailed)
from .web_reqrep import StreamResponse
from .multidict import upstr

Expand Down Expand Up @@ -43,9 +44,17 @@ def __repr__(self):

@asyncio.coroutine
def _defaultExpectHandler(request):
"""Default handler for Except: 100-continue"""
"""Default handler for Except header.

Just send "100 Continue" to client.
raise HTTPExpectationFailed if value of header is not "100-continue"
"""
expect = request.headers.get(hdrs.EXPECT)
if request.version == HttpVersion11:
request.transport.write(b"HTTP/1.1 100 Continue\r\n\r\n")
if expect.lower() == "100-continue":
request.transport.write(b"HTTP/1.1 100 Continue\r\n\r\n")
else:
raise HTTPExpectationFailed(text="Unknown Expect: %s" % expect)


class Route(metaclass=abc.ABCMeta):
Expand Down
28 changes: 21 additions & 7 deletions docs/web.rst
Original file line number Diff line number Diff line change
Expand Up @@ -312,15 +312,26 @@ usually called *session*.

.. versionadded:: 0.15

:mod:`aiohttp.web` supports *Expect* header. By default
it responds with an *HTTP/1.1 100 Continue* status code.
It is possible to specify custom *Expect* header handler on per route basis.
This handler gets called after receiving all headers and before
:mod:`aiohttp.web` supports *Expect* header. By default it sends
*HTTP/1.1 100 Continue* line to client, or raises HTTPExpectationFailed if
header value is not equal to "100-continue". It is possible to specify
custom *Expect* header handler on per route basis. This handler gets called
if *Expect* header exist in request after receiving all headers and before
processing application middlewares :ref:`aiohttp-web-middlewares` and route
handler. Handler can return *None*, in that case the request processing
continues as usual. If handler returns an instance of
class :class:`StreamResponse`, *request handler* uses it as response.
Custom handler *must* write *HTTP/1.1 100 Continue* status if all checks pass.
class :class:`StreamResponse`, *request handler* uses it as response. Also
handler can raise a subclass of HTTPException. In this case all further
processing will not happen and client will receive appropriate http response.

.. note::
A server that does not understand or is unable to comply with any of the
expectation values in the Expect field of a request MUST respond with
appropriate error status. The server MUST respond with a 417
(Expectation Failed) status if any of the expectations cannot be met or,
if there are other problems with the request, some other 4xx status.

http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.20

This example shows custom handler for *Except* header:

Expand All @@ -330,8 +341,11 @@ This example shows custom handler for *Except* header:
if request.version != aiohttp.HttpVersion11:
return

if request.headers.get('EXPECT') != '100-continue':
raise HTTPExpectationFailed(text="Unknown Expect: %s" % expect)

if request.headers.get('AUTHORIZATION') is None:
return web.HTTPForbidden()
raise HTTPForbidden()

request.transport.write(b"HTTP/1.1 100 Continue\r\n\r\n")

Expand Down
26 changes: 26 additions & 0 deletions tests/test_web_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,32 @@ def go():

self.loop.run_until_complete(go())

def test_expect_default_handler_unknown(self):
"""Test default Expect handler for unknown Expect value.

A server that does not understand or is unable to comply with any of
the expectation values in the Expect field of a request MUST respond
with appropriate error status. The server MUST respond with a 417
(Expectation Failed) status if any of the expectations cannot be met
or, if there are other problems with the request, some other 4xx
status.

http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.20
"""
@asyncio.coroutine
def handler(request):
yield from request.post()
self.fail('Handler should not proceed to this point in case of '
'unknown Expect header')

@asyncio.coroutine
def go():
_, _, url = yield from self.create_server('POST', '/', handler)
resp = yield from request('POST', url, headers={'Expect': 'SPAM'},
loop=self.loop)
self.assertEqual(417, resp.status)
self.loop.run_until_complete(go())

def test_100_continue(self):
@asyncio.coroutine
def handler(request):
Expand Down