daphne/channels/tests/test_request.py

244 lines
8.9 KiB
Python

from __future__ import unicode_literals
from django.utils import six
from channels import Channel
from channels.tests import ChannelTestCase
from channels.handler import AsgiRequest
from channels.exceptions import RequestTimeout, RequestAborted
class RequestTests(ChannelTestCase):
"""
Tests that ASGI request handling correctly decodes HTTP requests.
"""
def test_basic(self):
"""
Tests that the handler can decode the most basic request message,
with all optional fields omitted.
"""
Channel("test").send({
"reply_channel": "test-reply",
"http_version": "1.1",
"method": "GET",
"path": "/test/",
})
request = AsgiRequest(self.get_next_message("test"))
self.assertEqual(request.path, "/test/")
self.assertEqual(request.method, "GET")
self.assertFalse(request.body)
self.assertNotIn("HTTP_HOST", request.META)
self.assertNotIn("REMOTE_ADDR", request.META)
self.assertNotIn("REMOTE_HOST", request.META)
self.assertNotIn("REMOTE_PORT", request.META)
self.assertNotIn("SERVER_NAME", request.META)
self.assertNotIn("SERVER_PORT", request.META)
self.assertFalse(request.GET)
self.assertFalse(request.POST)
self.assertFalse(request.COOKIES)
def test_extended(self):
"""
Tests a more fully-featured GET request
"""
Channel("test").send({
"reply_channel": "test",
"http_version": "1.1",
"method": "GET",
"path": "/test2/",
"query_string": b"x=1&y=%26foo+bar%2Bbaz",
"headers": {
"host": b"example.com",
"cookie": b"test-time=1448995585123; test-value=yeah",
},
"client": ["10.0.0.1", 1234],
"server": ["10.0.0.2", 80],
})
request = AsgiRequest(self.get_next_message("test"))
self.assertEqual(request.path, "/test2/")
self.assertEqual(request.method, "GET")
self.assertFalse(request.body)
self.assertEqual(request.META["HTTP_HOST"], "example.com")
self.assertEqual(request.META["REMOTE_ADDR"], "10.0.0.1")
self.assertEqual(request.META["REMOTE_HOST"], "10.0.0.1")
self.assertEqual(request.META["REMOTE_PORT"], 1234)
self.assertEqual(request.META["SERVER_NAME"], "10.0.0.2")
self.assertEqual(request.META["SERVER_PORT"], 80)
self.assertEqual(request.GET["x"], "1")
self.assertEqual(request.GET["y"], "&foo bar+baz")
self.assertEqual(request.COOKIES["test-time"], "1448995585123")
self.assertEqual(request.COOKIES["test-value"], "yeah")
self.assertFalse(request.POST)
def test_post_single(self):
"""
Tests a POST body contained within a single message.
"""
Channel("test").send({
"reply_channel": "test",
"http_version": "1.1",
"method": "POST",
"path": "/test2/",
"query_string": "django=great",
"body": b"ponies=are+awesome",
"headers": {
"host": b"example.com",
"content-type": b"application/x-www-form-urlencoded",
"content-length": b"18",
},
})
request = AsgiRequest(self.get_next_message("test"))
self.assertEqual(request.path, "/test2/")
self.assertEqual(request.method, "POST")
self.assertEqual(request.body, b"ponies=are+awesome")
self.assertEqual(request.META["HTTP_HOST"], "example.com")
self.assertEqual(request.META["CONTENT_TYPE"], "application/x-www-form-urlencoded")
self.assertEqual(request.GET["django"], "great")
self.assertEqual(request.POST["ponies"], "are awesome")
with self.assertRaises(KeyError):
request.POST["django"]
with self.assertRaises(KeyError):
request.GET["ponies"]
def test_post_multiple(self):
"""
Tests a POST body across multiple messages (first part in 'body').
"""
Channel("test").send({
"reply_channel": "test",
"http_version": "1.1",
"method": "POST",
"path": "/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({
"content": b"re=fou",
"more_content": True,
})
Channel("test-input").send({
"content": b"r+lights",
})
request = AsgiRequest(self.get_next_message("test"))
self.assertEqual(request.method, "POST")
self.assertEqual(request.body, b"there_are=four+lights")
self.assertEqual(request.META["CONTENT_TYPE"], "application/x-www-form-urlencoded")
self.assertEqual(request.POST["there_are"], "four lights")
def test_post_files(self):
"""
Tests POSTing files using multipart form data and multiple messages,
with no body in the initial message.
"""
body = (
b'--BOUNDARY\r\n' +
b'Content-Disposition: form-data; name="title"\r\n\r\n' +
b'My First Book\r\n' +
b'--BOUNDARY\r\n' +
b'Content-Disposition: form-data; name="pdf"; filename="book.pdf"\r\n\r\n' +
b'FAKEPDFBYTESGOHERE' +
b'--BOUNDARY--'
)
Channel("test").send({
"reply_channel": "test",
"http_version": "1.1",
"method": "POST",
"path": "/test/",
"body_channel": "test-input",
"headers": {
"content-type": b"multipart/form-data; boundary=BOUNDARY",
"content-length": six.text_type(len(body)).encode("ascii"),
},
})
Channel("test-input").send({
"content": body[:20],
"more_content": True,
})
Channel("test-input").send({
"content": body[20:],
})
request = AsgiRequest(self.get_next_message("test"))
self.assertEqual(request.method, "POST")
self.assertEqual(len(request.body), len(body))
self.assertTrue(request.META["CONTENT_TYPE"].startswith("multipart/form-data"))
self.assertFalse(request._post_parse_error)
self.assertEqual(request.POST["title"], "My First Book")
self.assertEqual(request.FILES["pdf"].read(), b"FAKEPDFBYTESGOHERE")
def test_stream(self):
"""
Tests the body stream is emulated correctly.
"""
Channel("test").send({
"reply_channel": "test",
"http_version": "1.1",
"method": "PUT",
"path": "/",
"body": b"onetwothree",
"headers": {
"host": b"example.com",
"content-length": b"11",
},
})
request = AsgiRequest(self.get_next_message("test", require=True))
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": "/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"))
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": "/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"))