Unify all strings to double quotes

This commit is contained in:
Andrew Godwin 2017-11-25 13:40:15 -08:00
parent 22aa56e196
commit 0626f39214
16 changed files with 346 additions and 346 deletions

View File

@ -17,34 +17,34 @@ class AccessLogGenerator(object):
# HTTP requests # HTTP requests
if protocol == "http" and action == "complete": if protocol == "http" and action == "complete":
self.write_entry( self.write_entry(
host=details['client'], host=details["client"],
date=datetime.datetime.now(), date=datetime.datetime.now(),
request="%(method)s %(path)s" % details, request="%(method)s %(path)s" % details,
status=details['status'], status=details["status"],
length=details['size'], length=details["size"],
) )
# Websocket requests # Websocket requests
elif protocol == "websocket" and action == "connecting": elif protocol == "websocket" and action == "connecting":
self.write_entry( self.write_entry(
host=details['client'], host=details["client"],
date=datetime.datetime.now(), date=datetime.datetime.now(),
request="WSCONNECTING %(path)s" % details, request="WSCONNECTING %(path)s" % details,
) )
elif protocol == "websocket" and action == "rejected": elif protocol == "websocket" and action == "rejected":
self.write_entry( self.write_entry(
host=details['client'], host=details["client"],
date=datetime.datetime.now(), date=datetime.datetime.now(),
request="WSREJECT %(path)s" % details, request="WSREJECT %(path)s" % details,
) )
elif protocol == "websocket" and action == "connected": elif protocol == "websocket" and action == "connected":
self.write_entry( self.write_entry(
host=details['client'], host=details["client"],
date=datetime.datetime.now(), date=datetime.datetime.now(),
request="WSCONNECT %(path)s" % details, request="WSCONNECT %(path)s" % details,
) )
elif protocol == "websocket" and action == "disconnected": elif protocol == "websocket" and action == "disconnected":
self.write_entry( self.write_entry(
host=details['client'], host=details["client"],
date=datetime.datetime.now(), date=datetime.datetime.now(),
request="WSDISCONNECT %(path)s" % details, request="WSDISCONNECT %(path)s" % details,
) )

View File

@ -10,7 +10,7 @@ from .utils import import_by_path
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
DEFAULT_HOST = '127.0.0.1' DEFAULT_HOST = "127.0.0.1"
DEFAULT_PORT = 8000 DEFAULT_PORT = 8000
class CommandLineInterface(object): class CommandLineInterface(object):
@ -27,108 +27,108 @@ class CommandLineInterface(object):
description=self.description, description=self.description,
) )
self.parser.add_argument( self.parser.add_argument(
'-p', "-p",
'--port', "--port",
type=int, type=int,
help='Port number to listen on', help="Port number to listen on",
default=None, default=None,
) )
self.parser.add_argument( self.parser.add_argument(
'-b', "-b",
'--bind', "--bind",
dest='host', dest="host",
help='The host/address to bind to', help="The host/address to bind to",
default=None, default=None,
) )
self.parser.add_argument( self.parser.add_argument(
'--websocket_timeout', "--websocket_timeout",
type=int, type=int,
help='Maximum time to allow a websocket to be connected. -1 for infinite.', help="Maximum time to allow a websocket to be connected. -1 for infinite.",
default=86400, default=86400,
) )
self.parser.add_argument( self.parser.add_argument(
'--websocket_connect_timeout', "--websocket_connect_timeout",
type=int, type=int,
help='Maximum time to allow a connection to handshake. -1 for infinite', help="Maximum time to allow a connection to handshake. -1 for infinite",
default=5, default=5,
) )
self.parser.add_argument( self.parser.add_argument(
'-u', "-u",
'--unix-socket', "--unix-socket",
dest='unix_socket', dest="unix_socket",
help='Bind to a UNIX socket rather than a TCP host/port', help="Bind to a UNIX socket rather than a TCP host/port",
default=None, default=None,
) )
self.parser.add_argument( self.parser.add_argument(
'--fd', "--fd",
type=int, type=int,
dest='file_descriptor', dest="file_descriptor",
help='Bind to a file descriptor rather than a TCP host/port or named unix socket', help="Bind to a file descriptor rather than a TCP host/port or named unix socket",
default=None, default=None,
) )
self.parser.add_argument( self.parser.add_argument(
'-e', "-e",
'--endpoint', "--endpoint",
dest='socket_strings', dest="socket_strings",
action='append', action="append",
help='Use raw server strings passed directly to twisted', help="Use raw server strings passed directly to twisted",
default=[], default=[],
) )
self.parser.add_argument( self.parser.add_argument(
'-v', "-v",
'--verbosity', "--verbosity",
type=int, type=int,
help='How verbose to make the output', help="How verbose to make the output",
default=1, default=1,
) )
self.parser.add_argument( self.parser.add_argument(
'-t', "-t",
'--http-timeout', "--http-timeout",
type=int, type=int,
help='How long to wait for worker before timing out HTTP connections', help="How long to wait for worker before timing out HTTP connections",
default=120, default=120,
) )
self.parser.add_argument( self.parser.add_argument(
'--access-log', "--access-log",
help='Where to write the access log (- for stdout, the default for verbosity=1)', help="Where to write the access log (- for stdout, the default for verbosity=1)",
default=None, default=None,
) )
self.parser.add_argument( self.parser.add_argument(
'--ping-interval', "--ping-interval",
type=int, type=int,
help='The number of seconds a WebSocket must be idle before a keepalive ping is sent', help="The number of seconds a WebSocket must be idle before a keepalive ping is sent",
default=20, default=20,
) )
self.parser.add_argument( self.parser.add_argument(
'--ping-timeout', "--ping-timeout",
type=int, type=int,
help='The number of seconds before a WebSocket is closed if no response to a keepalive ping', help="The number of seconds before a WebSocket is closed if no response to a keepalive ping",
default=30, default=30,
) )
self.parser.add_argument( self.parser.add_argument(
'--ws-protocol', "--ws-protocol",
nargs='*', nargs="*",
dest='ws_protocols', dest="ws_protocols",
help='The WebSocket protocols you wish to support', help="The WebSocket protocols you wish to support",
default=None, default=None,
) )
self.parser.add_argument( self.parser.add_argument(
'--root-path', "--root-path",
dest='root_path', dest="root_path",
help='The setting for the ASGI root_path variable', help="The setting for the ASGI root_path variable",
default="", default="",
) )
self.parser.add_argument( self.parser.add_argument(
'--proxy-headers', "--proxy-headers",
dest='proxy_headers', dest="proxy_headers",
help='Enable parsing and using of X-Forwarded-For and X-Forwarded-Port headers and using that as the ' help="Enable parsing and using of X-Forwarded-For and X-Forwarded-Port headers and using that as the "
'client address', "client address",
default=False, default=False,
action='store_true', action="store_true",
) )
self.parser.add_argument( self.parser.add_argument(
'application', "application",
help='The application to dispatch to as path.to.module:instance.path', help="The application to dispatch to as path.to.module:instance.path",
) )
self.server = None self.server = None
@ -190,8 +190,8 @@ class CommandLineInterface(object):
) )
# Start the server # Start the server
logger.info( logger.info(
'Starting server at %s' % "Starting server at %s" %
(', '.join(endpoints), ) (", ".join(endpoints), )
) )
self.server = self.server_class( self.server = self.server_class(
application=application, application=application,
@ -205,7 +205,7 @@ class CommandLineInterface(object):
ws_protocols=args.ws_protocols, ws_protocols=args.ws_protocols,
root_path=args.root_path, root_path=args.root_path,
verbosity=args.verbosity, verbosity=args.verbosity,
proxy_forwarded_address_header='X-Forwarded-For' if args.proxy_headers else None, proxy_forwarded_address_header="X-Forwarded-For" if args.proxy_headers else None,
proxy_forwarded_port_header='X-Forwarded-Port' if args.proxy_headers else None, proxy_forwarded_port_header="X-Forwarded-Port" if args.proxy_headers else None,
) )
self.server.run() self.server.run()

View File

