From bac3c14bfa0e07926d8c7e1e59d9f5d050a499de Mon Sep 17 00:00:00 2001 From: Andrew Godwin Date: Fri, 5 Feb 2016 17:23:49 -0800 Subject: [PATCH] Fix python 3 support --- daphne/http_protocol.py | 18 +++++---- daphne/tests/__init__.py | 0 daphne/tests/test_http.py | 77 +++++++++++++++++++++++++++++++++++++++ setup.py | 5 +++ 4 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 daphne/tests/__init__.py create mode 100644 daphne/tests/test_http.py diff --git a/daphne/http_protocol.py b/daphne/http_protocol.py index d89141a..1bebca6 100755 --- a/daphne/http_protocol.py +++ b/daphne/http_protocol.py @@ -70,19 +70,21 @@ class WebRequest(http.Request): self.content.seek(0, 0) # Calculate query string query_string = "" - if "?" in self.uri: - query_string = self.uri.split("?", 1)[1] + if b"?" in self.uri: + query_string = self.uri.split(b"?", 1)[1] # Sanitize headers headers = {} for name, value in self.requestHeaders.getAllRawHeaders(): # Prevent CVE-2015-0219 - if "_" in name: + if b"_" in name: continue - headers[name.lower()] = value[0] + headers[name.lower().decode("latin1")] = value[0] # Send message self.factory.channel_layer.send("http.request", { "reply_channel": self.reply_channel, - "method": self.method, + # TODO: Correctly say if it's 1.1 or 1.0 + "http_version": "1.1", + "method": self.method.decode("ascii"), "path": self.path, "scheme": "http", "query_string": query_string, @@ -110,13 +112,13 @@ class WebRequest(http.Request): raise ValueError("Got multiple Response messages!") self._got_response_start = True # Write code - self.setResponseCode(message['status']) + self.setResponseCode(message['status'], message.get("status_text", None)) # Write headers for header, value in message.get("headers", {}): - self.setHeader(header.encode("utf8"), value.encode("utf8")) + self.setHeader(header.encode("utf8"), value) # Write out body if "content" in message: - http.Request.write(self, message['content'].encode("utf8")) + http.Request.write(self, message['content']) # End if there's no more content if not message.get("more_content", False): self.finish() diff --git a/daphne/tests/__init__.py b/daphne/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/daphne/tests/test_http.py b/daphne/tests/test_http.py new file mode 100644 index 0000000..73c6340 --- /dev/null +++ b/daphne/tests/test_http.py @@ -0,0 +1,77 @@ +from __future__ import unicode_literals +from unittest import TestCase +from asgiref.inmemory import ChannelLayer +from twisted.test import proto_helpers + +from ..http_protocol import HTTPFactory + + +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") diff --git a/setup.py b/setup.py index 43b1eaf..d53cc05 100755 --- a/setup.py +++ b/setup.py @@ -20,6 +20,11 @@ setup( zip_safe=False, packages=find_packages(), include_package_data=True, + install_requires=[ + 'asgiref', + 'twisted>=15.5', + 'autobahn>=0.12', + ], entry_points={'console_scripts': [ 'daphne = daphne.cli:CommandLineInterface.entrypoint', ]},