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

View File

@ -4,7 +4,7 @@ from django.utils import six
from channels import Channel
from channels.tests import ChannelTestCase
from channels.handler import AsgiRequest
from channels.exceptions import RequestTimeout
from channels.exceptions import RequestTimeout, RequestAborted
class RequestTests(ChannelTestCase):
@ -216,3 +216,26 @@ class RequestTests(ChannelTestCase):
body_receive_timeout = 0
with self.assertRaises(RequestTimeout):
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"))