mirror of
https://github.com/django/daphne.git
synced 2025-04-21 17:22:03 +03:00
Implement timeout on request body reading
This commit is contained in:
parent
e18bfed8f3
commit
920882f1da
|
@ -14,3 +14,10 @@ class ResponseLater(Exception):
|
|||
returning a response.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class RequestTimeout(Exception):
|
||||
"""
|
||||
Raised when it takes too long to read a request body.
|
||||
"""
|
||||
pass
|
||||
|
|
|
@ -4,6 +4,7 @@ import cgi
|
|||
import codecs
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
from io import BytesIO
|
||||
from threading import Lock
|
||||
|
@ -13,11 +14,11 @@ from django.conf import settings
|
|||
from django.core import signals
|
||||
from django.core.handlers import base
|
||||
from django.core.urlresolvers import set_script_prefix
|
||||
from django.http import FileResponse, HttpResponseServerError
|
||||
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
|
||||
from .exceptions import ResponseLater as ResponseLaterOuter, RequestTimeout
|
||||
|
||||
logger = logging.getLogger('django.request')
|
||||
|
||||
|
@ -30,6 +31,10 @@ class AsgiRequest(http.HttpRequest):
|
|||
|
||||
ResponseLater = ResponseLaterOuter
|
||||
|
||||
# Number of seconds until a Request gives up on trying to read a request
|
||||
# body and aborts.
|
||||
body_receive_timeout = 60
|
||||
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
self.reply_channel = self.message.reply_channel
|
||||
|
@ -100,10 +105,15 @@ class AsgiRequest(http.HttpRequest):
|
|||
# Body handling
|
||||
self._body = message.get("body", b"")
|
||||
if message.get("body_channel", None):
|
||||
body_handle_start = time.time()
|
||||
while True:
|
||||
# Get the next chunk from the request body channel
|
||||
chunk = None
|
||||
while chunk is None:
|
||||
# If they take too long, raise request timeout and the handler
|
||||
# will turn it into a response
|
||||
if time.time() - body_handle_start > self.body_receive_timeout:
|
||||
raise RequestTimeout()
|
||||
_, chunk = message.channel_layer.receive_many(
|
||||
[message['body_channel']],
|
||||
block=True,
|
||||
|
@ -184,6 +194,9 @@ class AsgiHandler(base.BaseHandler):
|
|||
}
|
||||
)
|
||||
response = http.HttpResponseBadRequest()
|
||||
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)
|
||||
else:
|
||||
try:
|
||||
response = self.get_response(request)
|
||||
|
|
|
@ -4,6 +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
|
||||
|
||||
|
||||
class RequestTests(ChannelTestCase):
|
||||
|
@ -188,3 +189,30 @@ class RequestTests(ChannelTestCase):
|
|||
self.assertEqual(request.method, "PUT")
|
||||
self.assertEqual(request.read(3), b"one")
|
||||
self.assertEqual(request.read(), b"twothree")
|
||||
|
||||
def test_request_timeout(self):
|
||||
"""
|
||||
Tests that the code correctly gives up after the request body read timeout.
|
||||
"""
|
||||
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",
|
||||
},
|
||||
})
|
||||
# Say there's more content, but never provide it! Muahahaha!
|
||||
Channel("test-input").send({
|
||||
"content": b"re=fou",
|
||||
"more_content": True,
|
||||
})
|
||||
class VeryImpatientRequest(AsgiRequest):
|
||||
body_receive_timeout = 0
|
||||
with self.assertRaises(RequestTimeout):
|
||||
VeryImpatientRequest(self.get_next_message("test"))
|
||||
|
|
Loading…
Reference in New Issue
Block a user