class TestHTTPResponse(TestCase): """ Tests that the HTTP protocol class correctly generates and parses messages. """ def setUp(self): self.channel_layer = ChannelLayer() self.factory = HTTPFactory(self.channel_layer, send_channel="test!") self.proto = self.factory.buildProtocol(('127.0.0.1', 0)) self.tr = proto_helpers.StringTransport() self.proto.makeConnection(self.tr) def test_http_disconnect_sets_path_key(self): """ Tests http disconnect has the path key set, see https://channels.readthedocs.io/en/latest/asgi.html#disconnect """ # Send a simple request to the protocol self.proto.dataReceived(b"GET /te%20st-%C3%A0/?foo=bar HTTP/1.1\r\n" + b"Host: anywhere.com\r\n" + b"\r\n") # Get the request message _, message = self.channel_layer.receive(["http.request"]) # Send back an example response self.factory.dispatch_reply(message['reply_channel'], { "status": 200, "status_text": b"OK", "content": b"DISCO", }) # Get the disconnection notification _, disconnect_message = self.channel_layer.receive(["http.disconnect"]) self.assertEqual(disconnect_message['path'], "/te st-à/")
class TestHTTPProtocol(TestCase): """ Tests that the HTTP protocol class correctly generates and parses messages. """ def setUp(self): self.channel_layer = ChannelLayer() self.factory = HTTPFactory(self.channel_layer) self.proto = self.factory.buildProtocol(('127.0.0.1', 0)) self.tr = proto_helpers.StringTransport() self.proto.makeConnection(self.tr) def test_basic(self): """ Tests basic HTTP parsing """ # Send a simple request to the protocol self.proto.dataReceived( b"GET /te%20st-%C3%A0/?foo=+bar HTTP/1.1\r\n" + b"Host: somewhere.com\r\n" + b"\r\n" ) # Get the resulting message off of the channel layer _, message = self.channel_layer.receive_many(["http.request"]) self.assertEqual(message['http_version'], "1.1") self.assertEqual(message['method'], "GET") self.assertEqual(message['scheme'], "http") self.assertEqual(message['path'], "/te st-à/") self.assertEqual(message['query_string'], b"foo=+bar") self.assertEqual(message['headers'], [(b"host", b"somewhere.com")]) self.assertFalse(message.get("body", None)) self.assertTrue(message['reply_channel']) # Send back an example response self.factory.dispatch_reply( message['reply_channel'], { "status": 201, "status_text": b"Created", "content": b"OH HAI", "headers": [[b"X-Test", b"Boom!"]], } ) # Make sure that comes back right on the protocol self.assertEqual(self.tr.value(), b"HTTP/1.1 201 Created\r\nTransfer-Encoding: chunked\r\nX-Test: Boom!\r\n\r\n6\r\nOH HAI\r\n0\r\n\r\n") def test_root_path_header(self): """ Tests root path header handling """ # Send a simple request to the protocol self.proto.dataReceived( b"GET /te%20st-%C3%A0/?foo=bar HTTP/1.1\r\n" + b"Host: somewhere.com\r\n" + b"Daphne-Root-Path: /foobar%20/bar\r\n" + b"\r\n" ) # Get the resulting message off of the channel layer, check root_path _, message = self.channel_layer.receive_many(["http.request"]) self.assertEqual(message['root_path'], "/foobar /bar")
def setUp(self): """ Make an in memory channel layer for testing """ self.channel_layer = ChannelLayer() self.reply_channel = "http.response.test" self.start_response_value = None self.application = TestWsgiToAsgiAdapter(self.channel_layer)
def __init__(self): self.last_message = None self.channel_layer = ChannelLayer() self.factory = HTTPFactory(self.channel_layer, send_channel="test!") self.proto = self.factory.buildProtocol(('127.0.0.1', 0)) self.transport = proto_helpers.StringTransport() self.proto.makeConnection(self.transport)
class TestHTTPProtocol(TestCase): """ Tests that the HTTP protocol class correctly generates and parses messages. """ def setUp(self): self.channel_layer = ChannelLayer() self.factory = HTTPFactory(self.channel_layer) self.proto = self.factory.buildProtocol(('127.0.0.1', 0)) self.tr = proto_helpers.StringTransport() self.proto.makeConnection(self.tr) def test_basic(self): """ Tests basic HTTP parsing """ # Send a simple request to the protocol self.proto.dataReceived(b"GET /te%20st-%C3%A0/?foo=+bar HTTP/1.1\r\n" + b"Host: somewhere.com\r\n" + b"\r\n") # Get the resulting message off of the channel layer _, message = self.channel_layer.receive_many(["http.request"]) self.assertEqual(message['http_version'], "1.1") self.assertEqual(message['method'], "GET") self.assertEqual(message['scheme'], "http") self.assertEqual(message['path'], "/te st-à/") self.assertEqual(message['query_string'], b"foo=+bar") self.assertEqual(message['headers'], [(b"host", b"somewhere.com")]) self.assertFalse(message.get("body", None)) self.assertTrue(message['reply_channel']) # Send back an example response self.factory.dispatch_reply( message['reply_channel'], { "status": 201, "status_text": b"Created", "content": b"OH HAI", "headers": [[b"X-Test", b"Boom!"]], }) # Make sure that comes back right on the protocol self.assertEqual( self.tr.value(), b"HTTP/1.1 201 Created\r\nTransfer-Encoding: chunked\r\nX-Test: Boom!\r\n\r\n6\r\nOH HAI\r\n0\r\n\r\n" ) def test_root_path_header(self): """ Tests root path header handling """ # Send a simple request to the protocol self.proto.dataReceived(b"GET /te%20st-%C3%A0/?foo=bar HTTP/1.1\r\n" + b"Host: somewhere.com\r\n" + b"Daphne-Root-Path: /foobar%20/bar\r\n" + b"\r\n") # Get the resulting message off of the channel layer, check root_path _, message = self.channel_layer.receive_many(["http.request"]) self.assertEqual(message['root_path'], "/foobar /bar")
class TestProxyHandling(unittest.TestCase): """ Tests that concern interaction of Daphne with proxies. They live in a separate test case, because they're not part of the spec. """ def setUp(self): self.channel_layer = ChannelLayer() self.factory = HTTPFactory(self.channel_layer) self.proto = self.factory.buildProtocol(('127.0.0.1', 0)) self.tr = proto_helpers.StringTransport() self.proto.makeConnection(self.tr) def test_x_forwarded_for_ignored(self): self.proto.dataReceived( b"GET /te%20st-%C3%A0/?foo=+bar HTTP/1.1\r\n" + b"Host: somewhere.com\r\n" + b"X-Forwarded-For: 10.1.2.3\r\n" + b"X-Forwarded-Port: 80\r\n" + b"\r\n" ) # Get the resulting message off of the channel layer _, message = self.channel_layer.receive(["http.request"]) self.assertEqual(message['client'], ['192.168.1.1', 54321]) def test_x_forwarded_for_parsed(self): self.factory.proxy_forwarded_address_header = 'X-Forwarded-For' self.factory.proxy_forwarded_port_header = 'X-Forwarded-Port' self.proto.dataReceived( b"GET /te%20st-%C3%A0/?foo=+bar HTTP/1.1\r\n" + b"Host: somewhere.com\r\n" + b"X-Forwarded-For: 10.1.2.3\r\n" + b"X-Forwarded-Port: 80\r\n" + b"\r\n" ) # Get the resulting message off of the channel layer _, message = self.channel_layer.receive(["http.request"]) self.assertEqual(message['client'], ['10.1.2.3', 80]) def test_x_forwarded_for_port_missing(self): self.factory.proxy_forwarded_address_header = 'X-Forwarded-For' self.factory.proxy_forwarded_port_header = 'X-Forwarded-Port' self.proto.dataReceived( b"GET /te%20st-%C3%A0/?foo=+bar HTTP/1.1\r\n" + b"Host: somewhere.com\r\n" + b"X-Forwarded-For: 10.1.2.3\r\n" + b"\r\n" ) # Get the resulting message off of the channel layer _, message = self.channel_layer.receive(["http.request"]) self.assertEqual(message['client'], ['10.1.2.3', 0])
class WsgiToAsgiTests(TestCase): """ Tests the WSGI-to-ASGI adapter. """ def setUp(self): """ Make an in memory channel layer for testing """ self.channel_layer = ChannelLayer() self.reply_channel = "http.response.test" self.start_response_value = None self.application = TestWsgiToAsgiAdapter(self.channel_layer) def native_string(self, value): """ Makes sure that the passed in string value comes out as a PEP3333 "native string". """ if six.PY2: if isinstance(value, unicode): return value.encode("latin1") else: return value else: if isinstance(value, bytes): return value.decode("latin1") else: return value def start_response(self, status, headers, exc_info=None): self.start_response_value = [status, headers, exc_info] def test_basic(self): # Example request ns = self.native_string environ = { "PATH_INFO": ns("/"), "CONTENT_TYPE": ns("text/html; charset=utf-8"), "REQUEST_METHOD": ns("GET"), } # Inject the response ahead of time self.channel_layer.send(self.reply_channel, { "status": 200, "content": b"Hi there!", }) # Run WSGI adapter response = list(self.application(environ, self.start_response)) # Make sure response looks fine self.assertEqual(response[0], b"Hi there!") self.assertEqual(self.start_response_value[0], b"200 OK")
def _run_through_daphne(request, channel_name): """ Returns Daphne's channel message for a given request. This helper requires a fair bit of scaffolding and can certainly be improved, but it works for now. """ channel_layer = ChannelLayer() factory = HTTPFactory(channel_layer, send_channel="test!") proto = factory.buildProtocol(('127.0.0.1', 0)) transport = proto_helpers.StringTransport() proto.makeConnection(transport) proto.dataReceived(request) _, message = channel_layer.receive([channel_name]) return message, factory, transport
def factory(name, **content): channel_layer = ChannelLayer() message = Message(content, name, channel_layer) settings.SESSION_FILE_PATH = str(tmpdir) message.channel_session = SessionStore() return message
class InMemoryLayerTests(ConformanceTestCase): channel_layer = ChannelLayer(expiry=1, group_expiry=2) expiry_delay = 1.1 def test_group_message_eviction(self): """ Tests that when messages expire, group expiry also occurs. """ # Add things to a group and send a message that should expire self.channel_layer.group_add("tgme_group", "tgme_test") self.channel_layer.send_group("tgme_group", {"value": "blue"}) # Wait message expiry plus a tiny bit (must sum to less than group expiry) time.sleep(1.2) # Send new message to group, ensure message never arrives self.channel_layer.send_group("tgme_group", {"value": "blue"}) channel, message = self.channel_layer.receive_many(["tgme_test"]) self.assertIs(channel, None) self.assertIs(message, None)
class WebSocketConnection(object): """ Helper class that makes it easier to test Dahpne's WebSocket support. """ def __init__(self): self.last_message = None self.channel_layer = ChannelLayer() self.factory = HTTPFactory(self.channel_layer, send_channel="test!") self.proto = self.factory.buildProtocol(('127.0.0.1', 0)) self.transport = proto_helpers.StringTransport() self.proto.makeConnection(self.transport) def receive(self, request): """ Low-level method to let Daphne handle HTTP/WebSocket data """ self.proto.dataReceived(request) _, self.last_message = self.channel_layer.receive( ['websocket.connect']) return self.last_message def send(self, content): """ Method to respond with a channel message """ if self.last_message is None: # Auto-connect for convenience. self.connect() self.factory.dispatch_reply(self.last_message['reply_channel'], content) response = self.transport.value() self.transport.clear() return response def connect(self, path='/', params=None, headers=None): """ High-level method to perform the WebSocket handshake """ request = factories.build_websocket_upgrade(path, params, headers or []) message = self.receive(request) return message
def setUp(self): self.channel_layer = ChannelLayer() self.factory = HTTPFactory(self.channel_layer) self.proto = self.factory.buildProtocol(('127.0.0.1', 0)) self.tr = proto_helpers.StringTransport() self.proto.makeConnection(self.tr)
class RequestTests(SimpleTestCase): """ Tests that ASGI request handling correctly decodes HTTP requests. """ def setUp(self): """ Make an in memory channel layer for testing """ self.channel_layer = ChannelLayer() self.make_message = lambda m, c: Message(m, c, self.channel_layer) def test_basic(self): """ Tests that the handler can decode the most basic request message, with all optional fields omitted. """ message = self.make_message( { "reply_channel": "test-reply", "http_version": "1.1", "method": "GET", "path": "/test/", }, "test") request = AsgiRequest(message) 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 """ message = self.make_message( { "reply_channel": "test", "http_version": "1.1", "method": "GET", "path": "/test2/", "query_string": b"x=1&y=foo%20bar+baz", "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], }, "test") request = AsgiRequest(message) 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. """ message = self.make_message( { "reply_channel": "test", "http_version": "1.1", "method": "POST", "path": "/test2/", "query_string": b"django=great", "body": b"ponies=are+awesome", "headers": { "host": b"example.com", "content-type": b"application/x-www-form-urlencoded", "content-length": b"18", }, }, "test") request = AsgiRequest(message) 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'). """ message = self.make_message( { "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", }, }, "test") self.channel_layer.send("test-input", { "content": b"re=fou", "more_content": True, }) self.channel_layer.send("test-input", { "content": b"r+lights", }) request = AsgiRequest(message) 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--') message = self.make_message( { "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"), }, }, "test") self.channel_layer.send("test-input", { "content": body[:20], "more_content": True, }) self.channel_layer.send("test-input", { "content": body[20:], }) request = AsgiRequest(message) 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")
class TestHTTPProtocol(TestCase): """ Tests that the HTTP protocol class correctly generates and parses messages. """ def setUp(self): self.channel_layer = ChannelLayer() self.factory = HTTPFactory(self.channel_layer) self.proto = self.factory.buildProtocol(('127.0.0.1', 0)) self.tr = proto_helpers.StringTransport() self.proto.makeConnection(self.tr) def test_basic(self): """ Tests basic HTTP parsing """ # Send a simple request to the protocol self.proto.dataReceived(b"GET /te%20st-%C3%A0/?foo=+bar HTTP/1.1\r\n" + b"Host: somewhere.com\r\n" + b"\r\n") # Get the resulting message off of the channel layer _, message = self.channel_layer.receive_many(["http.request"]) self.assertEqual(message['http_version'], "1.1") self.assertEqual(message['method'], "GET") self.assertEqual(message['scheme'], "http") self.assertEqual(message['path'], "/te st-à/") self.assertEqual(message['query_string'], b"foo=+bar") self.assertEqual(message['headers'], [(b"host", b"somewhere.com")]) self.assertFalse(message.get("body", None)) self.assertTrue(message['reply_channel']) # Send back an example response self.factory.dispatch_reply( message['reply_channel'], { "status": 201, "status_text": b"Created", "content": b"OH HAI", "headers": [[b"X-Test", b"Boom!"]], }) # Make sure that comes back right on the protocol self.assertEqual( self.tr.value(), b"HTTP/1.1 201 Created\r\nTransfer-Encoding: chunked\r\nX-Test: Boom!\r\n\r\n6\r\nOH HAI\r\n0\r\n\r\n" ) def test_root_path_header(self): """ Tests root path header handling """ # Send a simple request to the protocol self.proto.dataReceived(b"GET /te%20st-%C3%A0/?foo=bar HTTP/1.1\r\n" + b"Host: somewhere.com\r\n" + b"Daphne-Root-Path: /foobar%20/bar\r\n" + b"\r\n") # Get the resulting message off of the channel layer, check root_path _, message = self.channel_layer.receive_many(["http.request"]) self.assertEqual(message['root_path'], "/foobar /bar") def test_http_disconnect_sets_path_key(self): """ Tests http disconnect has the path key set, see https://channels.readthedocs.io/en/latest/asgi.html#disconnect """ # Send a simple request to the protocol self.proto.dataReceived(b"GET /te%20st-%C3%A0/?foo=bar HTTP/1.1\r\n" + b"Host: anywhere.com\r\n" + b"\r\n") # Get the request message _, message = self.channel_layer.receive_many(["http.request"]) # Send back an example response self.factory.dispatch_reply(message['reply_channel'], { "status": 200, "status_text": b"OK", "content": b"DISCO", }) # Get the disconnection notification _, disconnect_message = self.channel_layer.receive_many( ["http.disconnect"]) self.assertEqual(disconnect_message['path'], "/te st-à/") def test_x_forwarded_for_ignored(self): """ Tests basic HTTP parsing """ self.proto.dataReceived(b"GET /te%20st-%C3%A0/?foo=+bar HTTP/1.1\r\n" + b"Host: somewhere.com\r\n" + b"X-Forwarded-For: 10.1.2.3\r\n" + b"X-Forwarded-Port: 80\r\n" + b"\r\n") # Get the resulting message off of the channel layer _, message = self.channel_layer.receive_many(["http.request"]) self.assertEqual(message['client'], ['192.168.1.1', 54321]) def test_x_forwarded_for_parsed(self): """ Tests basic HTTP parsing """ self.factory.proxy_forwarded_address_header = 'X-Forwarded-For' self.factory.proxy_forwarded_port_header = 'X-Forwarded-Port' self.proto.dataReceived(b"GET /te%20st-%C3%A0/?foo=+bar HTTP/1.1\r\n" + b"Host: somewhere.com\r\n" + b"X-Forwarded-For: 10.1.2.3\r\n" + b"X-Forwarded-Port: 80\r\n" + b"\r\n") # Get the resulting message off of the channel layer _, message = self.channel_layer.receive_many(["http.request"]) self.assertEqual(message['client'], ['10.1.2.3', 80]) def test_x_forwarded_for_port_missing(self): """ Tests basic HTTP parsing """ self.factory.proxy_forwarded_address_header = 'X-Forwarded-For' self.factory.proxy_forwarded_port_header = 'X-Forwarded-Port' self.proto.dataReceived(b"GET /te%20st-%C3%A0/?foo=+bar HTTP/1.1\r\n" + b"Host: somewhere.com\r\n" + b"X-Forwarded-For: 10.1.2.3\r\n" + b"\r\n") # Get the resulting message off of the channel layer _, message = self.channel_layer.receive_many(["http.request"]) self.assertEqual(message['client'], ['10.1.2.3', 0])
class RequestTests(SimpleTestCase): """ Tests that ASGI request handling correctly decodes HTTP requests. """ def setUp(self): """ Make an in memory channel layer for testing """ self.channel_layer = ChannelLayer() self.make_message = lambda m, c: Message(m, c, self.channel_layer) def test_basic(self): """ Tests that the handler can decode the most basic request message, with all optional fields omitted. """ message = self.make_message({ "reply_channel": "test-reply", "http_version": "1.1", "method": "GET", "path": "/test/", }, "test") request = AsgiRequest(message) 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 """ message = self.make_message({ "reply_channel": "test", "http_version": "1.1", "method": "GET", "path": "/test2/", "query_string": b"x=1&y=foo%20bar+baz", "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], }, "test") request = AsgiRequest(message) 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. """ message = self.make_message({ "reply_channel": "test", "http_version": "1.1", "method": "POST", "path": "/test2/", "query_string": b"django=great", "body": b"ponies=are+awesome", "headers": { "host": b"example.com", "content-type": b"application/x-www-form-urlencoded", "content-length": b"18", }, }, "test") request = AsgiRequest(message) 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'). """ message = self.make_message({ "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", }, }, "test") self.channel_layer.send("test-input", { "content": b"re=fou", "more_content": True, }) self.channel_layer.send("test-input", { "content": b"r+lights", }) request = AsgiRequest(message) 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--' ) message = self.make_message({ "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"), }, }, "test") self.channel_layer.send("test-input", { "content": body[:20], "more_content": True, }) self.channel_layer.send("test-input", { "content": body[20:], }) request = AsgiRequest(message) 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 setUp(self): """ Make an in memory channel layer for testing """ self.channel_layer = ChannelLayer() self.make_message = lambda m, c: Message(m, c, self.channel_layer)
from asgiref.inmemory import ChannelLayer from asgiref.conformance import make_tests channel_layer = ChannelLayer(expiry=1) InMemoryTests = make_tests(channel_layer, expiry_delay=1.1)
class TestHTTPProtocol(TestCase): """ Tests that the HTTP protocol class correctly generates and parses messages. """ def setUp(self): self.channel_layer = ChannelLayer() self.factory = HTTPFactory(self.channel_layer) self.proto = self.factory.buildProtocol(('127.0.0.1', 0)) self.tr = proto_helpers.StringTransport() self.proto.makeConnection(self.tr) def assertStartsWith(self, data, prefix): real_prefix = data[:len(prefix)] self.assertEqual(real_prefix, prefix) def test_basic(self): """ Tests basic HTTP parsing """ # Send a simple request to the protocol self.proto.dataReceived(b"GET /test/?foo=bar HTTP/1.1\r\n" + b"Host: somewhere.com\r\n" + b"\r\n") # Get the resulting message off of the channel layer _, message = self.channel_layer.receive_many(["http.request"]) self.assertEqual(message['http_version'], "1.1") self.assertEqual(message['method'], "GET") self.assertEqual(message['scheme'], "http") self.assertEqual(message['path'], b"/test/") self.assertEqual(message['query_string'], b"foo=bar") self.assertEqual(message['headers'], {"host": b"somewhere.com"}) self.assertFalse(message.get("body", None)) self.assertTrue(message['reply_channel']) # Send back an example response self.factory.dispatch_reply( message['reply_channel'], { "status": 201, "status_text": b"Created", "content": b"OH HAI", "headers": [["X-Test", b"Boom!"]], }) # Make sure that comes back right on the protocol self.assertEqual( self.tr.value(), b"HTTP/1.1 201 Created\r\nTransfer-Encoding: chunked\r\nX-Test: Boom!\r\n\r\n6\r\nOH HAI\r\n0\r\n\r\n" ) def test_custom_status_text(self): """ Tests basic HTTP parsing """ # Send a simple request to the protocol self.proto.dataReceived(b"GET /test/?foo=bar HTTP/1.0\r\n" + b"\r\n") # Send back an example response _, message = self.channel_layer.receive_many(["http.request"]) self.factory.dispatch_reply(message['reply_channel'], { "status": 484, "status_text": b"Daphne Is Awesome", }) # Make sure that comes back right on the protocol self.assertStartsWith(self.tr.value(), b"HTTP/1.0 484 Daphne Is Awesome\r\n")
class TestWebSocketProtocol(TestCase): """ Tests that the WebSocket protocol class correcly generates and parses messages. """ def setUp(self): self.channel_layer = ChannelLayer() self.factory = HTTPFactory(self.channel_layer, send_channel="test!") self.proto = self.factory.buildProtocol(('127.0.0.1', 0)) self.tr = proto_helpers.StringTransport() self.proto.makeConnection(self.tr) def test_basic(self): # Send a simple request to the protocol self.proto.dataReceived( b"GET /chat HTTP/1.1\r\n" b"Host: somewhere.com\r\n" b"Upgrade: websocket\r\n" b"Connection: Upgrade\r\n" b"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n" b"Sec-WebSocket-Protocol: chat, superchat\r\n" b"Sec-WebSocket-Version: 13\r\n" b"Origin: http://example.com\r\n" b"\r\n") # Get the resulting message off of the channel layer _, message = self.channel_layer.receive(["websocket.connect"]) self.assertEqual(message['path'], "/chat") self.assertEqual(message['query_string'], "") self.assertEqual(sorted(message['headers']), [(b'connection', b'Upgrade'), (b'host', b'somewhere.com'), (b'origin', b'http://example.com'), (b'sec-websocket-key', b'x3JJHMbDL1EzLkh9GBhXDw=='), (b'sec-websocket-protocol', b'chat, superchat'), (b'sec-websocket-version', b'13'), (b'upgrade', b'websocket')]) self.assertTrue(message['reply_channel'].startswith("test!")) # Accept the connection self.factory.dispatch_reply(message['reply_channel'], {'accept': True}) # Make sure that we get a 101 Switching Protocols back response = self.tr.value() self.assertIn(b"HTTP/1.1 101 Switching Protocols\r\n", response) self.assertIn( b"Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=\r\n", response) self.tr.clear() # Send some text self.factory.dispatch_reply(message['reply_channel'], {'text': "Hello World!"}) response = self.tr.value() self.assertEqual(response, b"\x81\x0cHello World!") self.tr.clear() # Send some bytes self.factory.dispatch_reply(message['reply_channel'], {'bytes': b"\xaa\xbb\xcc\xdd"}) response = self.tr.value() self.assertEqual(response, b"\x82\x04\xaa\xbb\xcc\xdd") self.tr.clear() # Close the connection self.factory.dispatch_reply(message['reply_channel'], {'close': True}) response = self.tr.value() self.assertEqual(response, b"\x88\x02\x03\xe8") self.tr.clear() def test_connection_with_file_origin_is_accepted(self): # Send a simple request to the protocol self.proto.dataReceived( b"GET /chat HTTP/1.1\r\n" b"Host: somewhere.com\r\n" b"Upgrade: websocket\r\n" b"Connection: Upgrade\r\n" b"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n" b"Sec-WebSocket-Protocol: chat, superchat\r\n" b"Sec-WebSocket-Version: 13\r\n" b"Origin: file://\r\n" b"\r\n") # Get the resulting message off of the channel layer _, message = self.channel_layer.receive(["websocket.connect"]) self.assertIn((b'origin', b'file://'), message['headers']) self.assertTrue(message['reply_channel'].startswith("test!")) # Accept the connection self.factory.dispatch_reply(message['reply_channel'], {'accept': True}) # Make sure that we get a 101 Switching Protocols back response = self.tr.value() self.assertIn(b"HTTP/1.1 101 Switching Protocols\r\n", response) self.assertIn( b"Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=\r\n", response) def test_connection_with_no_origin_is_accepted(self): # Send a simple request to the protocol self.proto.dataReceived( b"GET /chat HTTP/1.1\r\n" b"Host: somewhere.com\r\n" b"Upgrade: websocket\r\n" b"Connection: Upgrade\r\n" b"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n" b"Sec-WebSocket-Protocol: chat, superchat\r\n" b"Sec-WebSocket-Version: 13\r\n" b"\r\n") # Get the resulting message off of the channel layer _, message = self.channel_layer.receive(["websocket.connect"]) self.assertNotIn( b'origin', [header_tuple[0] for header_tuple in message['headers']]) self.assertTrue(message['reply_channel'].startswith("test!")) # Accept the connection self.factory.dispatch_reply(message['reply_channel'], {'accept': True}) # Make sure that we get a 101 Switching Protocols back response = self.tr.value() self.assertIn(b"HTTP/1.1 101 Switching Protocols\r\n", response) self.assertIn( b"Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=\r\n", response)
class TestWebSocketProtocol(TestCase): """ Tests that the WebSocket protocol class correctly generates and parses messages. """ def setUp(self): self.channel_layer = ChannelLayer() self.factory = HTTPFactory(self.channel_layer) self.proto = self.factory.buildProtocol(('127.0.0.1', 0)) self.tr = proto_helpers.StringTransport() self.proto.makeConnection(self.tr) def test_basic(self): # Send a simple request to the protocol self.proto.dataReceived( b"GET /chat HTTP/1.1\r\n" b"Host: somewhere.com\r\n" b"Upgrade: websocket\r\n" b"Connection: Upgrade\r\n" b"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n" b"Sec-WebSocket-Protocol: chat, superchat\r\n" b"Sec-WebSocket-Version: 13\r\n" b"Origin: http://example.com\r\n" b"\r\n" ) # Get the resulting message off of the channel layer _, message = self.channel_layer.receive(["websocket.connect"]) self.assertEqual(message['path'], "/chat") self.assertEqual(message['query_string'], "") self.assertEqual( sorted(message['headers']), [(b'connection', b'Upgrade'), (b'host', b'somewhere.com'), (b'origin', b'http://example.com'), (b'sec-websocket-key', b'x3JJHMbDL1EzLkh9GBhXDw=='), (b'sec-websocket-protocol', b'chat, superchat'), (b'sec-websocket-version', b'13'), (b'upgrade', b'websocket')] ) self.assertTrue(message['reply_channel'].startswith("websocket.send!")) # Accept the connection self.factory.dispatch_reply( message['reply_channel'], {'accept': True} ) # Make sure that we get a 101 Switching Protocols back response = self.tr.value() self.assertIn(b"HTTP/1.1 101 Switching Protocols\r\n", response) self.assertIn(b"Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=\r\n", response) self.tr.clear() # Send some text self.factory.dispatch_reply( message['reply_channel'], {'text': "Hello World!"} ) response = self.tr.value() self.assertEqual(response, b"\x81\x0cHello World!") self.tr.clear() # Send some bytes self.factory.dispatch_reply( message['reply_channel'], {'bytes': b"\xaa\xbb\xcc\xdd"} ) response = self.tr.value() self.assertEqual(response, b"\x82\x04\xaa\xbb\xcc\xdd") self.tr.clear() # Close the connection self.factory.dispatch_reply( message['reply_channel'], {'close': True} ) response = self.tr.value() self.assertEqual(response, b"\x88\x02\x03\xe8") self.tr.clear() def test_connection_with_file_origin_is_accepted(self): # Send a simple request to the protocol self.proto.dataReceived( b"GET /chat HTTP/1.1\r\n" b"Host: somewhere.com\r\n" b"Upgrade: websocket\r\n" b"Connection: Upgrade\r\n" b"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n" b"Sec-WebSocket-Protocol: chat, superchat\r\n" b"Sec-WebSocket-Version: 13\r\n" b"Origin: file://\r\n" b"\r\n" ) # Get the resulting message off of the channel layer _, message = self.channel_layer.receive(["websocket.connect"]) self.assertIn((b'origin', b'file://'), message['headers']) self.assertTrue(message['reply_channel'].startswith("websocket.send!")) # Accept the connection self.factory.dispatch_reply( message['reply_channel'], {'accept': True} ) # Make sure that we get a 101 Switching Protocols back response = self.tr.value() self.assertIn(b"HTTP/1.1 101 Switching Protocols\r\n", response) self.assertIn(b"Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=\r\n", response) def test_connection_with_no_origin_is_accepted(self): # Send a simple request to the protocol self.proto.dataReceived( b"GET /chat HTTP/1.1\r\n" b"Host: somewhere.com\r\n" b"Upgrade: websocket\r\n" b"Connection: Upgrade\r\n" b"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n" b"Sec-WebSocket-Protocol: chat, superchat\r\n" b"Sec-WebSocket-Version: 13\r\n" b"\r\n" ) # Get the resulting message off of the channel layer _, message = self.channel_layer.receive(["websocket.connect"]) self.assertNotIn(b'origin', [header_tuple[0] for header_tuple in message['headers']]) self.assertTrue(message['reply_channel'].startswith("websocket.send!")) # Accept the connection self.factory.dispatch_reply( message['reply_channel'], {'accept': True} ) # Make sure that we get a 101 Switching Protocols back response = self.tr.value() self.assertIn(b"HTTP/1.1 101 Switching Protocols\r\n", response) self.assertIn(b"Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=\r\n", response)
class TestHTTPProtocol(TestCase): """ Tests that the HTTP protocol class correctly generates and parses messages. """ def setUp(self): self.channel_layer = ChannelLayer() self.factory = HTTPFactory(self.channel_layer) self.proto = self.factory.buildProtocol(('127.0.0.1', 0)) self.tr = proto_helpers.StringTransport() self.proto.makeConnection(self.tr) def test_basic(self): """ Tests basic HTTP parsing """ # Send a simple request to the protocol self.proto.dataReceived( b"GET /te%20st-%C3%A0/?foo=+bar HTTP/1.1\r\n" + b"Host: somewhere.com\r\n" + b"\r\n" ) # Get the resulting message off of the channel layer _, message = self.channel_layer.receive(["http.request"]) self.assertEqual(message['http_version'], "1.1") self.assertEqual(message['method'], "GET") self.assertEqual(message['scheme'], "http") self.assertEqual(message['path'], "/te st-à/") self.assertEqual(message['query_string'], b"foo=+bar") self.assertEqual(message['headers'], [(b"host", b"somewhere.com")]) self.assertFalse(message.get("body", None)) self.assertTrue(message['reply_channel']) # Send back an example response self.factory.dispatch_reply( message['reply_channel'], { "status": 201, "status_text": b"Created", "content": b"OH HAI", "headers": [[b"X-Test", b"Boom!"]], } ) # Make sure that comes back right on the protocol self.assertEqual(self.tr.value(), b"HTTP/1.1 201 Created\r\nTransfer-Encoding: chunked\r\nX-Test: Boom!\r\n\r\n6\r\nOH HAI\r\n0\r\n\r\n") def test_root_path_header(self): """ Tests root path header handling """ # Send a simple request to the protocol self.proto.dataReceived( b"GET /te%20st-%C3%A0/?foo=bar HTTP/1.1\r\n" + b"Host: somewhere.com\r\n" + b"Daphne-Root-Path: /foobar%20/bar\r\n" + b"\r\n" ) # Get the resulting message off of the channel layer, check root_path _, message = self.channel_layer.receive(["http.request"]) self.assertEqual(message['root_path'], "/foobar /bar") def test_http_disconnect_sets_path_key(self): """ Tests http disconnect has the path key set, see https://channels.readthedocs.io/en/latest/asgi.html#disconnect """ # Send a simple request to the protocol self.proto.dataReceived( b"GET /te%20st-%C3%A0/?foo=bar HTTP/1.1\r\n" + b"Host: anywhere.com\r\n" + b"\r\n" ) # Get the request message _, message = self.channel_layer.receive(["http.request"]) # Send back an example response self.factory.dispatch_reply( message['reply_channel'], { "status": 200, "status_text": b"OK", "content": b"DISCO", } ) # Get the disconnection notification _, disconnect_message = self.channel_layer.receive(["http.disconnect"]) self.assertEqual(disconnect_message['path'], "/te st-à/") def test_x_forwarded_for_ignored(self): """ Tests basic HTTP parsing """ self.proto.dataReceived( b"GET /te%20st-%C3%A0/?foo=+bar HTTP/1.1\r\n" + b"Host: somewhere.com\r\n" + b"X-Forwarded-For: 10.1.2.3\r\n" + b"X-Forwarded-Port: 80\r\n" + b"\r\n" ) # Get the resulting message off of the channel layer _, message = self.channel_layer.receive(["http.request"]) self.assertEqual(message['client'], ['192.168.1.1', 54321]) def test_x_forwarded_for_parsed(self): """ Tests basic HTTP parsing """ self.factory.proxy_forwarded_address_header = 'X-Forwarded-For' self.factory.proxy_forwarded_port_header = 'X-Forwarded-Port' self.proto.dataReceived( b"GET /te%20st-%C3%A0/?foo=+bar HTTP/1.1\r\n" + b"Host: somewhere.com\r\n" + b"X-Forwarded-For: 10.1.2.3\r\n" + b"X-Forwarded-Port: 80\r\n" + b"\r\n" ) # Get the resulting message off of the channel layer _, message = self.channel_layer.receive(["http.request"]) self.assertEqual(message['client'], ['10.1.2.3', 80]) def test_x_forwarded_for_port_missing(self): """ Tests basic HTTP parsing """ self.factory.proxy_forwarded_address_header = 'X-Forwarded-For' self.factory.proxy_forwarded_port_header = 'X-Forwarded-Port' self.proto.dataReceived( b"GET /te%20st-%C3%A0/?foo=+bar HTTP/1.1\r\n" + b"Host: somewhere.com\r\n" + b"X-Forwarded-For: 10.1.2.3\r\n" + b"\r\n" ) # Get the resulting message off of the channel layer _, message = self.channel_layer.receive(["http.request"]) self.assertEqual(message['client'], ['10.1.2.3', 0])