mirror of
https://github.com/django/daphne.git
synced 2025-05-07 09:03:50 +03:00
356 lines
13 KiB
Python
356 lines
13 KiB
Python
from __future__ import unicode_literals
|
|
|
|
import os
|
|
from datetime import datetime
|
|
from itertools import islice
|
|
|
|
from django.http import FileResponse, HttpResponse, HttpResponseRedirect, JsonResponse, StreamingHttpResponse
|
|
from six import BytesIO
|
|
|
|
from channels import Channel
|
|
from channels.handler import AsgiHandler
|
|
from channels.tests import ChannelTestCase
|
|
|
|
|
|
class FakeAsgiHandler(AsgiHandler):
|
|
"""
|
|
Handler subclass that just returns a premade response rather than
|
|
go into the view subsystem.
|
|
"""
|
|
|
|
chunk_size = 30
|
|
|
|
def __init__(self, response):
|
|
assert isinstance(response, (HttpResponse, StreamingHttpResponse))
|
|
self._response = response
|
|
super(FakeAsgiHandler, self).__init__()
|
|
|
|
def get_response(self, request):
|
|
return self._response
|
|
|
|
|
|
class HandlerTests(ChannelTestCase):
|
|
"""
|
|
Tests that the handler works correctly and round-trips things into a
|
|
correct response.
|
|
"""
|
|
|
|
def test_basic(self):
|
|
"""
|
|
Tests a simple request
|
|
"""
|
|
# Make stub request and desired response
|
|
Channel("test").send({
|
|
"reply_channel": "test",
|
|
"http_version": "1.1",
|
|
"method": "GET",
|
|
"path": b"/test/",
|
|
})
|
|
response = HttpResponse(b"Hi there!", content_type="text/plain")
|
|
# Run the handler
|
|
handler = FakeAsgiHandler(response)
|
|
reply_messages = list(
|
|
handler(self.get_next_message("test", require=True)))
|
|
# Make sure we got the right number of messages
|
|
self.assertEqual(len(reply_messages), 1)
|
|
reply_message = reply_messages[0]
|
|
# Make sure the message looks correct
|
|
self.assertEqual(reply_message["content"], b"Hi there!")
|
|
self.assertEqual(reply_message["status"], 200)
|
|
self.assertEqual(reply_message.get("more_content", False), False)
|
|
self.assertEqual(
|
|
reply_message["headers"],
|
|
[
|
|
(b"Content-Type", b"text/plain"),
|
|
],
|
|
)
|
|
|
|
def test_cookies(self):
|
|
Channel("test").send({
|
|
"reply_channel": "test",
|
|
"http_version": "1.1",
|
|
"method": "GET",
|
|
"path": b"/test/",
|
|
})
|
|
response = HttpResponse(b"Hi there!", content_type="text/plain")
|
|
response.set_signed_cookie('foo', '1', expires=datetime.now())
|
|
# Run the handler
|
|
handler = FakeAsgiHandler(response)
|
|
reply_messages = list(
|
|
handler(self.get_next_message("test", require=True)))
|
|
# Make sure we got the right number of messages
|
|
self.assertEqual(len(reply_messages), 1)
|
|
reply_message = reply_messages[0]
|
|
# Make sure the message looks correct
|
|
self.assertEqual(reply_message["content"], b"Hi there!")
|
|
self.assertEqual(reply_message["status"], 200)
|
|
self.assertEqual(reply_message.get("more_content", False), False)
|
|
self.assertEqual(reply_message["headers"][0], (b'Content-Type', b'text/plain'))
|
|
self.assertIn('foo=', reply_message["headers"][1][1].decode())
|
|
|
|
def test_headers(self):
|
|
Channel("test").send({
|
|
"reply_channel": "test",
|
|
"http_version": "1.1",
|
|
"method": "GET",
|
|
"path": b"/test/",
|
|
})
|
|
response = HttpResponse(b"Hi there!", content_type="text/plain")
|
|
response['foo'] = 1
|
|
response['bar'] = 1
|
|
del response['bar']
|
|
del response['nonexistant_key']
|
|
handler = FakeAsgiHandler(response)
|
|
reply_messages = list(
|
|
handler(self.get_next_message("test", require=True)))
|
|
# Make sure we got the right number of messages
|
|
self.assertEqual(len(reply_messages), 1)
|
|
reply_message = reply_messages[0]
|
|
# Make sure the message looks correct
|
|
self.assertEqual(reply_message["content"], b"Hi there!")
|
|
header_dict = dict(reply_messages[0]['headers'])
|
|
self.assertEqual(header_dict[b'foo'].decode(), '1')
|
|
self.assertNotIn('bar', header_dict)
|
|
|
|
def test_large(self):
|
|
"""
|
|
Tests a large response (will need chunking)
|
|
"""
|
|
# Make stub request and desired response
|
|
Channel("test").send({
|
|
"reply_channel": "test",
|
|
"http_version": "1.1",
|
|
"method": "GET",
|
|
"path": b"/test/",
|
|
})
|
|
response = HttpResponse(
|
|
b"Thefirstthirtybytesisrighthereandhereistherest")
|
|
# Run the handler
|
|
handler = FakeAsgiHandler(response)
|
|
reply_messages = list(
|
|
handler(self.get_next_message("test", require=True)))
|
|
# Make sure we got the right number of messages
|
|
self.assertEqual(len(reply_messages), 2)
|
|
# Make sure the messages look correct
|
|
self.assertEqual(reply_messages[0][
|
|
"content"], b"Thefirstthirtybytesisrighthere")
|
|
self.assertEqual(reply_messages[0]["status"], 200)
|
|
self.assertEqual(reply_messages[0]["more_content"], True)
|
|
self.assertEqual(reply_messages[1]["content"], b"andhereistherest")
|
|
self.assertEqual(reply_messages[1].get("more_content", False), False)
|
|
|
|
def test_empty(self):
|
|
"""
|
|
Tests an empty response
|
|
"""
|
|
# Make stub request and desired response
|
|
Channel("test").send({
|
|
"reply_channel": "test",
|
|
"http_version": "1.1",
|
|
"method": "GET",
|
|
"path": b"/test/",
|
|
})
|
|
response = HttpResponse(b"", status=304)
|
|
# Run the handler
|
|
handler = FakeAsgiHandler(response)
|
|
reply_messages = list(
|
|
handler(self.get_next_message("test", require=True))
|
|
)
|
|
# Make sure we got the right number of messages
|
|
self.assertEqual(len(reply_messages), 1)
|
|
# Make sure the messages look correct
|
|
self.assertEqual(reply_messages[0].get("content", b""), b"")
|
|
self.assertEqual(reply_messages[0]["status"], 304)
|
|
self.assertEqual(reply_messages[0]["more_content"], False)
|
|
|
|
def test_empty_streaming(self):
|
|
"""
|
|
Tests an empty streaming response
|
|
"""
|
|
# Make stub request and desired response
|
|
Channel("test").send({
|
|
"reply_channel": "test",
|
|
"http_version": "1.1",
|
|
"method": "GET",
|
|
"path": b"/test/",
|
|
})
|
|
response = StreamingHttpResponse([], status=304)
|
|
# Run the handler
|
|
handler = FakeAsgiHandler(response)
|
|
reply_messages = list(
|
|
handler(self.get_next_message("test", require=True))
|
|
)
|
|
# Make sure we got the right number of messages
|
|
self.assertEqual(len(reply_messages), 1)
|
|
# Make sure the messages look correct
|
|
self.assertEqual(reply_messages[0].get("content", b""), b"")
|
|
self.assertEqual(reply_messages[0]["status"], 304)
|
|
self.assertEqual(reply_messages[0]["more_content"], False)
|
|
|
|
def test_chunk_bytes(self):
|
|
"""
|
|
Makes sure chunk_bytes works correctly
|
|
"""
|
|
# Empty string should still return one chunk
|
|
result = list(FakeAsgiHandler.chunk_bytes(b""))
|
|
self.assertEqual(len(result), 1)
|
|
self.assertEqual(result[0][0], b"")
|
|
self.assertEqual(result[0][1], True)
|
|
# Below chunk size
|
|
result = list(FakeAsgiHandler.chunk_bytes(
|
|
b"12345678901234567890123456789"))
|
|
self.assertEqual(len(result), 1)
|
|
self.assertEqual(result[0][0], b"12345678901234567890123456789")
|
|
self.assertEqual(result[0][1], True)
|
|
# Exactly chunk size
|
|
result = list(FakeAsgiHandler.chunk_bytes(
|
|
b"123456789012345678901234567890"))
|
|
self.assertEqual(len(result), 1)
|
|
self.assertEqual(result[0][0], b"123456789012345678901234567890")
|
|
self.assertEqual(result[0][1], True)
|
|
# Just above chunk size
|
|
result = list(FakeAsgiHandler.chunk_bytes(
|
|
b"123456789012345678901234567890a"))
|
|
self.assertEqual(len(result), 2)
|
|
self.assertEqual(result[0][0], b"123456789012345678901234567890")
|
|
self.assertEqual(result[0][1], False)
|
|
self.assertEqual(result[1][0], b"a")
|
|
self.assertEqual(result[1][1], True)
|
|
|
|
def test_iterator(self):
|
|
Channel("test").send({
|
|
"reply_channel": "test",
|
|
"http_version": "1.1",
|
|
"method": "GET",
|
|
"path": b"/test/",
|
|
})
|
|
response = HttpResponse(range(10))
|
|
handler = FakeAsgiHandler(response)
|
|
reply_messages = list(
|
|
handler(self.get_next_message("test", require=True)))
|
|
self.assertEqual(len(reply_messages), 1)
|
|
self.assertEqual(reply_messages[0]["content"], b"0123456789")
|
|
|
|
def test_streaming_data(self):
|
|
Channel("test").send({
|
|
"reply_channel": "test",
|
|
"http_version": "1.1",
|
|
"method": "GET",
|
|
"path": b"/test/",
|
|
})
|
|
response = StreamingHttpResponse('Line: %s' % i for i in range(10))
|
|
handler = FakeAsgiHandler(response)
|
|
reply_messages = list(
|
|
handler(self.get_next_message("test", require=True)))
|
|
self.assertEqual(len(reply_messages), 11)
|
|
self.assertEqual(reply_messages[0]["content"], b"Line: 0")
|
|
self.assertEqual(reply_messages[9]["content"], b"Line: 9")
|
|
|
|
def test_real_file_response(self):
|
|
Channel("test").send({
|
|
"reply_channel": "test",
|
|
"http_version": "1.1",
|
|
"method": "GET",
|
|
"path": b"/test/",
|
|
})
|
|
current_dir = os.path.realpath(os.path.join(
|
|
os.getcwd(), os.path.dirname(__file__)))
|
|
response = FileResponse(
|
|
open(os.path.join(current_dir, 'a_file'), 'rb'))
|
|
handler = FakeAsgiHandler(response)
|
|
reply_messages = list(
|
|
handler(self.get_next_message("test", require=True)))
|
|
self.assertEqual(len(reply_messages), 2)
|
|
self.assertEqual(response.getvalue(), b'')
|
|
|
|
def test_bytes_file_response(self):
|
|
Channel("test").send({
|
|
"reply_channel": "test",
|
|
"http_version": "1.1",
|
|
"method": "GET",
|
|
"path": b"/test/",
|
|
})
|
|
response = FileResponse(BytesIO(b'sadfdasfsdfsadf'))
|
|
handler = FakeAsgiHandler(response)
|
|
reply_messages = list(
|
|
handler(self.get_next_message("test", require=True)))
|
|
self.assertEqual(len(reply_messages), 2)
|
|
|
|
def test_string_file_response(self):
|
|
Channel("test").send({
|
|
"reply_channel": "test",
|
|
"http_version": "1.1",
|
|
"method": "GET",
|
|
"path": b"/test/",
|
|
})
|
|
response = FileResponse('abcd')
|
|
handler = FakeAsgiHandler(response)
|
|
reply_messages = list(
|
|
handler(self.get_next_message("test", require=True)))
|
|
self.assertEqual(len(reply_messages), 5)
|
|
|
|
def test_non_streaming_file_response(self):
|
|
Channel("test").send({
|
|
"reply_channel": "test",
|
|
"http_version": "1.1",
|
|
"method": "GET",
|
|
"path": b"/test/",
|
|
})
|
|
response = FileResponse(BytesIO(b'sadfdasfsdfsadf'))
|
|
# This is to test the exception handling. This would only happening if
|
|
# the StreamingHttpResponse was incorrectly subclassed.
|
|
response.streaming = False
|
|
|
|
handler = FakeAsgiHandler(response)
|
|
with self.assertRaises(AttributeError):
|
|
list(handler(self.get_next_message("test", require=True)))
|
|
|
|
def test_unclosable_filelike_object(self):
|
|
Channel("test").send({
|
|
"reply_channel": "test",
|
|
"http_version": "1.1",
|
|
"method": "GET",
|
|
"path": b"/test/",
|
|
})
|
|
|
|
# This is a readable object that cannot be closed.
|
|
class Unclosable:
|
|
|
|
def read(self, n=-1):
|
|
# Nothing to see here
|
|
return b""
|
|
|
|
response = FileResponse(Unclosable())
|
|
handler = FakeAsgiHandler(response)
|
|
reply_messages = list(islice(handler(self.get_next_message("test", require=True)), 5))
|
|
self.assertEqual(len(reply_messages), 1)
|
|
response.close()
|
|
|
|
def test_json_response(self):
|
|
Channel("test").send({
|
|
"reply_channel": "test",
|
|
"http_version": "1.1",
|
|
"method": "GET",
|
|
"path": b"/test/",
|
|
})
|
|
response = JsonResponse({'foo': (1, 2)})
|
|
handler = FakeAsgiHandler(response)
|
|
reply_messages = list(handler(self.get_next_message("test", require=True)))
|
|
self.assertEqual(len(reply_messages), 1)
|
|
self.assertEqual(reply_messages[0]['content'], b'{"foo": [1, 2]}')
|
|
|
|
def test_redirect(self):
|
|
for redirect_to in ['/', '..', 'https://example.com']:
|
|
Channel("test").send({
|
|
"reply_channel": "test",
|
|
"http_version": "1.1",
|
|
"method": "GET",
|
|
"path": b"/test/",
|
|
})
|
|
response = HttpResponseRedirect(redirect_to)
|
|
handler = FakeAsgiHandler(response)
|
|
reply_messages = list(handler(self.get_next_message("test", require=True)))
|
|
self.assertEqual(reply_messages[0]['status'], 302)
|
|
header_dict = dict(reply_messages[0]['headers'])
|
|
self.assertEqual(header_dict[b'Location'].decode(), redirect_to)
|