mirror of
https://github.com/django/daphne.git
synced 2025-07-12 17:02:18 +03:00
Merge 50979f1679
into 630caed915
This commit is contained in:
commit
9489246e61
14
README.rst
14
README.rst
|
@ -105,6 +105,20 @@ should start with a slash, but not end with one; for example::
|
||||||
daphne --root-path=/forum django_project.asgi:application
|
daphne --root-path=/forum django_project.asgi:application
|
||||||
|
|
||||||
|
|
||||||
|
Permessage compression
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Daphne supports and by default accepts ``permessage-deflate`` compression
|
||||||
|
(`permessage-deflate specification <http://tools.ietf.org/html/draft-ietf-hybi-permessage-compression>`_).
|
||||||
|
Additional ``permessage-bzip2``, ``permessage-snappy`` compressions will be also enabled by default if
|
||||||
|
``bz2`` and `snappy <https://snappy.math.uic.edu/>`_ python packages are available in daphne environment.
|
||||||
|
The compression implementation is provided by
|
||||||
|
`Autobahn|Python <https://github.com/crossbario/autobahn-python>`_ package, see:
|
||||||
|
`permessage-deflate <https://github.com/crossbario/autobahn-python/blob/master/autobahn/websocket/compress_deflate.py>`_,
|
||||||
|
`permessage-bzip2 <https://github.com/crossbario/autobahn-python/blob/master/autobahn/websocket/compress_bzip2.py>`_,
|
||||||
|
`permessage-snappy <https://github.com/crossbario/autobahn-python/blob/master/autobahn/websocket/compress_snappy.py>`_.
|
||||||
|
|
||||||
|
|
||||||
Python Support
|
Python Support
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ import time
|
||||||
from concurrent.futures import CancelledError
|
from concurrent.futures import CancelledError
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
|
from autobahn.websocket.compress import PERMESSAGE_COMPRESSION_EXTENSION as EXTENSIONS
|
||||||
from twisted.internet import defer, reactor
|
from twisted.internet import defer, reactor
|
||||||
from twisted.internet.endpoints import serverFromString
|
from twisted.internet.endpoints import serverFromString
|
||||||
from twisted.logger import STDLibLogObserver, globalLogBeginner
|
from twisted.logger import STDLibLogObserver, globalLogBeginner
|
||||||
|
@ -55,6 +56,11 @@ class Server:
|
||||||
request_buffer_size=8192,
|
request_buffer_size=8192,
|
||||||
websocket_timeout=86400,
|
websocket_timeout=86400,
|
||||||
websocket_connect_timeout=20,
|
websocket_connect_timeout=20,
|
||||||
|
websocket_permessage_compression_extensions=(
|
||||||
|
"permessage-deflate",
|
||||||
|
"permessage-bzip2",
|
||||||
|
"permessage-snappy",
|
||||||
|
),
|
||||||
ping_interval=20,
|
ping_interval=20,
|
||||||
ping_timeout=30,
|
ping_timeout=30,
|
||||||
root_path="",
|
root_path="",
|
||||||
|
@ -83,6 +89,9 @@ class Server:
|
||||||
self.websocket_timeout = websocket_timeout
|
self.websocket_timeout = websocket_timeout
|
||||||
self.websocket_connect_timeout = websocket_connect_timeout
|
self.websocket_connect_timeout = websocket_connect_timeout
|
||||||
self.websocket_handshake_timeout = websocket_handshake_timeout
|
self.websocket_handshake_timeout = websocket_handshake_timeout
|
||||||
|
self.websocket_permessage_compression_extensions = (
|
||||||
|
websocket_permessage_compression_extensions
|
||||||
|
)
|
||||||
self.application_close_timeout = application_close_timeout
|
self.application_close_timeout = application_close_timeout
|
||||||
self.root_path = root_path
|
self.root_path = root_path
|
||||||
self.verbosity = verbosity
|
self.verbosity = verbosity
|
||||||
|
@ -104,6 +113,7 @@ class Server:
|
||||||
autoPingTimeout=self.ping_timeout,
|
autoPingTimeout=self.ping_timeout,
|
||||||
allowNullOrigin=True,
|
allowNullOrigin=True,
|
||||||
openHandshakeTimeout=self.websocket_handshake_timeout,
|
openHandshakeTimeout=self.websocket_handshake_timeout,
|
||||||
|
perMessageCompressionAccept=self.accept_permessage_compression_extension,
|
||||||
)
|
)
|
||||||
if self.verbosity <= 1:
|
if self.verbosity <= 1:
|
||||||
# Redirect the Twisted log to nowhere
|
# Redirect the Twisted log to nowhere
|
||||||
|
@ -258,6 +268,21 @@ class Server:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def accept_permessage_compression_extension(self, offers):
|
||||||
|
"""
|
||||||
|
Accepts websocket compression extension as required by `autobahn` package.
|
||||||
|
"""
|
||||||
|
for offer in offers:
|
||||||
|
for ext in self.websocket_permessage_compression_extensions:
|
||||||
|
if ext in EXTENSIONS and isinstance(offer, EXTENSIONS[ext]["Offer"]):
|
||||||
|
return EXTENSIONS[ext]["OfferAccept"](offer)
|
||||||
|
elif ext not in EXTENSIONS:
|
||||||
|
logger.warning(
|
||||||
|
"Compression extension %s could not be accepted. "
|
||||||
|
"It is not supported or a dependency is missing.",
|
||||||
|
ext,
|
||||||
|
)
|
||||||
|
|
||||||
### Utility
|
### Utility
|
||||||
|
|
||||||
def application_checker(self):
|
def application_checker(self):
|
||||||
|
|
|
@ -187,7 +187,10 @@ class WebSocketProtocol(WebSocketServerProtocol):
|
||||||
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"] == "websocket.accept":
|
if message["type"] == "websocket.accept":
|
||||||
self.serverAccept(message.get("subprotocol", None))
|
self.serverAccept(
|
||||||
|
message.get("subprotocol", None), message.get("headers", None)
|
||||||
|
)
|
||||||
|
|
||||||
elif message["type"] == "websocket.close":
|
elif message["type"] == "websocket.close":
|
||||||
if self.state == self.STATE_CONNECTING:
|
if self.state == self.STATE_CONNECTING:
|
||||||
self.serverReject()
|
self.serverReject()
|
||||||
|
@ -219,11 +222,15 @@ class WebSocketProtocol(WebSocketServerProtocol):
|
||||||
else:
|
else:
|
||||||
self.sendCloseFrame(code=1011)
|
self.sendCloseFrame(code=1011)
|
||||||
|
|
||||||
def serverAccept(self, subprotocol=None):
|
def serverAccept(self, subprotocol=None, headers=None):
|
||||||
"""
|
"""
|
||||||
Called when we get a message saying to accept the connection.
|
Called when we get a message saying to accept the connection.
|
||||||
"""
|
"""
|
||||||
self.handshake_deferred.callback(subprotocol)
|
if headers is None:
|
||||||
|
self.handshake_deferred.callback(subprotocol)
|
||||||
|
else:
|
||||||
|
headers_dict = {key.decode(): value.decode() for key, value in headers}
|
||||||
|
self.handshake_deferred.callback((subprotocol, headers_dict))
|
||||||
del self.handshake_deferred
|
del self.handshake_deferred
|
||||||
logger.debug("WebSocket %s accepted by application", self.client_addr)
|
logger.debug("WebSocket %s accepted by application", self.client_addr)
|
||||||
|
|
||||||
|
|
|
@ -139,6 +139,60 @@ class TestWebsocket(DaphneTestCase):
|
||||||
self.assert_valid_websocket_scope(scope, subprotocols=subprotocols)
|
self.assert_valid_websocket_scope(scope, subprotocols=subprotocols)
|
||||||
self.assert_valid_websocket_connect_message(messages[0])
|
self.assert_valid_websocket_connect_message(messages[0])
|
||||||
|
|
||||||
|
def test_accept_permessage_deflate_extension(self):
|
||||||
|
"""
|
||||||
|
Tests that permessage-deflate extension is successfuly accepted
|
||||||
|
by underlying `autobahn` package.
|
||||||
|
"""
|
||||||
|
|
||||||
|
headers = [
|
||||||
|
(
|
||||||
|
b"Sec-WebSocket-Extensions",
|
||||||
|
b"permessage-deflate; client_max_window_bits",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
with DaphneTestingInstance() as test_app:
|
||||||
|
test_app.add_send_messages(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "websocket.accept",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
sock, subprotocol = self.websocket_handshake(
|
||||||
|
test_app,
|
||||||
|
headers=headers,
|
||||||
|
)
|
||||||
|
# Validate the scope and messages we got
|
||||||
|
scope, messages = test_app.get_received()
|
||||||
|
self.assert_valid_websocket_connect_message(messages[0])
|
||||||
|
|
||||||
|
def test_accept_custom_extension(self):
|
||||||
|
"""
|
||||||
|
Tests that custom headers can be accpeted during handshake.
|
||||||
|
"""
|
||||||
|
with DaphneTestingInstance() as test_app:
|
||||||
|
test_app.add_send_messages(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "websocket.accept",
|
||||||
|
"headers": [(b"Sec-WebSocket-Extensions", b"custom-extension")],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
sock, subprotocol = self.websocket_handshake(
|
||||||
|
test_app,
|
||||||
|
headers=[
|
||||||
|
(b"Sec-WebSocket-Extensions", b"custom-extension"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
# Validate the scope and messages we got
|
||||||
|
scope, messages = test_app.get_received()
|
||||||
|
self.assert_valid_websocket_connect_message(messages[0])
|
||||||
|
|
||||||
def test_xff(self):
|
def test_xff(self):
|
||||||
"""
|
"""
|
||||||
Tests that X-Forwarded-For headers get parsed right
|
Tests that X-Forwarded-For headers get parsed right
|
||||||
|
|
Loading…
Reference in New Issue
Block a user