[Python-modules-commits] [python-daphne] 02/07: New upstream version 1.4.2
Michael Fladischer
fladi at moszumanska.debian.org
Mon Jan 8 16:55:07 UTC 2018
This is an automated email from the git hooks/post-receive script.
fladi pushed a commit to branch debian/master
in repository python-daphne.
commit 0b8b502ffa80153c33b1bfa3cb6b25f3aeebf919
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date: Mon Jan 8 16:18:48 2018 +0100
New upstream version 1.4.2
---
.travis.yml | 1 +
CHANGELOG.txt | 22 ++++++++++++++
README.rst | 2 +-
daphne/__init__.py | 2 +-
daphne/cli.py | 1 +
daphne/http_protocol.py | 13 ++++++---
daphne/server.py | 16 +++++++++--
daphne/tests/__init__.py | 7 +++++
daphne/tests/test_http_request.py | 5 ++--
daphne/tests/test_http_response.py | 2 +-
daphne/tests/test_utils.py | 59 +++++++++++++++++++++++++++-----------
daphne/tests/test_ws.py | 3 +-
daphne/utils.py | 41 +++++++++++++++++---------
daphne/ws_protocol.py | 5 ++--
14 files changed, 135 insertions(+), 44 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 9672140..da96f02 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,4 +1,5 @@
sudo: false
+dist: trusty
language: python
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 5a1b551..269cece 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,3 +1,25 @@
+1.4.2 (2018-01-05)
+------------------
+
+* Bugfix for WebSocket protocol when X-Forwarded-For is turned on.
+
+
+1.4.1 (2018-01-02)
+------------------
+
+* Bugfix for a bad merge of HTTPFactory for X-Forwarded-Proto causing Daphne
+ to not start.
+
+
+1.4.0 (2018-01-02)
+------------------
+
+* The X-Forwarded-Proto header can now be used to pass along protocol from
+ a reverse proxy.
+
+* WebSocket headers are now correctly always passed as bytestrings.
+
+
1.3.0 (2017-06-16)
------------------
diff --git a/README.rst b/README.rst
index bab5110..aa285b6 100644
--- a/README.rst
+++ b/README.rst
@@ -57,7 +57,7 @@ Daphne 1.1 and above supports terminating HTTP/2 connections natively. You'll
need to do a couple of things to get it working, though. First, you need to
make sure you install the Twisted ``http2`` and ``tls`` extras::
- pip install -U Twisted[tls,http2]
+ pip install -U 'Twisted[tls,http2]'
Next, because all current browsers only support HTTP/2 when using TLS, you will
need to start Daphne with TLS turned on, which can be done using the Twisted endpoint syntax::
diff --git a/daphne/__init__.py b/daphne/__init__.py
index 67bc602..daa50c7 100755
--- a/daphne/__init__.py
+++ b/daphne/__init__.py
@@ -1 +1 @@
-__version__ = "1.3.0"
+__version__ = "1.4.2"
diff --git a/daphne/cli.py b/daphne/cli.py
index b27cb06..a149632 100755
--- a/daphne/cli.py
+++ b/daphne/cli.py
@@ -213,6 +213,7 @@ class CommandLineInterface(object):
verbosity=args.verbosity,
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_proto_header='X-Forwarded-Proto' if args.proxy_headers else None,
force_sync=args.force_sync,
)
self.server.run()
diff --git a/daphne/http_protocol.py b/daphne/http_protocol.py
index 32dc276..41e8552 100755
--- a/daphne/http_protocol.py
+++ b/daphne/http_protocol.py
@@ -80,12 +80,16 @@ class WebRequest(http.Request):
self.client_addr = None
self.server_addr = None
+ self.client_scheme = 'https' if self.isSecure() else 'http'
+
if self.factory.proxy_forwarded_address_header:
- self.client_addr = parse_x_forwarded_for(
+ self.client_addr, self.client_scheme = parse_x_forwarded_for(
self.requestHeaders,
self.factory.proxy_forwarded_address_header,
self.factory.proxy_forwarded_port_header,
- self.client_addr
+ self.factory.proxy_forwarded_proto_header,
+ self.client_addr,
+ self.client_scheme
)
# Check for unicodeish path (or it'll crash when trying to parse)
@@ -166,7 +170,7 @@ class WebRequest(http.Request):
"method": self.method.decode("ascii"),
"path": self.unquote(self.path),
"root_path": self.root_path,
- "scheme": "https" if self.isSecure() else "http",
+ "scheme": self.client_scheme,
"query_string": self.query_string,
"headers": self.clean_headers,
"body": self.content.read(),
@@ -308,7 +312,7 @@ class HTTPFactory(http.HTTPFactory):
routed appropriately.
"""
- def __init__(self, channel_layer, action_logger=None, send_channel=None, timeout=120, websocket_timeout=86400, ping_interval=20, ping_timeout=30, ws_protocols=None, root_path="", websocket_connect_timeout=30, proxy_forwarded_address_header=None, proxy_forwarded_port_header=None, websocket_handshake_timeout=5):
+ def __init__(self, channel_layer, action_logger=None, send_channel=None, timeout=120, websocket_timeout=86400, ping_interval=20, ping_timeout=30, ws_protocols=None, root_path="", websocket_connect_timeout=30, proxy_forwarded_address_header=None, proxy_forwarded_port_header=None, proxy_forwarded_proto_header=None, websocket_handshake_timeout=5):
http.HTTPFactory.__init__(self)
self.channel_layer = channel_layer
self.action_logger = action_logger
@@ -320,6 +324,7 @@ class HTTPFactory(http.HTTPFactory):
self.ping_interval = ping_interval
self.proxy_forwarded_address_header = proxy_forwarded_address_header
self.proxy_forwarded_port_header = proxy_forwarded_port_header
+ self.proxy_forwarded_proto_header = proxy_forwarded_proto_header
# We track all sub-protocols for response channel mapping
self.reply_protocols = {}
# Make a factory for WebSocket protocols
diff --git a/daphne/server.py b/daphne/server.py
index 20b19cb..fc9f761 100755
--- a/daphne/server.py
+++ b/daphne/server.py
@@ -36,6 +36,7 @@ class Server(object):
root_path="",
proxy_forwarded_address_header=None,
proxy_forwarded_port_header=None,
+ proxy_forwarded_proto_header=None,
force_sync=False,
verbosity=1,
websocket_handshake_timeout=5
@@ -66,6 +67,7 @@ class Server(object):
self.ping_timeout = ping_timeout
self.proxy_forwarded_address_header = proxy_forwarded_address_header
self.proxy_forwarded_port_header = proxy_forwarded_port_header
+ self.proxy_forwarded_proto_header = proxy_forwarded_proto_header
# If they did not provide a websocket timeout, default it to the
# channel layer's group_expiry value if present, or one day if not.
self.websocket_timeout = websocket_timeout or getattr(channel_layer, "group_expiry", 86400)
@@ -95,6 +97,7 @@ class Server(object):
root_path=self.root_path,
proxy_forwarded_address_header=self.proxy_forwarded_address_header,
proxy_forwarded_port_header=self.proxy_forwarded_port_header,
+ proxy_forwarded_proto_header=self.proxy_forwarded_proto_header,
websocket_handshake_timeout=self.websocket_handshake_timeout
)
if self.verbosity <= 1:
@@ -121,10 +124,19 @@ class Server(object):
logger.info("Listening on endpoint %s" % socket_description)
# Twisted requires str on python2 (not unicode) and str on python3 (not bytes)
ep = serverFromString(reactor, str(socket_description))
- self.listeners.append(ep.listen(self.factory))
+ listener = ep.listen(self.factory)
+ listener.addErrback(self.on_listener_error)
+ self.listeners.append(listener)
reactor.run(installSignalHandlers=self.signal_handlers)
+ def on_listener_error(self, failure):
+ """
+ Callback function used to process interface listener errors.
+ """
+
+ logger.error(failure.getErrorMessage())
+
def backend_reader_sync(self):
"""
Runs as an-often-as-possible task with the reactor, unless there was
@@ -215,7 +227,7 @@ def build_endpoint_description_strings(
if unix_socket:
socket_descriptions.append('unix:%s' % unix_socket)
- if file_descriptor:
+ if file_descriptor is not None:
socket_descriptions.append('fd:fileno=%d' % int(file_descriptor))
return socket_descriptions
diff --git a/daphne/tests/__init__.py b/daphne/tests/__init__.py
index e69de29..3f43747 100644
--- a/daphne/tests/__init__.py
+++ b/daphne/tests/__init__.py
@@ -0,0 +1,7 @@
+from hypothesis import HealthCheck, settings
+
+settings.register_profile(
+ 'daphne',
+ settings(suppress_health_check=[HealthCheck.too_slow]),
+)
+settings.load_profile('daphne')
diff --git a/daphne/tests/test_http_request.py b/daphne/tests/test_http_request.py
index ee38ab9..dd64ae3 100644
--- a/daphne/tests/test_http_request.py
+++ b/daphne/tests/test_http_request.py
@@ -8,7 +8,7 @@ import unittest
from six.moves.urllib import parse
from asgiref.inmemory import ChannelLayer
-from hypothesis import given, assume, settings, HealthCheck
+from hypothesis import given, assume
from twisted.test import proto_helpers
from daphne.http_protocol import HTTPFactory
@@ -99,7 +99,6 @@ class TestHTTPRequestSpec(testcases.ASGIHTTPTestCase):
request_body=http_strategies.http_body(),
)
# This test is slow enough that on Travis, hypothesis sometimes complains.
- @settings(suppress_health_check=[HealthCheck.too_slow])
def test_kitchen_sink(
self, request_method, request_path, request_params, request_headers, request_body):
"""
@@ -172,6 +171,7 @@ class TestProxyHandling(unittest.TestCase):
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.factory.proxy_forwarded_proto_header = 'X-Forwarded-Proto'
self.proto.dataReceived(
b"GET /te%20st-%C3%A0/?foo=+bar HTTP/1.1\r\n" +
b"Host: somewhere.com\r\n" +
@@ -186,6 +186,7 @@ class TestProxyHandling(unittest.TestCase):
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.factory.proxy_forwarded_proto_header = 'X-Forwarded-Proto'
self.proto.dataReceived(
b"GET /te%20st-%C3%A0/?foo=+bar HTTP/1.1\r\n" +
b"Host: somewhere.com\r\n" +
diff --git a/daphne/tests/test_http_response.py b/daphne/tests/test_http_response.py
index a650deb..f5f5a28 100644
--- a/daphne/tests/test_http_response.py
+++ b/daphne/tests/test_http_response.py
@@ -70,7 +70,7 @@ class TestHTTPResponseSpec(testcases.ASGIHTTPTestCase):
@given(
headers=http_strategies.headers(),
- body=http_strategies.http_body()
+ body=http_strategies.http_body(),
)
def test_kitchen_sink(self, headers, body):
"""
diff --git a/daphne/tests/test_utils.py b/daphne/tests/test_utils.py
index 525fec9..9e962a6 100644
--- a/daphne/tests/test_utils.py
+++ b/daphne/tests/test_utils.py
@@ -16,11 +16,13 @@ class TestXForwardedForHttpParsing(TestCase):
def test_basic(self):
headers = Headers({
b'X-Forwarded-For': [b'10.1.2.3'],
- b'X-Forwarded-Port': [b'1234']
+ b'X-Forwarded-Port': [b'1234'],
+ b'X-Forwarded-Proto': [b'https']
})
result = parse_x_forwarded_for(headers)
- self.assertEqual(result, ['10.1.2.3', 1234])
- self.assertIsInstance(result[0], six.text_type)
+ self.assertEqual(result, (['10.1.2.3', 1234], 'https'))
+ self.assertIsInstance(result[0][0], six.text_type)
+ self.assertIsInstance(result[1], six.text_type)
def test_address_only(self):
headers = Headers({
@@ -28,7 +30,7 @@ class TestXForwardedForHttpParsing(TestCase):
})
self.assertEqual(
parse_x_forwarded_for(headers),
- ['10.1.2.3', 0]
+ (['10.1.2.3', 0], None)
)
def test_v6_address(self):
@@ -37,7 +39,7 @@ class TestXForwardedForHttpParsing(TestCase):
})
self.assertEqual(
parse_x_forwarded_for(headers),
- ['1043::a321:0001', 0]
+ (['1043::a321:0001', 0], None)
)
def test_multiple_proxys(self):
@@ -46,19 +48,39 @@ class TestXForwardedForHttpParsing(TestCase):
})
self.assertEqual(
parse_x_forwarded_for(headers),
- ['10.1.2.3', 0]
+ (['10.1.2.3', 0], None)
)
- def test_original(self):
+ def test_original_addr(self):
+ headers = Headers({})
+ self.assertEqual(
+ parse_x_forwarded_for(headers, original_addr=['127.0.0.1', 80]),
+ (['127.0.0.1', 80], None)
+ )
+
+ def test_original_proto(self):
headers = Headers({})
self.assertEqual(
- parse_x_forwarded_for(headers, original=['127.0.0.1', 80]),
- ['127.0.0.1', 80]
+ parse_x_forwarded_for(headers, original_scheme='http'),
+ (None, 'http')
)
def test_no_original(self):
headers = Headers({})
- self.assertIsNone(parse_x_forwarded_for(headers))
+ self.assertEqual(
+ parse_x_forwarded_for(headers),
+ (None, None)
+ )
+
+ def test_address_and_proto(self):
+ headers = Headers({
+ b'X-Forwarded-For': [b'10.1.2.3'],
+ b'X-Forwarded-Proto': [b'https'],
+ })
+ self.assertEqual(
+ parse_x_forwarded_for(headers),
+ (['10.1.2.3', 0], 'https')
+ )
class TestXForwardedForWsParsing(TestCase):
@@ -73,7 +95,7 @@ class TestXForwardedForWsParsing(TestCase):
}
self.assertEqual(
parse_x_forwarded_for(headers),
- ['10.1.2.3', 1234]
+ (['10.1.2.3', 1234], None)
)
def test_address_only(self):
@@ -82,7 +104,7 @@ class TestXForwardedForWsParsing(TestCase):
}
self.assertEqual(
parse_x_forwarded_for(headers),
- ['10.1.2.3', 0]
+ (['10.1.2.3', 0], None)
)
def test_v6_address(self):
@@ -91,7 +113,7 @@ class TestXForwardedForWsParsing(TestCase):
}
self.assertEqual(
parse_x_forwarded_for(headers),
- ['1043::a321:0001', 0]
+ (['1043::a321:0001', 0], None)
)
def test_multiple_proxys(self):
@@ -100,16 +122,19 @@ class TestXForwardedForWsParsing(TestCase):
}
self.assertEqual(
parse_x_forwarded_for(headers),
- ['10.1.2.3', 0]
+ (['10.1.2.3', 0], None)
)
def test_original(self):
headers = {}
self.assertEqual(
- parse_x_forwarded_for(headers, original=['127.0.0.1', 80]),
- ['127.0.0.1', 80]
+ parse_x_forwarded_for(headers, original_addr=['127.0.0.1', 80]),
+ (['127.0.0.1', 80], None)
)
def test_no_original(self):
headers = {}
- self.assertIsNone(parse_x_forwarded_for(headers))
+ self.assertEqual(
+ parse_x_forwarded_for(headers),
+ (None, None)
+ )
diff --git a/daphne/tests/test_ws.py b/daphne/tests/test_ws.py
index ceb1deb..f03655a 100644
--- a/daphne/tests/test_ws.py
+++ b/daphne/tests/test_ws.py
@@ -1,7 +1,7 @@
# coding: utf8
from __future__ import unicode_literals
-from hypothesis import assume, given, strategies
+from hypothesis import assume, given, strategies, settings
from twisted.test import proto_helpers
from asgiref.inmemory import ChannelLayer
@@ -66,6 +66,7 @@ class TestHandshake(testcases.ASGIWebSocketTestCase):
params=http_strategies.query_params(),
headers=http_strategies.headers(),
)
+ @settings(perform_health_check=False)
def test_connection(self, path, params, headers):
message = WebSocketConnection().connect(path, params, headers)
self.assert_valid_websocket_connect_message(message, path, params, headers)
diff --git a/daphne/utils.py b/daphne/utils.py
index cb8043c..04cef09 100644
--- a/daphne/utils.py
+++ b/daphne/utils.py
@@ -5,41 +5,51 @@ def header_value(headers, header_name):
value = headers[header_name]
if isinstance(value, list):
value = value[0]
- return value.decode("utf-8")
+ # decode to urf-8 if value is bytes
+ if isinstance(value, bytes):
+ value = value.decode("utf-8")
+ return value
def parse_x_forwarded_for(headers,
address_header_name='X-Forwarded-For',
port_header_name='X-Forwarded-Port',
- original=None):
+ proto_header_name='X-Forwarded-Proto',
+ original_addr=None,
+ original_scheme=None):
"""
Parses an X-Forwarded-For header and returns a host/port pair as a list.
@param headers: The twisted-style object containing a request's headers
@param address_header_name: The name of the expected host header
@param port_header_name: The name of the expected port header
- @param original: A host/port pair that should be returned if the headers are not in the request
- @return: A list containing a host (string) as the first entry and a port (int) as the second.
+ @param proto_header_name: The name of the expected protocol header
+ @param original_addr: A host/port pair that should be returned if the headers are not in the request
+ @param original_scheme: A scheme that should be returned if the headers are not in the request
+ @return: A tuple containing a list [host (string), port (int)] as the first entry and a proto (string) as the second
"""
if not address_header_name:
- return original
+ return (original_addr, original_scheme)
- # Convert twisted-style headers into dicts
if isinstance(headers, Headers):
+ # Convert twisted-style headers into a dict
headers = dict(headers.getAllRawHeaders())
-
- # Lowercase all header names in the dict
- headers = {name.lower(): values for name, values in headers.items()}
+ # Lowercase all header keys
+ headers = {name.lower(): values for name, values in headers.items()}
+ else:
+ # Lowercase (and encode to utf-8 where needed) non-twisted header keys
+ headers = {name.lower() if isinstance(name, bytes) else name.lower().encode("utf-8"): values for name, values in headers.items()}
address_header_name = address_header_name.lower().encode("utf-8")
- result = original
+ result_addr = original_addr
+ result_scheme = original_scheme
if address_header_name in headers:
address_value = header_value(headers, address_header_name)
if ',' in address_value:
address_value = address_value.split(",")[0].strip()
- result = [address_value, 0]
+ result_addr = [address_value, 0]
if port_header_name:
# We only want to parse the X-Forwarded-Port header if we also parsed the X-Forwarded-For
@@ -48,8 +58,13 @@ def parse_x_forwarded_for(headers,
if port_header_name in headers:
port_value = header_value(headers, port_header_name)
try:
- result[1] = int(port_value)
+ result_addr[1] = int(port_value)
except ValueError:
pass
- return result
+ if proto_header_name:
+ proto_header_name = proto_header_name.lower().encode("utf-8")
+ if proto_header_name in headers:
+ result_scheme = header_value(headers, proto_header_name)
+
+ return result_addr, result_scheme
diff --git a/daphne/ws_protocol.py b/daphne/ws_protocol.py
index 502f340..45a3ab5 100755
--- a/daphne/ws_protocol.py
+++ b/daphne/ws_protocol.py
@@ -4,7 +4,7 @@ import logging
import six
import time
import traceback
-from six.moves.urllib_parse import unquote, urlencode
+from six.moves.urllib_parse import unquote
from twisted.internet import defer
from autobahn.twisted.websocket import WebSocketServerProtocol, WebSocketServerFactory, ConnectionDeny
@@ -57,10 +57,11 @@ class WebSocketProtocol(WebSocketServerProtocol):
self.server_addr = None
if self.main_factory.proxy_forwarded_address_header:
- self.client_addr = parse_x_forwarded_for(
+ self.client_addr, self.client_scheme = parse_x_forwarded_for(
self.http_headers,
self.main_factory.proxy_forwarded_address_header,
self.main_factory.proxy_forwarded_port_header,
+ self.main_factory.proxy_forwarded_proto_header,
self.client_addr
)
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-daphne.git
More information about the Python-modules-commits
mailing list