@ -11,15 +11,15 @@ def build_endpoint_description_strings(
""" """
socket_descriptions = [] socket_descriptions = []
if host and port: if host and port:
host = host.strip('[]').replace(':', '\:') host = host.strip("[]").replace(":", "\:")
socket_descriptions.append('tcp:port=%d:interface=%s' % (int(port), host)) socket_descriptions.append("tcp:port=%d:interface=%s" % (int(port), host))
elif any([host, port]): elif any([host, port]):
raise ValueError('TCP binding requires both port and host kwargs.') raise ValueError("TCP binding requires both port and host kwargs.")
if unix_socket: if unix_socket:
socket_descriptions.append('unix:%s' % unix_socket) socket_descriptions.append("unix:%s" % unix_socket)
if file_descriptor is not None: if file_descriptor is not None:
socket_descriptions.append('fd:fileno=%d' % int(file_descriptor)) socket_descriptions.append("fd:fileno=%d" % int(file_descriptor))
return socket_descriptions return socket_descriptions

View File

@ -118,9 +118,9 @@ class WebRequest(http.Request):
transport.protocol = protocol transport.protocol = protocol
protocol.makeConnection(transport) protocol.makeConnection(transport)
# Re-inject request # Re-inject request
data = self.method + b' ' + self.uri + b' HTTP/1.1\x0d\x0a' data = self.method + b" " + self.uri + b" HTTP/1.1\x0d\x0a"
for h in self.requestHeaders.getAllRawHeaders(): for h in self.requestHeaders.getAllRawHeaders():
data += h[0] + b': ' + b",".join(h[1]) + b'\x0d\x0a' data += h[0] + b": " + b",".join(h[1]) + b"\x0d\x0a"
data += b"\x0d\x0a" data += b"\x0d\x0a"
data += self.content.read() data += self.content.read()
protocol.dataReceived(data) protocol.dataReceived(data)
@ -203,29 +203,29 @@ class WebRequest(http.Request):
""" """
if "type" not in message: if "type" not in message:
raise ValueError("Message has no type defined") raise ValueError("Message has no type defined")
if message['type'] == "http.response": if message["type"] == "http.response":
if self._response_started: if self._response_started:
raise ValueError("HTTP response has already been started") raise ValueError("HTTP response has already been started")
self._response_started = True self._response_started = True
if 'status' not in message: if "status" not in message:
raise ValueError("Specifying a status code is required for a Response message.") raise ValueError("Specifying a status code is required for a Response message.")
# Set HTTP status code # Set HTTP status code
self.setResponseCode(message['status']) self.setResponseCode(message["status"])
# Write headers # Write headers
for header, value in message.get("headers", {}): for header, value in message.get("headers", {}):
# Shim code from old ASGI version, can be removed after a while # Shim code from old ASGI version, can be removed after a while
if isinstance(header, six.text_type): if isinstance(header, six.text_type):
header = header.encode("latin1") header = header.encode("latin1")
self.responseHeaders.addRawHeader(header, value) self.responseHeaders.addRawHeader(header, value)
logger.debug("HTTP %s response started for %s", message['status'], self.client_addr) logger.debug("HTTP %s response started for %s", message["status"], self.client_addr)
elif message['type'] == "http.response.hunk": elif message["type"] == "http.response.hunk":
if not self._response_started: if not self._response_started:
raise ValueError("HTTP response has not yet been started but got %s" % message['type']) raise ValueError("HTTP response has not yet been started but got %s" % message["type"])
else: else:
raise ValueError("Cannot handle message type %s!" % message['type']) raise ValueError("Cannot handle message type %s!" % message["type"])
# Write out body # Write out body
http.Request.write(self, message.get('content', b'')) http.Request.write(self, message.get("content", b""))
# End if there's no more content # End if there's no more content
if not message.get("more_content", False): if not message.get("more_content", False):
@ -356,9 +356,9 @@ class HTTPFactory(http.HTTPFactory):
using ALPN, so that doesn't go here: anyone wanting websockets will using ALPN, so that doesn't go here: anyone wanting websockets will
negotiate HTTP/1.1 and then do the upgrade dance. negotiate HTTP/1.1 and then do the upgrade dance.
""" """
baseProtocols = [b'http/1.1'] baseProtocols = [b"http/1.1"]
if http.H2_ENABLED: if http.H2_ENABLED:
baseProtocols.insert(0, b'h2') baseProtocols.insert(0, b"h2")
return baseProtocols return baseProtocols

View File

@ -68,7 +68,7 @@ class Server(object):
self.application_instances = {} self.application_instances = {}
# Make the factory # Make the factory
self.http_factory = HTTPFactory(self) self.http_factory = HTTPFactory(self)
self.ws_factory = WebSocketFactory(self, protocols=self.websocket_protocols, server='Daphne') self.ws_factory = WebSocketFactory(self, protocols=self.websocket_protocols, server="Daphne")
self.ws_factory.setProtocolOptions( self.ws_factory.setProtocolOptions(
autoPingTimeout=self.ping_timeout, autoPingTimeout=self.ping_timeout,
allowNullOrigin=True, allowNullOrigin=True,

View File

@ -14,7 +14,7 @@ def message_for_request(method, path, params=None, headers=None, body=None):
that through daphne and returns the emitted channel message. that through daphne and returns the emitted channel message.
""" """
request = _build_request(method, path, params, headers, body) request = _build_request(method, path, params, headers, body)
message, factory, transport = _run_through_daphne(request, 'http.request') message, factory, transport = _run_through_daphne(request, "http.request")
return message return message
@ -27,9 +27,9 @@ def response_for_message(message):
message_for_request) because we need a valid reply channel. I'm sure message_for_request) because we need a valid reply channel. I'm sure
this can be streamlined, but it works for now. this can be streamlined, but it works for now.
""" """
request = _build_request('GET', '/') request = _build_request("GET", "/")
request_message, factory, transport = _run_through_daphne(request, 'http.request') request_message, factory, transport = _run_through_daphne(request, "http.request")
factory.dispatch_reply(request_message['reply_channel'], message) factory.dispatch_reply(request_message["reply_channel"], message)
return transport.value() return transport.value()
@ -65,43 +65,43 @@ def _build_request(method, path, params=None, headers=None, body=None):
if six.PY3: if six.PY3:
quoted_path = parse.quote(path) quoted_path = parse.quote(path)
if params: if params:
quoted_path += '?' + parse.urlencode(params) quoted_path += "?" + parse.urlencode(params)
quoted_path = quoted_path.encode('ascii') quoted_path = quoted_path.encode("ascii")
else: else:
quoted_path = parse.quote(path.encode('utf8')) quoted_path = parse.quote(path.encode("utf8"))
if params: if params:
quoted_path += b'?' + parse.urlencode(params) quoted_path += b"?" + parse.urlencode(params)
request = method.encode('ascii') + b' ' + quoted_path + b" HTTP/1.1\r\n" request = method.encode("ascii") + b" " + quoted_path + b" HTTP/1.1\r\n"
for name, value in headers: for name, value in headers:
request += header_line(name, value) request += header_line(name, value)
request += b'\r\n' request += b"\r\n"
if body: if body:
request += body.encode('ascii') request += body.encode("ascii")
return request return request
def build_websocket_upgrade(path, params, headers): def build_websocket_upgrade(path, params, headers):
ws_headers = [ ws_headers = [
('Host', 'somewhere.com'), ("Host", "somewhere.com"),
('Upgrade', 'websocket'), ("Upgrade", "websocket"),
('Connection', 'Upgrade'), ("Connection", "Upgrade"),
('Sec-WebSocket-Key', 'x3JJHMbDL1EzLkh9GBhXDw=='), ("Sec-WebSocket-Key", "x3JJHMbDL1EzLkh9GBhXDw=="),
('Sec-WebSocket-Protocol', 'chat, superchat'), ("Sec-WebSocket-Protocol", "chat, superchat"),
('Sec-WebSocket-Version', '13'), ("Sec-WebSocket-Version", "13"),
('Origin', 'http://example.com') ("Origin", "http://example.com")
] ]
return _build_request('GET', path, params, headers=headers + ws_headers, body=None) return _build_request("GET", path, params, headers=headers + ws_headers, body=None)
def header_line(name, value): def header_line(name, value):
""" """
Given a header name and value, returns the line to use in a HTTP request or response. Given a header name and value, returns the line to use in a HTTP request or response.
""" """
return name.encode('ascii') + b': ' + value.encode('ascii') + b"\r\n" return name.encode("ascii") + b": " + value.encode("ascii") + b"\r\n"
def _run_through_daphne(request, channel_name): def _run_through_daphne(request, channel_name):
@ -113,7 +113,7 @@ def _run_through_daphne(request, channel_name):
""" """
channel_layer = ChannelLayer() channel_layer = ChannelLayer()
factory = HTTPFactory(channel_layer, send_channel="test!") factory = HTTPFactory(channel_layer, send_channel="test!")
proto = factory.buildProtocol(('127.0.0.1', 0)) proto = factory.buildProtocol(("127.0.0.1", 0))
transport = proto_helpers.StringTransport() transport = proto_helpers.StringTransport()
proto.makeConnection(transport) proto.makeConnection(transport)
proto.dataReceived(request) proto.dataReceived(request)
@ -125,4 +125,4 @@ def content_length_header(body):
""" """
Returns an appropriate Content-Length HTTP header for a given body. Returns an appropriate Content-Length HTTP header for a given body.
""" """
return 'Content-Length', six.text_type(len(body)) return "Content-Length", six.text_type(len(body))

View File

@ -7,10 +7,10 @@ import string
from hypothesis import strategies from hypothesis import strategies
HTTP_METHODS = ['OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT'] HTTP_METHODS = ["OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT"]
# Unicode characters of the "Letter" category # Unicode characters of the "Letter" category
letters = strategies.characters(whitelist_categories=('Lu', 'Ll', 'Lt', 'Lm', 'Lo', 'Nl')) letters = strategies.characters(whitelist_categories=("Lu", "Ll", "Lt", "Lm", "Lo", "Nl"))
def http_method(): def http_method():
@ -18,7 +18,7 @@ def http_method():
def _http_path_portion(): def _http_path_portion():
alphabet = string.ascii_letters + string.digits + '-._~' alphabet = string.ascii_letters + string.digits + "-._~"
return strategies.text(min_size=1, average_size=10, max_size=128, alphabet=alphabet) return strategies.text(min_size=1, average_size=10, max_size=128, alphabet=alphabet)
@ -27,7 +27,7 @@ def http_path():
Returns a URL path (not encoded). Returns a URL path (not encoded).
""" """
return strategies.lists( return strategies.lists(
_http_path_portion(), min_size=0, max_size=10).map(lambda s: '/' + '/'.join(s)) _http_path_portion(), min_size=0, max_size=10).map(lambda s: "/" + "/".join(s))
def http_body(): def http_body():
@ -50,7 +50,7 @@ def valid_bidi(value):
direction of text point of view. This little helper just rejects those. direction of text point of view. This little helper just rejects those.
""" """
try: try:
value.encode('idna') value.encode("idna")
except UnicodeError: except UnicodeError:
return False return False
else: else:
@ -67,12 +67,12 @@ def international_domain_name():
Returns a byte string of a domain name, IDNA-encoded. Returns a byte string of a domain name, IDNA-encoded.
""" """
return strategies.lists( return strategies.lists(
_domain_label(), min_size=2, average_size=2).map(lambda s: ('.'.join(s)).encode('idna')) _domain_label(), min_size=2, average_size=2).map(lambda s: (".".join(s)).encode("idna"))
def _query_param(): def _query_param():
return strategies.text(alphabet=letters, min_size=1, average_size=10, max_size=255).\ return strategies.text(alphabet=letters, min_size=1, average_size=10, max_size=255).\
map(lambda s: s.encode('utf8')) map(lambda s: s.encode("utf8"))
def query_params(): def query_params():
@ -94,7 +94,7 @@ def header_name():
and 20 characters long and 20 characters long
""" """
return strategies.text( return strategies.text(
alphabet=string.ascii_letters + string.digits + '-', min_size=1, max_size=30) alphabet=string.ascii_letters + string.digits + "-", min_size=1, max_size=30)
def header_value(): def header_value():
@ -105,8 +105,8 @@ def header_value():
https://en.wikipedia.org/wiki/List_of_HTTP_header_fields https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
""" """
return strategies.text( return strategies.text(
alphabet=string.ascii_letters + string.digits + string.punctuation + ' /t', alphabet=string.ascii_letters + string.digits + string.punctuation + " /t",
min_size=1, average_size=40, max_size=8190).filter(lambda s: len(s.encode('utf8')) < 8190) min_size=1, average_size=40, max_size=8190).filter(lambda s: len(s.encode("utf8")) < 8190)
def headers(): def headers():

View File

@ -28,7 +28,7 @@ class TestHTTPRequestSpec(testcases.ASGIHTTPTestCase):
""" """
Smallest viable example. Mostly verifies that our request building works. Smallest viable example. Mostly verifies that our request building works.
""" """
request_method, request_path = 'GET', '/' request_method, request_path = "GET", "/"
message = message_for_request(request_method, request_path) message = message_for_request(request_method, request_path)
self.assert_valid_http_request_message(message, request_method, request_path) self.assert_valid_http_request_message(message, request_method, request_path)
@ -41,7 +41,7 @@ class TestHTTPRequestSpec(testcases.ASGIHTTPTestCase):
""" """
Tests a typical HTTP GET request, with a path and query parameters Tests a typical HTTP GET request, with a path and query parameters
""" """
request_method = 'GET' request_method = "GET"
message = message_for_request(request_method, request_path, request_params) message = message_for_request(request_method, request_path, request_params)
self.assert_valid_http_request_message( self.assert_valid_http_request_message(
@ -55,7 +55,7 @@ class TestHTTPRequestSpec(testcases.ASGIHTTPTestCase):
""" """
Tests a typical POST request, submitting some data in a body. Tests a typical POST request, submitting some data in a body.
""" """
request_method = 'POST' request_method = "POST"
headers = [content_length_header(request_body)] headers = [content_length_header(request_body)]
message = message_for_request( message = message_for_request(
request_method, request_path, headers=headers, body=request_body) request_method, request_path, headers=headers, body=request_body)
@ -69,7 +69,7 @@ class TestHTTPRequestSpec(testcases.ASGIHTTPTestCase):
""" """
Tests that HTTP header fields are handled as specified Tests that HTTP header fields are handled as specified
""" """
request_method, request_path = 'OPTIONS', '/te st-à/' request_method, request_path = "OPTIONS", "/te st-à/"
message = message_for_request(request_method, request_path, headers=request_headers) message = message_for_request(request_method, request_path, headers=request_headers)
self.assert_valid_http_request_message( self.assert_valid_http_request_message(
@ -85,7 +85,7 @@ class TestHTTPRequestSpec(testcases.ASGIHTTPTestCase):
header_name = request_headers[0][0] header_name = request_headers[0][0]
duplicated_headers = [(header_name, header[1]) for header in request_headers] duplicated_headers = [(header_name, header[1]) for header in request_headers]
request_method, request_path = 'OPTIONS', '/te st-à/' request_method, request_path = "OPTIONS", "/te st-à/"
message = message_for_request(request_method, request_path, headers=duplicated_headers) message = message_for_request(request_method, request_path, headers=duplicated_headers)
self.assert_valid_http_request_message( self.assert_valid_http_request_message(
@ -114,24 +114,24 @@ class TestHTTPRequestSpec(testcases.ASGIHTTPTestCase):
message, request_method, request_path, request_params, request_headers, request_body) message, request_method, request_path, request_params, request_headers, request_body)
def test_headers_are_lowercased_and_stripped(self): def test_headers_are_lowercased_and_stripped(self):
request_method, request_path = 'GET', '/' request_method, request_path = "GET", "/"
headers = [('MYCUSTOMHEADER', ' foobar ')] headers = [("MYCUSTOMHEADER", " foobar ")]
message = message_for_request(request_method, request_path, headers=headers) message = message_for_request(request_method, request_path, headers=headers)
self.assert_valid_http_request_message( self.assert_valid_http_request_message(
message, request_method, request_path, request_headers=headers) message, request_method, request_path, request_headers=headers)
# Note that Daphne returns a list of tuples here, which is fine, because the spec # Note that Daphne returns a list of tuples here, which is fine, because the spec
# asks to treat them interchangeably. # asks to treat them interchangeably.
assert message['headers'] == [(b'mycustomheader', b'foobar')] assert message["headers"] == [(b"mycustomheader", b"foobar")]
@given(daphne_path=http_strategies.http_path()) @given(daphne_path=http_strategies.http_path())
def test_root_path_header(self, daphne_path): def test_root_path_header(self, daphne_path):
""" """
Tests root_path handling. Tests root_path handling.
""" """
request_method, request_path = 'GET', '/' request_method, request_path = "GET", "/"
# Daphne-Root-Path must be URL encoded when submitting as HTTP header field # Daphne-Root-Path must be URL encoded when submitting as HTTP header field
headers = [('Daphne-Root-Path', parse.quote(daphne_path.encode('utf8')))] headers = [("Daphne-Root-Path", parse.quote(daphne_path.encode("utf8")))]
message = message_for_request(request_method, request_path, headers=headers) message = message_for_request(request_method, request_path, headers=headers)
# Daphne-Root-Path is not included in the returned 'headers' section. So we expect # Daphne-Root-Path is not included in the returned 'headers' section. So we expect
@ -140,7 +140,7 @@ class TestHTTPRequestSpec(testcases.ASGIHTTPTestCase):
self.assert_valid_http_request_message( self.assert_valid_http_request_message(
message, request_method, request_path, request_headers=expected_headers) message, request_method, request_path, request_headers=expected_headers)
# And what we're looking for, root_path being set. # And what we're looking for, root_path being set.
assert message['root_path'] == daphne_path assert message["root_path"] == daphne_path
class TestProxyHandling(unittest.TestCase): class TestProxyHandling(unittest.TestCase):
@ -153,7 +153,7 @@ class TestProxyHandling(unittest.TestCase):
def setUp(self): def setUp(self):
self.channel_layer = ChannelLayer() self.channel_layer = ChannelLayer()
self.factory = HTTPFactory(self.channel_layer, send_channel="test!") self.factory = HTTPFactory(self.channel_layer, send_channel="test!")
self.proto = self.factory.buildProtocol(('127.0.0.1', 0)) self.proto = self.factory.buildProtocol(("127.0.0.1", 0))
self.tr = proto_helpers.StringTransport() self.tr = proto_helpers.StringTransport()
self.proto.makeConnection(self.tr) self.proto.makeConnection(self.tr)
@ -167,11 +167,11 @@ class TestProxyHandling(unittest.TestCase):
) )
# Get the resulting message off of the channel layer # Get the resulting message off of the channel layer
_, message = self.channel_layer.receive(["http.request"]) _, message = self.channel_layer.receive(["http.request"])
self.assertEqual(message['client'], ['192.168.1.1', 54321]) self.assertEqual(message["client"], ["192.168.1.1", 54321])
def test_x_forwarded_for_parsed(self): def test_x_forwarded_for_parsed(self):
self.factory.proxy_forwarded_address_header = 'X-Forwarded-For' self.factory.proxy_forwarded_address_header = "X-Forwarded-For"
self.factory.proxy_forwarded_port_header = 'X-Forwarded-Port' self.factory.proxy_forwarded_port_header = "X-Forwarded-Port"
self.proto.dataReceived( self.proto.dataReceived(
b"GET /te%20st-%C3%A0/?foo=+bar HTTP/1.1\r\n" + b"GET /te%20st-%C3%A0/?foo=+bar HTTP/1.1\r\n" +
b"Host: somewhere.com\r\n" + b"Host: somewhere.com\r\n" +
@ -181,11 +181,11 @@ class TestProxyHandling(unittest.TestCase):
) )
# Get the resulting message off of the channel layer # Get the resulting message off of the channel layer
_, message = self.channel_layer.receive(["http.request"]) _, message = self.channel_layer.receive(["http.request"])
self.assertEqual(message['client'], ['10.1.2.3', 80]) self.assertEqual(message["client"], ["10.1.2.3", 80])
def test_x_forwarded_for_port_missing(self): def test_x_forwarded_for_port_missing(self):
self.factory.proxy_forwarded_address_header = 'X-Forwarded-For' self.factory.proxy_forwarded_address_header = "X-Forwarded-For"
self.factory.proxy_forwarded_port_header = 'X-Forwarded-Port' self.factory.proxy_forwarded_port_header = "X-Forwarded-Port"
self.proto.dataReceived( self.proto.dataReceived(
b"GET /te%20st-%C3%A0/?foo=+bar HTTP/1.1\r\n" + b"GET /te%20st-%C3%A0/?foo=+bar HTTP/1.1\r\n" +
b"Host: somewhere.com\r\n" + b"Host: somewhere.com\r\n" +
@ -194,4 +194,4 @@ class TestProxyHandling(unittest.TestCase):
) )
# Get the resulting message off of the channel layer # Get the resulting message off of the channel layer
_, message = self.channel_layer.receive(["http.request"]) _, message = self.channel_layer.receive(["http.request"])
self.assertEqual(message['client'], ['10.1.2.3', 0]) self.assertEqual(message["client"], ["10.1.2.3", 0])

View File

@ -20,15 +20,15 @@ class TestHTTPResponseSpec(testcases.ASGIHTTPTestCase):
""" """
Smallest viable example. Mostly verifies that our response building works. Smallest viable example. Mostly verifies that our response building works.
""" """
message = {'status': 200} message = {"status": 200}
response = factories.response_for_message(message) response = factories.response_for_message(message)
self.assert_valid_http_response_message(message, response) self.assert_valid_http_response_message(message, response)
self.assertIn(b'200 OK', response) self.assertIn(b"200 OK", response)
# Assert that the response is the last of the chunks. # Assert that the response is the last of the chunks.
# N.b. at the time of writing, Daphne did not support multiple response chunks, # N.b. at the time of writing, Daphne did not support multiple response chunks,
# but still sends with Transfer-Encoding: chunked if no Content-Length header # but still sends with Transfer-Encoding: chunked if no Content-Length header
# is specified (and maybe even if specified). # is specified (and maybe even if specified).
self.assertTrue(response.endswith(b'0\r\n\r\n')) self.assertTrue(response.endswith(b"0\r\n\r\n"))
def test_status_code_required(self): def test_status_code_required(self):
""" """
@ -48,21 +48,21 @@ class TestHTTPResponseSpec(testcases.ASGIHTTPTestCase):
of them have meaning that is respected by Twisted. E.g. setting 204 (No Content) of them have meaning that is respected by Twisted. E.g. setting 204 (No Content)
as a status code results in Twisted discarding the body. as a status code results in Twisted discarding the body.
""" """
message = {'status': 201} # 'Created' message = {"status": 201} # 'Created'
response = factories.response_for_message(message) response = factories.response_for_message(message)
self.assert_valid_http_response_message(message, response) self.assert_valid_http_response_message(message, response)
self.assertIn(b'201 Created', response) self.assertIn(b"201 Created", response)
@given(body=http_strategies.http_body()) @given(body=http_strategies.http_body())
def test_body_is_transmitted(self, body): def test_body_is_transmitted(self, body):
message = {'status': 200, 'content': body.encode('ascii')} message = {"status": 200, "content": body.encode("ascii")}
response = factories.response_for_message(message) response = factories.response_for_message(message)
self.assert_valid_http_response_message(message, response) self.assert_valid_http_response_message(message, response)
@given(headers=http_strategies.headers()) @given(headers=http_strategies.headers())
def test_headers(self, headers): def test_headers(self, headers):
# The ASGI spec requires us to lowercase our header names # The ASGI spec requires us to lowercase our header names
message = {'status': 200, 'headers': [(name.lower(), value) for name, value in headers]} message = {"status": 200, "headers": [(name.lower(), value) for name, value in headers]}
response = factories.response_for_message(message) response = factories.response_for_message(message)
# The assert_ method does the heavy lifting of checking that headers are # The assert_ method does the heavy lifting of checking that headers are
# as expected. # as expected.
@ -80,9 +80,9 @@ class TestHTTPResponseSpec(testcases.ASGIHTTPTestCase):
so there's not a lot going on here. so there's not a lot going on here.
""" """
message = { message = {
'status': 202, # 'Accepted' "status": 202, # 'Accepted'
'headers': [(name.lower(), value) for name, value in headers], "headers": [(name.lower(), value) for name, value in headers],
'content': body.encode('ascii') "content": body.encode("ascii")
} }
response = factories.response_for_message(message) response = factories.response_for_message(message)
self.assert_valid_http_response_message(message, response) self.assert_valid_http_response_message(message, response)
@ -96,7 +96,7 @@ class TestHTTPResponse(TestCase):
def setUp(self): def setUp(self):
self.channel_layer = ChannelLayer() self.channel_layer = ChannelLayer()
self.factory = HTTPFactory(self.channel_layer, send_channel="test!") self.factory = HTTPFactory(self.channel_layer, send_channel="test!")
self.proto = self.factory.buildProtocol(('127.0.0.1', 0)) self.proto = self.factory.buildProtocol(("127.0.0.1", 0))
self.tr = proto_helpers.StringTransport() self.tr = proto_helpers.StringTransport()
self.proto.makeConnection(self.tr) self.proto.makeConnection(self.tr)
@ -115,7 +115,7 @@ class TestHTTPResponse(TestCase):
# Send back an example response # Send back an example response
self.factory.dispatch_reply( self.factory.dispatch_reply(
message['reply_channel'], message["reply_channel"],
{ {
"status": 200, "status": 200,
"status_text": b"OK", "status_text": b"OK",
@ -125,4 +125,4 @@ class TestHTTPResponse(TestCase):
# Get the disconnection notification # Get the disconnection notification
_, disconnect_message = self.channel_layer.receive(["http.disconnect"]) _, disconnect_message = self.channel_layer.receive(["http.disconnect"])
self.assertEqual(disconnect_message['path'], "/te st-à/") self.assertEqual(disconnect_message["path"], "/te st-à/")

View File

@ -15,45 +15,45 @@ class TestXForwardedForHttpParsing(TestCase):
def test_basic(self): def test_basic(self):
headers = Headers({ headers = Headers({
b'X-Forwarded-For': [b'10.1.2.3'], b"X-Forwarded-For": [b"10.1.2.3"],
b'X-Forwarded-Port': [b'1234'] b"X-Forwarded-Port": [b"1234"]
}) })
result = parse_x_forwarded_for(headers) result = parse_x_forwarded_for(headers)
self.assertEqual(result, ['10.1.2.3', 1234]) self.assertEqual(result, ["10.1.2.3", 1234])
self.assertIsInstance(result[0], six.text_type) self.assertIsInstance(result[0], six.text_type)
def test_address_only(self): def test_address_only(self):
headers = Headers({ headers = Headers({
b'X-Forwarded-For': [b'10.1.2.3'], b"X-Forwarded-For": [b"10.1.2.3"],
}) })
self.assertEqual( self.assertEqual(
parse_x_forwarded_for(headers), parse_x_forwarded_for(headers),
['10.1.2.3', 0] ["10.1.2.3", 0]
) )
def test_v6_address(self): def test_v6_address(self):
headers = Headers({ headers = Headers({
b'X-Forwarded-For': [b'1043::a321:0001, 10.0.5.6'], b"X-Forwarded-For": [b"1043::a321:0001, 10.0.5.6"],
}) })
self.assertEqual( self.assertEqual(
parse_x_forwarded_for(headers), parse_x_forwarded_for(headers),
['1043::a321:0001', 0] ["1043::a321:0001", 0]
) )
def test_multiple_proxys(self): def test_multiple_proxys(self):
headers = Headers({ headers = Headers({
b'X-Forwarded-For': [b'10.1.2.3, 10.1.2.4'], b"X-Forwarded-For": [b"10.1.2.3, 10.1.2.4"],
}) })
self.assertEqual( self.assertEqual(
parse_x_forwarded_for(headers), parse_x_forwarded_for(headers),
['10.1.2.3', 0] ["10.1.2.3", 0]
) )
def test_original(self): def test_original(self):
headers = Headers({}) headers = Headers({})
self.assertEqual( self.assertEqual(
parse_x_forwarded_for(headers, original=['127.0.0.1', 80]), parse_x_forwarded_for(headers, original=["127.0.0.1", 80]),
['127.0.0.1', 80] ["127.0.0.1", 80]
) )
def test_no_original(self): def test_no_original(self):
@ -68,46 +68,46 @@ class TestXForwardedForWsParsing(TestCase):
def test_basic(self): def test_basic(self):
headers = { headers = {
b'X-Forwarded-For': b'10.1.2.3', b"X-Forwarded-For": b"10.1.2.3",
b'X-Forwarded-Port': b'1234', b"X-Forwarded-Port": b"1234",
} }
self.assertEqual( self.assertEqual(
parse_x_forwarded_for(headers), parse_x_forwarded_for(headers),
['10.1.2.3', 1234] ["10.1.2.3", 1234]
) )
def test_address_only(self): def test_address_only(self):
headers = { headers = {
b'X-Forwarded-For': b'10.1.2.3', b"X-Forwarded-For": b"10.1.2.3",
} }
self.assertEqual( self.assertEqual(
parse_x_forwarded_for(headers), parse_x_forwarded_for(headers),
['10.1.2.3', 0] ["10.1.2.3", 0]
) )
def test_v6_address(self): def test_v6_address(self):
headers = { headers = {
b'X-Forwarded-For': [b'1043::a321:0001, 10.0.5.6'], b"X-Forwarded-For": [b"1043::a321:0001, 10.0.5.6"],
} }
self.assertEqual( self.assertEqual(
parse_x_forwarded_for(headers), parse_x_forwarded_for(headers),
['1043::a321:0001', 0] ["1043::a321:0001", 0]
) )
def test_multiple_proxys(self): def test_multiple_proxys(self):
headers = { headers = {
b'X-Forwarded-For': b'10.1.2.3, 10.1.2.4', b"X-Forwarded-For": b"10.1.2.3, 10.1.2.4",
} }
self.assertEqual( self.assertEqual(
parse_x_forwarded_for(headers), parse_x_forwarded_for(headers),
['10.1.2.3', 0] ["10.1.2.3", 0]
) )
def test_original(self): def test_original(self):
headers = {} headers = {}
self.assertEqual( self.assertEqual(
parse_x_forwarded_for(headers, original=['127.0.0.1', 80]), parse_x_forwarded_for(headers, original=["127.0.0.1", 80]),
['127.0.0.1', 80] ["127.0.0.1", 80]
) )
def test_no_original(self): def test_no_original(self):

View File

@ -19,7 +19,7 @@ class WebSocketConnection(object):
self.channel_layer = ChannelLayer() self.channel_layer = ChannelLayer()
self.factory = HTTPFactory(self.channel_layer, send_channel="test!") self.factory = HTTPFactory(self.channel_layer, send_channel="test!")
self.proto = self.factory.buildProtocol(('127.0.0.1', 0)) self.proto = self.factory.buildProtocol(("127.0.0.1", 0))
self.transport = proto_helpers.StringTransport() self.transport = proto_helpers.StringTransport()
self.proto.makeConnection(self.transport) self.proto.makeConnection(self.transport)
@ -28,7 +28,7 @@ class WebSocketConnection(object):
Low-level method to let Daphne handle HTTP/WebSocket data Low-level method to let Daphne handle HTTP/WebSocket data
""" """
self.proto.dataReceived(request) self.proto.dataReceived(request)
_, self.last_message = self.channel_layer.receive(['websocket.connect']) _, self.last_message = self.channel_layer.receive(["websocket.connect"])
return self.last_message return self.last_message
def send(self, content): def send(self, content):
@ -38,12 +38,12 @@ class WebSocketConnection(object):
if self.last_message is None: if self.last_message is None:
# Auto-connect for convenience. # Auto-connect for convenience.
self.connect() self.connect()
self.factory.dispatch_reply(self.last_message['reply_channel'], content) self.factory.dispatch_reply(self.last_message["reply_channel"], content)
response = self.transport.value() response = self.transport.value()
self.transport.clear() self.transport.clear()
return response return response
def connect(self, path='/', params=None, headers=None): def connect(self, path="/", params=None, headers=None):
""" """
High-level method to perform the WebSocket handshake High-level method to perform the WebSocket handshake
""" """
@ -78,21 +78,21 @@ class TestSendCloseAccept(testcases.ASGIWebSocketTestCase):
""" """
def test_empty_accept(self): def test_empty_accept(self):
response = WebSocketConnection().send({'accept': True}) response = WebSocketConnection().send({"accept": True})
self.assert_websocket_upgrade(response) self.assert_websocket_upgrade(response)
@given(text=http_strategies.http_body()) @given(text=http_strategies.http_body())
def test_accept_and_text(self, text): def test_accept_and_text(self, text):
response = WebSocketConnection().send({'accept': True, 'text': text}) response = WebSocketConnection().send({"accept": True, "text": text})
self.assert_websocket_upgrade(response, text.encode('ascii')) self.assert_websocket_upgrade(response, text.encode("ascii"))
@given(data=http_strategies.binary_payload()) @given(data=http_strategies.binary_payload())
def test_accept_and_bytes(self, data): def test_accept_and_bytes(self, data):
response = WebSocketConnection().send({'accept': True, 'bytes': data}) response = WebSocketConnection().send({"accept": True, "bytes": data})
self.assert_websocket_upgrade(response, data) self.assert_websocket_upgrade(response, data)
def test_accept_false(self): def test_accept_false(self):
response = WebSocketConnection().send({'accept': False}) response = WebSocketConnection().send({"accept": False})
self.assert_websocket_denied(response) self.assert_websocket_denied(response)
def test_accept_false_with_text(self): def test_accept_false_with_text(self):
@ -102,10 +102,10 @@ class TestSendCloseAccept(testcases.ASGIWebSocketTestCase):
We can't easily use Hypothesis to generate data for this test because it's We can't easily use Hypothesis to generate data for this test because it's
hard to detect absence of the body if e.g. Hypothesis would generate a 'GET' hard to detect absence of the body if e.g. Hypothesis would generate a 'GET'
""" """
text = 'foobar' text = "foobar"
response = WebSocketConnection().send({'accept': False, 'text': text}) response = WebSocketConnection().send({"accept": False, "text": text})
self.assert_websocket_denied(response) self.assert_websocket_denied(response)
self.assertNotIn(text.encode('ascii'), response) self.assertNotIn(text.encode("ascii"), response)
def test_accept_false_with_bytes(self): def test_accept_false_with_bytes(self):
""" """
@ -114,8 +114,8 @@ class TestSendCloseAccept(testcases.ASGIWebSocketTestCase):
We can't easily use Hypothesis to generate data for this test because it's We can't easily use Hypothesis to generate data for this test because it's
hard to detect absence of the body if e.g. Hypothesis would generate a 'GET' hard to detect absence of the body if e.g. Hypothesis would generate a 'GET'
""" """
data = b'foobar' data = b"foobar"
response = WebSocketConnection().send({'accept': False, 'bytes': data}) response = WebSocketConnection().send({"accept": False, "bytes": data})
self.assert_websocket_denied(response) self.assert_websocket_denied(response)
self.assertNotIn(data, response) self.assertNotIn(data, response)
@ -123,35 +123,35 @@ class TestSendCloseAccept(testcases.ASGIWebSocketTestCase):
def test_just_text(self, text): def test_just_text(self, text):
assume(len(text) > 0) assume(len(text) > 0)
# If content is sent, accept=True is implied. # If content is sent, accept=True is implied.
response = WebSocketConnection().send({'text': text}) response = WebSocketConnection().send({"text": text})
self.assert_websocket_upgrade(response, text.encode('ascii')) self.assert_websocket_upgrade(response, text.encode("ascii"))
@given(data=http_strategies.binary_payload()) @given(data=http_strategies.binary_payload())
def test_just_bytes(self, data): def test_just_bytes(self, data):
assume(len(data) > 0) assume(len(data) > 0)
# If content is sent, accept=True is implied. # If content is sent, accept=True is implied.
response = WebSocketConnection().send({'bytes': data}) response = WebSocketConnection().send({"bytes": data})
self.assert_websocket_upgrade(response, data) self.assert_websocket_upgrade(response, data)
def test_close_boolean(self): def test_close_boolean(self):
response = WebSocketConnection().send({'close': True}) response = WebSocketConnection().send({"close": True})
self.assert_websocket_denied(response) self.assert_websocket_denied(response)
@given(number=strategies.integers(min_value=1)) @given(number=strategies.integers(min_value=1))
def test_close_integer(self, number): def test_close_integer(self, number):
response = WebSocketConnection().send({'close': number}) response = WebSocketConnection().send({"close": number})
self.assert_websocket_denied(response) self.assert_websocket_denied(response)
@given(text=http_strategies.http_body()) @given(text=http_strategies.http_body())
def test_close_with_text(self, text): def test_close_with_text(self, text):
assume(len(text) > 0) assume(len(text) > 0)
response = WebSocketConnection().send({'close': True, 'text': text}) response = WebSocketConnection().send({"close": True, "text": text})
self.assert_websocket_upgrade(response, text.encode('ascii'), expect_close=True) self.assert_websocket_upgrade(response, text.encode("ascii"), expect_close=True)
@given(data=http_strategies.binary_payload()) @given(data=http_strategies.binary_payload())
def test_close_with_data(self, data): def test_close_with_data(self, data):
assume(len(data) > 0) assume(len(data) > 0)
response = WebSocketConnection().send({'close': True, 'bytes': data}) response = WebSocketConnection().send({"close": True, "bytes": data})
self.assert_websocket_upgrade(response, data, expect_close=True) self.assert_websocket_upgrade(response, data, expect_close=True)
@ -177,34 +177,34 @@ class TestWebSocketProtocol(testcases.ASGIWebSocketTestCase):
b"Origin: http://example.com\r\n" b"Origin: http://example.com\r\n"
b"\r\n" b"\r\n"
) )
self.assertEqual(message['path'], "/chat") self.assertEqual(message["path"], "/chat")
self.assertEqual(message['query_string'], b"") self.assertEqual(message["query_string"], b"")
self.assertEqual( self.assertEqual(
sorted(message['headers']), sorted(message["headers"]),
[(b'connection', b'Upgrade'), [(b"connection", b"Upgrade"),
(b'host', b'somewhere.com'), (b"host", b"somewhere.com"),
(b'origin', b'http://example.com'), (b"origin", b"http://example.com"),
(b'sec-websocket-key', b'x3JJHMbDL1EzLkh9GBhXDw=='), (b"sec-websocket-key", b"x3JJHMbDL1EzLkh9GBhXDw=="),
(b'sec-websocket-protocol', b'chat, superchat'), (b"sec-websocket-protocol", b"chat, superchat"),
(b'sec-websocket-version', b'13'), (b"sec-websocket-version", b"13"),
(b'upgrade', b'websocket')] (b"upgrade", b"websocket")]
) )
self.assert_valid_websocket_connect_message(message, '/chat') self.assert_valid_websocket_connect_message(message, "/chat")
# Accept the connection # Accept the connection
response = self.connection.send({'accept': True}) response = self.connection.send({"accept": True})
self.assert_websocket_upgrade(response) self.assert_websocket_upgrade(response)
# Send some text # Send some text
response = self.connection.send({'text': "Hello World!"}) response = self.connection.send({"text": "Hello World!"})
self.assertEqual(response, b"\x81\x0cHello World!") self.assertEqual(response, b"\x81\x0cHello World!")
# Send some bytes # Send some bytes
response = self.connection.send({'bytes': b"\xaa\xbb\xcc\xdd"}) response = self.connection.send({"bytes": b"\xaa\xbb\xcc\xdd"})
self.assertEqual(response, b"\x82\x04\xaa\xbb\xcc\xdd") self.assertEqual(response, b"\x82\x04\xaa\xbb\xcc\xdd")
# Close the connection # Close the connection
response = self.connection.send({'close': True}) response = self.connection.send({"close": True})
self.assertEqual(response, b"\x88\x02\x03\xe8") self.assertEqual(response, b"\x88\x02\x03\xe8")
def test_connection_with_file_origin_is_accepted(self): def test_connection_with_file_origin_is_accepted(self):
@ -219,11 +219,11 @@ class TestWebSocketProtocol(testcases.ASGIWebSocketTestCase):
b"Origin: file://\r\n" b"Origin: file://\r\n"
b"\r\n" b"\r\n"
) )
self.assertIn((b'origin', b'file://'), message['headers']) self.assertIn((b"origin", b"file://"), message["headers"])
self.assert_valid_websocket_connect_message(message, '/chat') self.assert_valid_websocket_connect_message(message, "/chat")
# Accept the connection # Accept the connection
response = self.connection.send({'accept': True}) response = self.connection.send({"accept": True})
self.assert_websocket_upgrade(response) self.assert_websocket_upgrade(response)
def test_connection_with_no_origin_is_accepted(self): def test_connection_with_no_origin_is_accepted(self):
@ -238,9 +238,9 @@ class TestWebSocketProtocol(testcases.ASGIWebSocketTestCase):
b"\r\n" b"\r\n"
) )
self.assertNotIn(b'origin', [header_tuple[0] for header_tuple in message['headers']]) self.assertNotIn(b"origin", [header_tuple[0] for header_tuple in message["headers"]])
self.assert_valid_websocket_connect_message(message, '/chat') self.assert_valid_websocket_connect_message(message, "/chat")
# Accept the connection # Accept the connection
response = self.connection.send({'accept': True}) response = self.connection.send({"accept": True})
self.assert_websocket_upgrade(response) self.assert_websocket_upgrade(response)

View File

@ -34,7 +34,7 @@ class ASGITestCaseBase(unittest.TestCase):
def assert_valid_reply_channel(self, reply_channel): def assert_valid_reply_channel(self, reply_channel):
self.assertIsInstance(reply_channel, str) self.assertIsInstance(reply_channel, str)
# The reply channel is decided by the server. # The reply channel is decided by the server.
self.assertTrue(reply_channel.startswith('test!')) self.assertTrue(reply_channel.startswith("test!"))
def assert_valid_path(self, path, request_path): def assert_valid_path(self, path, request_path):
self.assertIsInstance(path, str) self.assertIsInstance(path, str)
@ -65,96 +65,96 @@ class ASGIHTTPTestCase(ASGITestCaseBase):
self.assert_presence_of_message_keys( self.assert_presence_of_message_keys(
channel_message.keys(), channel_message.keys(),
{'reply_channel', 'http_version', 'method', 'path', 'query_string', 'headers'}, {"reply_channel", "http_version", "method", "path", "query_string", "headers"},
{'scheme', 'root_path', 'body', 'body_channel', 'client', 'server'}) {"scheme", "root_path", "body", "body_channel", "client", "server"})
# == Assertions about required channel_message fields == # == Assertions about required channel_message fields ==
self.assert_valid_reply_channel(channel_message['reply_channel']) self.assert_valid_reply_channel(channel_message["reply_channel"])
self.assert_valid_path(channel_message['path'], request_path) self.assert_valid_path(channel_message["path"], request_path)
http_version = channel_message['http_version'] http_version = channel_message["http_version"]
self.assertIsInstance(http_version, str) self.assertIsInstance(http_version, str)
self.assertIn(http_version, ['1.0', '1.1', '1.2']) self.assertIn(http_version, ["1.0", "1.1", "1.2"])
method = channel_message['method'] method = channel_message["method"]
self.assertIsInstance(method, str) self.assertIsInstance(method, str)
self.assertTrue(method.isupper()) self.assertTrue(method.isupper())
self.assertEqual(channel_message['method'], request_method) self.assertEqual(channel_message["method"], request_method)
query_string = channel_message['query_string'] query_string = channel_message["query_string"]
# Assert that query_string is a byte string and still url encoded # Assert that query_string is a byte string and still url encoded
self.assertIsInstance(query_string, bytes) self.assertIsInstance(query_string, bytes)
self.assertEqual(query_string, parse.urlencode(request_params or []).encode('ascii')) self.assertEqual(query_string, parse.urlencode(request_params or []).encode("ascii"))
# Ordering of header names is not important, but the order of values for a header # Ordering of header names is not important, but the order of values for a header
# name is. To assert whether that order is kept, we transform both the request # name is. To assert whether that order is kept, we transform both the request
# headers and the channel message headers into a dictionary # headers and the channel message headers into a dictionary
# {name: [value1, value2, ...]} and check if they're equal. # {name: [value1, value2, ...]} and check if they're equal.
transformed_message_headers = defaultdict(list) transformed_message_headers = defaultdict(list)
for name, value in channel_message['headers']: for name, value in channel_message["headers"]:
transformed_message_headers[name].append(value) transformed_message_headers[name].append(value)
transformed_request_headers = defaultdict(list) transformed_request_headers = defaultdict(list)
for name, value in (request_headers or []): for name, value in (request_headers or []):
expected_name = name.lower().strip().encode('ascii') expected_name = name.lower().strip().encode("ascii")
expected_value = value.strip().encode('ascii') expected_value = value.strip().encode("ascii")
transformed_request_headers[expected_name].append(expected_value) transformed_request_headers[expected_name].append(expected_value)
self.assertEqual(transformed_message_headers, transformed_request_headers) self.assertEqual(transformed_message_headers, transformed_request_headers)
# == Assertions about optional channel_message fields == # == Assertions about optional channel_message fields ==
scheme = channel_message.get('scheme') scheme = channel_message.get("scheme")
if scheme is not None: if scheme is not None:
self.assertIsInstance(scheme, str) self.assertIsInstance(scheme, str)
self.assertTrue(scheme) # May not be empty self.assertTrue(scheme) # May not be empty
root_path = channel_message.get('root_path') root_path = channel_message.get("root_path")
if root_path is not None: if root_path is not None:
self.assertIsInstance(root_path, str) self.assertIsInstance(root_path, str)
body = channel_message.get('body') body = channel_message.get("body")
# Ensure we test for presence of 'body' if a request body was given # Ensure we test for presence of 'body' if a request body was given
if request_body is not None or body is not None: if request_body is not None or body is not None:
self.assertIsInstance(body, str) self.assertIsInstance(body, str)
self.assertEqual(body, (request_body or '').encode('ascii')) self.assertEqual(body, (request_body or "").encode("ascii"))
body_channel = channel_message.get('body_channel') body_channel = channel_message.get("body_channel")
if body_channel is not None: if body_channel is not None:
self.assertIsInstance(body_channel, str) self.assertIsInstance(body_channel, str)
self.assertIn('?', body_channel) self.assertIn("?", body_channel)
client = channel_message.get('client') client = channel_message.get("client")
if client is not None: if client is not None:
self.assert_valid_address_and_port(channel_message['client']) self.assert_valid_address_and_port(channel_message["client"])
server = channel_message.get('server') server = channel_message.get("server")
if server is not None: if server is not None:
self.assert_valid_address_and_port(channel_message['server']) self.assert_valid_address_and_port(channel_message["server"])
def assert_valid_http_response_message(self, message, response): def assert_valid_http_response_message(self, message, response):
self.assertTrue(message) self.assertTrue(message)
self.assertTrue(response.startswith(b'HTTP')) self.assertTrue(response.startswith(b"HTTP"))
status_code_bytes = str(message['status']).encode('ascii') status_code_bytes = str(message["status"]).encode("ascii")
self.assertIn(status_code_bytes, response) self.assertIn(status_code_bytes, response)
if 'content' in message: if "content" in message:
self.assertIn(message['content'], response) self.assertIn(message["content"], response)
# Check that headers are in the given order. # Check that headers are in the given order.
# N.b. HTTP spec only enforces that the order of header values is kept, but # N.b. HTTP spec only enforces that the order of header values is kept, but
# the ASGI spec requires that order of all headers is kept. This code # the ASGI spec requires that order of all headers is kept. This code
# checks conformance with the stricter ASGI spec. # checks conformance with the stricter ASGI spec.
if 'headers' in message: if "headers" in message:
for name, value in message['headers']: for name, value in message["headers"]:
expected_header = factories.header_line(name, value) expected_header = factories.header_line(name, value)
# Daphne or Twisted turn our lower cased header names ('foo-bar') into title # Daphne or Twisted turn our lower cased header names ('foo-bar') into title
# case ('Foo-Bar'). So technically we want to to match that the header name is # case ('Foo-Bar'). So technically we want to to match that the header name is
# present while ignoring casing, and want to ensure the value is present without # present while ignoring casing, and want to ensure the value is present without
# altered casing. The approach below does this well enough. # altered casing. The approach below does this well enough.
self.assertIn(expected_header.lower(), response.lower()) self.assertIn(expected_header.lower(), response.lower())
self.assertIn(value.encode('ascii'), response) self.assertIn(value.encode("ascii"), response)
class ASGIWebSocketTestCase(ASGITestCaseBase): class ASGIWebSocketTestCase(ASGITestCaseBase):
@ -162,17 +162,17 @@ class ASGIWebSocketTestCase(ASGITestCaseBase):
Test case with helpers for verifying WebSocket channel messages Test case with helpers for verifying WebSocket channel messages
""" """
def assert_websocket_upgrade(self, response, body=b'', expect_close=False): def assert_websocket_upgrade(self, response, body=b"", expect_close=False):
self.assertIn(b"HTTP/1.1 101 Switching Protocols", response) self.assertIn(b"HTTP/1.1 101 Switching Protocols", response)
self.assertIn(b"Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=\r\n", response) self.assertIn(b"Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=\r\n", response)
self.assertIn(body, response) self.assertIn(body, response)
self.assertEqual(expect_close, response.endswith(b"\x88\x02\x03\xe8")) self.assertEqual(expect_close, response.endswith(b"\x88\x02\x03\xe8"))
def assert_websocket_denied(self, response): def assert_websocket_denied(self, response):
self.assertIn(b'HTTP/1.1 403', response) self.assertIn(b"HTTP/1.1 403", response)
def assert_valid_websocket_connect_message( def assert_valid_websocket_connect_message(
self, channel_message, request_path='/', request_params=None, request_headers=None): self, channel_message, request_path="/", request_params=None, request_headers=None):
""" """
Asserts that a given channel message conforms to the HTTP request section of the ASGI spec. Asserts that a given channel message conforms to the HTTP request section of the ASGI spec.
""" """
@ -181,14 +181,14 @@ class ASGIWebSocketTestCase(ASGITestCaseBase):
self.assert_presence_of_message_keys( self.assert_presence_of_message_keys(
channel_message.keys(), channel_message.keys(),
{'reply_channel', 'path', 'headers', 'order'}, {"reply_channel", "path", "headers", "order"},
{'scheme', 'query_string', 'root_path', 'client', 'server'}) {"scheme", "query_string", "root_path", "client", "server"})
# == Assertions about required channel_message fields == # == Assertions about required channel_message fields ==
self.assert_valid_reply_channel(channel_message['reply_channel']) self.assert_valid_reply_channel(channel_message["reply_channel"])
self.assert_valid_path(channel_message['path'], request_path) self.assert_valid_path(channel_message["path"], request_path)
order = channel_message['order'] order = channel_message["order"]
self.assertIsInstance(order, int) self.assertIsInstance(order, int)
self.assertEqual(order, 0) self.assertEqual(order, 0)
@ -200,39 +200,39 @@ class ASGIWebSocketTestCase(ASGITestCaseBase):
# get one string per header field with values separated by comma. # get one string per header field with values separated by comma.
transformed_request_headers = defaultdict(list) transformed_request_headers = defaultdict(list)
for name, value in (request_headers or []): for name, value in (request_headers or []):
expected_name = name.lower().strip().encode('ascii') expected_name = name.lower().strip().encode("ascii")
expected_value = value.strip().encode('ascii') expected_value = value.strip().encode("ascii")
transformed_request_headers[expected_name].append(expected_value) transformed_request_headers[expected_name].append(expected_value)
final_request_headers = { final_request_headers = {
(name, b','.join(value)) for name, value in transformed_request_headers.items() (name, b",".join(value)) for name, value in transformed_request_headers.items()
} }
# Websockets carry a lot of additional header fields, so instead of verifying that # Websockets carry a lot of additional header fields, so instead of verifying that
# headers look exactly like expected, we just check that the expected header fields # headers look exactly like expected, we just check that the expected header fields
# and values are present - additional header fields (e.g. Sec-WebSocket-Key) are allowed # and values are present - additional header fields (e.g. Sec-WebSocket-Key) are allowed
# and not tested for. # and not tested for.
assert final_request_headers.issubset(set(channel_message['headers'])) assert final_request_headers.issubset(set(channel_message["headers"]))
# == Assertions about optional channel_message fields == # == Assertions about optional channel_message fields ==
scheme = channel_message.get('scheme') scheme = channel_message.get("scheme")
if scheme: if scheme:
self.assertIsInstance(scheme, six.text_type) self.assertIsInstance(scheme, six.text_type)
self.assertIn(scheme, ['ws', 'wss']) self.assertIn(scheme, ["ws", "wss"])
query_string = channel_message.get('query_string') query_string = channel_message.get("query_string")
if query_string: if query_string:
# Assert that query_string is a byte string and still url encoded # Assert that query_string is a byte string and still url encoded
self.assertIsInstance(query_string, six.binary_type) self.assertIsInstance(query_string, six.binary_type)
self.assertEqual(query_string, parse.urlencode(request_params or []).encode('ascii')) self.assertEqual(query_string, parse.urlencode(request_params or []).encode("ascii"))
root_path = channel_message.get('root_path') root_path = channel_message.get("root_path")
if root_path is not None: if root_path is not None:
self.assertIsInstance(root_path, six.text_type) self.assertIsInstance(root_path, six.text_type)
client = channel_message.get('client') client = channel_message.get("client")
if client is not None: if client is not None:
self.assert_valid_address_and_port(channel_message['client']) self.assert_valid_address_and_port(channel_message["client"])
server = channel_message.get('server') server = channel_message.get("server")
if server is not None: if server is not None:
self.assert_valid_address_and_port(channel_message['server']) self.assert_valid_address_and_port(channel_message["server"])

View File

@ -23,8 +23,8 @@ def header_value(headers, header_name):
def parse_x_forwarded_for(headers, def parse_x_forwarded_for(headers,
address_header_name='X-Forwarded-For', address_header_name="X-Forwarded-For",
port_header_name='X-Forwarded-Port', port_header_name="X-Forwarded-Port",
original=None): original=None):
""" """
Parses an X-Forwarded-For header and returns a host/port pair as a list. Parses an X-Forwarded-For header and returns a host/port pair as a list.
@ -50,7 +50,7 @@ def parse_x_forwarded_for(headers,
if address_header_name in headers: if address_header_name in headers:
address_value = header_value(headers, address_header_name) address_value = header_value(headers, address_header_name)
if ',' in address_value: if "," in address_value:
address_value = address_value.split(",")[0].strip() address_value = address_value.split(",")[0].strip()
result = [address_value, 0] result = [address_value, 0]

View File

@ -61,7 +61,7 @@ class WebSocketProtocol(WebSocketServerProtocol):
# Decode websocket subprotocol options # Decode websocket subprotocol options
subprotocols = [] subprotocols = []
for header, value in self.clean_headers: for header, value in self.clean_headers:
if header == b'sec-websocket-protocol': if header == b"sec-websocket-protocol":
subprotocols = [x.strip() for x in self.unquote(value).split(",")] subprotocols = [x.strip() for x in self.unquote(value).split(",")]
# Make new application instance with scope # Make new application instance with scope
self.path = request.path.encode("ascii") self.path = request.path.encode("ascii")

View File

@ -10,47 +10,47 @@ with open(readme_path) as fp:
long_description = fp.read() long_description = fp.read()
setup( setup(
name='daphne', name="daphne",
version=__version__, version=__version__,
url='https://github.com/django/daphne', url="https://github.com/django/daphne",
author='Django Software Foundation', author="Django Software Foundation",
author_email='foundation@djangoproject.com', author_email="foundation@djangoproject.com",
description='Django ASGI (HTTP/WebSocket) server', description="Django ASGI (HTTP/WebSocket) server",
long_description=long_description, long_description=long_description,
license='BSD', license="BSD",
zip_safe=False, zip_safe=False,
package_dir={'twisted': 'daphne/twisted'}, package_dir={"twisted": "daphne/twisted"},
packages=find_packages() + ['twisted.plugins'], packages=find_packages() + ["twisted.plugins"],
include_package_data=True, include_package_data=True,
install_requires=[ install_requires=[
'asgiref~=2.0', "asgiref~=2.0",
'twisted>=17.5', "twisted>=17.5",
'autobahn>=0.18', "autobahn>=0.18",
], ],
setup_requires=[ setup_requires=[
'pytest-runner', "pytest-runner",
], ],
tests_require=[ tests_require=[
'hypothesis', "hypothesis",
'tox', "tox",
'pytest', "pytest",
], ],
entry_points={'console_scripts': [ entry_points={"console_scripts": [
'daphne = daphne.cli:CommandLineInterface.entrypoint', "daphne = daphne.cli:CommandLineInterface.entrypoint",
]}, ]},
classifiers=[ classifiers=[
'Development Status :: 4 - Beta', "Development Status :: 4 - Beta",
'Environment :: Web Environment', "Environment :: Web Environment",
'Intended Audience :: Developers', "Intended Audience :: Developers",
'License :: OSI Approved :: BSD License', "License :: OSI Approved :: BSD License",
'Operating System :: OS Independent', "Operating System :: OS Independent",
'Programming Language :: Python', "Programming Language :: Python",
'Programming Language :: Python :: 2', "Programming Language :: Python :: 2",
'Programming Language :: Python :: 2.7', "Programming Language :: Python :: 2.7",
'Programming Language :: Python :: 3', "Programming Language :: Python :: 3",
'Programming Language :: Python :: 3.4', "Programming Language :: Python :: 3.4",
'Programming Language :: Python :: 3.5', "Programming Language :: Python :: 3.5",
'Programming Language :: Python :: 3.6', "Programming Language :: Python :: 3.6",
'Topic :: Internet :: WWW/HTTP', "Topic :: Internet :: WWW/HTTP",
], ],
) )

View File

@ -13,26 +13,26 @@ class TestEndpointDescriptions(TestCase):
""" """
def testBasics(self): def testBasics(self):
self.assertEqual(build(), [], msg='Empty list returned when no kwargs given') self.assertEqual(build(), [], msg="Empty list returned when no kwargs given")
def testTcpPortBindings(self): def testTcpPortBindings(self):
self.assertEqual( self.assertEqual(
build(port=1234, host='example.com'), build(port=1234, host="example.com"),
['tcp:port=1234:interface=example.com'] ["tcp:port=1234:interface=example.com"]
) )
self.assertEqual( self.assertEqual(
build(port=8000, host='127.0.0.1'), build(port=8000, host="127.0.0.1"),
['tcp:port=8000:interface=127.0.0.1'] ["tcp:port=8000:interface=127.0.0.1"]
) )
self.assertEqual( self.assertEqual(
build(port=8000, host='[200a::1]'), build(port=8000, host="[200a::1]"),
[r'tcp:port=8000:interface=200a\:\:1'] [r'tcp:port=8000:interface=200a\:\:1']
) )
self.assertEqual( self.assertEqual(
build(port=8000, host='200a::1'), build(port=8000, host="200a::1"),
[r'tcp:port=8000:interface=200a\:\:1'] [r'tcp:port=8000:interface=200a\:\:1']
) )
@ -43,19 +43,19 @@ class TestEndpointDescriptions(TestCase):
) )
self.assertRaises( self.assertRaises(
ValueError, ValueError,
build, host='example.com' build, host="example.com"
) )
def testUnixSocketBinding(self): def testUnixSocketBinding(self):
self.assertEqual( self.assertEqual(
build(unix_socket='/tmp/daphne.sock'), build(unix_socket="/tmp/daphne.sock"),
['unix:/tmp/daphne.sock'] ["unix:/tmp/daphne.sock"]
) )
def testFileDescriptorBinding(self): def testFileDescriptorBinding(self):
self.assertEqual( self.assertEqual(
build(file_descriptor=5), build(file_descriptor=5),
['fd:fileno=5'] ["fd:fileno=5"]
) )
def testMultipleEnpoints(self): def testMultipleEnpoints(self):
@ -63,15 +63,15 @@ class TestEndpointDescriptions(TestCase):
sorted( sorted(
build( build(
file_descriptor=123, file_descriptor=123,
unix_socket='/tmp/daphne.sock', unix_socket="/tmp/daphne.sock",
port=8080, port=8080,
host='10.0.0.1' host="10.0.0.1"
) )
), ),
sorted([ sorted([
'tcp:port=8080:interface=10.0.0.1', "tcp:port=8080:interface=10.0.0.1",
'unix:/tmp/daphne.sock', "unix:/tmp/daphne.sock",
'fd:fileno=123' "fd:fileno=123"
]) ])
) )
@ -112,7 +112,7 @@ class TestCLIInterface(TestCase):
Passes in a fake application automatically. Passes in a fake application automatically.
""" """
cli = self.TestedCLI() cli = self.TestedCLI()
cli.run(args + ['daphne:__version__']) # We just pass something importable as app cli.run(args + ["daphne:__version__"]) # We just pass something importable as app
# Check the server got all arguments as intended # Check the server got all arguments as intended
for key, value in server_kwargs.items(): for key, value in server_kwargs.items():
# Get the value and sort it if it's a list (for endpoint checking) # Get the value and sort it if it's a list (for endpoint checking)
@ -123,7 +123,7 @@ class TestCLIInterface(TestCase):
self.assertEqual( self.assertEqual(
value, value,
actual_value, actual_value,
'Wrong value for server kwarg %s: %r != %r' % ( "Wrong value for server kwarg %s: %r != %r" % (
key, key,
value, value,
actual_value, actual_value,
@ -137,65 +137,65 @@ class TestCLIInterface(TestCase):
self.assertCLI( self.assertCLI(
[], [],
{ {
'endpoints': ['tcp:port=8000:interface=127.0.0.1'], "endpoints": ["tcp:port=8000:interface=127.0.0.1"],
}, },
) )
self.assertCLI( self.assertCLI(
['-p', '123'], ["-p", "123"],
{ {
'endpoints': ['tcp:port=123:interface=127.0.0.1'], "endpoints": ["tcp:port=123:interface=127.0.0.1"],
}, },
) )
self.assertCLI( self.assertCLI(
['-b', '10.0.0.1'], ["-b", "10.0.0.1"],
{ {
'endpoints': ['tcp:port=8000:interface=10.0.0.1'], "endpoints": ["tcp:port=8000:interface=10.0.0.1"],
}, },
) )
self.assertCLI( self.assertCLI(
['-b', '200a::1'], ["-b", "200a::1"],
{ {
'endpoints': [r'tcp:port=8000:interface=200a\:\:1'], "endpoints": [r'tcp:port=8000:interface=200a\:\:1'],
}, },
) )
self.assertCLI( self.assertCLI(
['-b', '[200a::1]'], ["-b", "[200a::1]"],
{ {
'endpoints': [r'tcp:port=8000:interface=200a\:\:1'], "endpoints": [r'tcp:port=8000:interface=200a\:\:1'],
}, },
) )
self.assertCLI( self.assertCLI(
['-p', '8080', '-b', 'example.com'], ["-p", "8080", "-b", "example.com"],
{ {
'endpoints': ['tcp:port=8080:interface=example.com'], "endpoints": ["tcp:port=8080:interface=example.com"],
}, },
) )
def testUnixSockets(self): def testUnixSockets(self):
self.assertCLI( self.assertCLI(
['-p', '8080', '-u', '/tmp/daphne.sock'], ["-p", "8080", "-u", "/tmp/daphne.sock"],
{ {
'endpoints': [ "endpoints": [
'tcp:port=8080:interface=127.0.0.1', "tcp:port=8080:interface=127.0.0.1",
'unix:/tmp/daphne.sock', "unix:/tmp/daphne.sock",
], ],
}, },
) )
self.assertCLI( self.assertCLI(
['-b', 'example.com', '-u', '/tmp/daphne.sock'], ["-b", "example.com", "-u", "/tmp/daphne.sock"],
{ {
'endpoints': [ "endpoints": [
'tcp:port=8000:interface=example.com', "tcp:port=8000:interface=example.com",
'unix:/tmp/daphne.sock', "unix:/tmp/daphne.sock",
], ],
}, },
) )
self.assertCLI( self.assertCLI(
['-u', '/tmp/daphne.sock', '--fd', '5'], ["-u", "/tmp/daphne.sock", "--fd", "5"],
{ {
'endpoints': [ "endpoints": [
'fd:fileno=5', "fd:fileno=5",
'unix:/tmp/daphne.sock' "unix:/tmp/daphne.sock"
], ],
}, },
) )
@ -205,20 +205,20 @@ class TestCLIInterface(TestCase):
Tests mixing the shortcut options with the endpoint string options. Tests mixing the shortcut options with the endpoint string options.
""" """
self.assertCLI( self.assertCLI(
['-p', '8080', '-e', 'unix:/tmp/daphne.sock'], ["-p", "8080", "-e", "unix:/tmp/daphne.sock"],
{ {
'endpoints': [ "endpoints": [
'tcp:port=8080:interface=127.0.0.1', "tcp:port=8080:interface=127.0.0.1",
'unix:/tmp/daphne.sock' "unix:/tmp/daphne.sock"
], ],
}, },
) )
self.assertCLI( self.assertCLI(
['-p', '8080', '-e', 'tcp:port=8080:interface=127.0.0.1'], ["-p", "8080", "-e", "tcp:port=8080:interface=127.0.0.1"],
{ {
'endpoints': [ "endpoints": [
'tcp:port=8080:interface=127.0.0.1', "tcp:port=8080:interface=127.0.0.1",
'tcp:port=8080:interface=127.0.0.1', "tcp:port=8080:interface=127.0.0.1",
], ],
}, },
) )
@ -228,10 +228,10 @@ class TestCLIInterface(TestCase):
Tests entirely custom endpoints Tests entirely custom endpoints
""" """
self.assertCLI( self.assertCLI(
['-e', 'imap:'], ["-e", "imap:"],
{ {
'endpoints': [ "endpoints": [
'imap:', "imap:",
], ],
}, },
) )