Respect HTTP request body close in ASGI.

This commit is contained in:
Andrew Godwin 2016-04-04 01:02:48 +02:00
parent 920882f1da
commit 67e3e55131
3 changed files with 39 additions and 2 deletions

View File

@ -21,3 +21,11 @@ class RequestTimeout(Exception):
Raised when it takes too long to read a request body. Raised when it takes too long to read a request body.
""" """
pass pass
class RequestAborted(Exception):
"""
Raised when the incoming request tells us it's aborted partway through
reading the body.
"""
pass

View File

@ -18,7 +18,7 @@ from django.http import FileResponse, HttpResponse, HttpResponseServerError
from django.utils import six from django.utils import six
from django.utils.functional import cached_property from django.utils.functional import cached_property
from .exceptions import ResponseLater as ResponseLaterOuter, RequestTimeout from .exceptions import ResponseLater as ResponseLaterOuter, RequestTimeout, RequestAborted
logger = logging.getLogger('django.request') logger = logging.getLogger('django.request')
@ -118,6 +118,9 @@ class AsgiRequest(http.HttpRequest):
[message['body_channel']], [message['body_channel']],
block=True, block=True,
) )
# If chunk contains close, abort.
if chunk.get("closed", False):
raise RequestAborted()
# Add content to body # Add content to body
self._body += chunk.get("content", "") self._body += chunk.get("content", "")
# Exit loop if this was the last # Exit loop if this was the last
@ -197,6 +200,9 @@ class AsgiHandler(base.BaseHandler):
except RequestTimeout: except RequestTimeout:
# Parsing the rquest failed, so the response is a Request Timeout error # Parsing the rquest failed, so the response is a Request Timeout error
response = HttpResponse("408 Request Timeout (upload too slow)", status_code=408) response = HttpResponse("408 Request Timeout (upload too slow)", status_code=408)
except RequestAborted:
# Client closed connection on us mid request. Abort!
return
else: else:
try: try:
response = self.get_response(request) response = self.get_response(request)

View File

@ -4,7 +4,7 @@ from django.utils import six
from channels import Channel from channels import Channel
from channels.tests import ChannelTestCase from channels.tests import ChannelTestCase
from channels.handler import AsgiRequest from channels.handler import AsgiRequest
from channels.exceptions import RequestTimeout from channels.exceptions import RequestTimeout, RequestAborted
class RequestTests(ChannelTestCase): class RequestTests(ChannelTestCase):
@ -216,3 +216,26 @@ class RequestTests(ChannelTestCase):
body_receive_timeout = 0 body_receive_timeout = 0
with self.assertRaises(RequestTimeout): with self.assertRaises(RequestTimeout):
VeryImpatientRequest(self.get_next_message("test")) VeryImpatientRequest(self.get_next_message("test"))
def test_request_abort(self):
"""
Tests that the code aborts when a request-body close is sent.
"""
Channel("test").send({
"reply_channel": "test",
"http_version": "1.1",
"method": "POST",
"path": b"/test/",
"body": b"there_a",
"body_channel": "test-input",
"headers": {
"host": b"example.com",
"content-type": b"application/x-www-form-urlencoded",
"content-length": b"21",
},
})
Channel("test-input").send({
"closed": True,
})
with self.assertRaises(RequestAborted):
AsgiRequest(self.get_next_message("test"))