Skip to content

Commit

Permalink
Fix #1587: add support for handling Expect Header (#1600)
Browse files Browse the repository at this point in the history
Fix #1587: add support for handling Expect Header
  • Loading branch information
seemethere committed Jun 10, 2019
2 parents 13079c6 + 1b1a51c commit 072fcfe
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 2 deletions.
5 changes: 5 additions & 0 deletions sanic/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,11 @@ def __init__(self, message, content_range):
}


@add_status_code(417)
class HeaderExpectationFailed(SanicException):
pass


@add_status_code(403)
class Forbidden(SanicException):
pass
Expand Down
2 changes: 1 addition & 1 deletion sanic/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def json_loads(data):


DEFAULT_HTTP_CONTENT_TYPE = "application/octet-stream"

EXPECT_HEADER = "EXPECT"

# HTTP/1.1: https://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.2.1
# > If the media type remains unknown, the recipient SHOULD treat it
Expand Down
22 changes: 21 additions & 1 deletion sanic/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@
from multidict import CIMultiDict

from sanic.exceptions import (
HeaderExpectationFailed,
InvalidUsage,
PayloadTooLarge,
RequestTimeout,
ServerError,
ServiceUnavailable,
)
from sanic.log import access_logger, logger
from sanic.request import Request, StreamBuffer
from sanic.request import EXPECT_HEADER, Request, StreamBuffer
from sanic.response import HTTPResponse


Expand Down Expand Up @@ -314,6 +315,10 @@ def on_headers_complete(self):
if self._keep_alive_timeout_handler:
self._keep_alive_timeout_handler.cancel()
self._keep_alive_timeout_handler = None

if self.request.headers.get(EXPECT_HEADER):
self.expect_handler()

if self.is_request_stream:
self._is_stream_handler = self.router.is_stream_handler(
self.request
Expand All @@ -324,6 +329,21 @@ def on_headers_complete(self):
)
self.execute_request_handler()

def expect_handler(self):
"""
Handler for Expect Header.
"""
expect = self.request.headers.get(EXPECT_HEADER)
if self.request.version == "1.1":
if expect.lower() == "100-continue":
self.transport.write(b"HTTP/1.1 100 Continue\r\n\r\n")
else:
self.write_error(
HeaderExpectationFailed(
"Unknown Expect: {expect}".format(expect=expect)
)
)

def on_body(self, body):
if self.is_request_stream and self._is_stream_handler:
self._request_stream_task = self.loop.create_task(
Expand Down
34 changes: 34 additions & 0 deletions tests/test_request_stream.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import pytest
from sanic.blueprints import Blueprint
from sanic.exceptions import HeaderExpectationFailed
from sanic.request import StreamBuffer
from sanic.response import stream, text
from sanic.views import CompositionView, HTTPMethodView
Expand Down Expand Up @@ -40,6 +42,38 @@ async def post(self, request):
assert response.text == data


@pytest.mark.parametrize("headers, expect_raise_exception", [
({"EXPECT": "100-continue"}, False),
({"EXPECT": "100-continue-extra"}, True),
])
def test_request_stream_100_continue(app, headers, expect_raise_exception):
class SimpleView(HTTPMethodView):

@stream_decorator
async def post(self, request):
assert isinstance(request.stream, StreamBuffer)
result = ""
while True:
body = await request.stream.read()
if body is None:
break
result += body.decode("utf-8")
return text(result)

app.add_route(SimpleView.as_view(), "/method_view")

assert app.is_request_stream is True

if not expect_raise_exception:
request, response = app.test_client.post("/method_view", data=data, headers={"EXPECT": "100-continue"})
assert response.status == 200
assert response.text == data
else:
with pytest.raises(ValueError) as e:
app.test_client.post("/method_view", data=data, headers={"EXPECT": "100-continue-extra"})
assert "Unknown Expect: 100-continue-extra" in str(e)


def test_request_stream_app(app):
"""for self.is_request_stream = True and decorators"""

Expand Down

0 comments on commit 072fcfe

Please sign in to comment.