mirror of
https://github.com/django/daphne.git
synced 2025-07-30 00:49:44 +03:00
Channel and http session from http (#564)
* Add a new auth decorator to get the user and rehydrate the http session * Add http_user_and_session, taking precedence over http_user, applying the channel_and_http_session_user_from_http decorator (a superset of http user functionality) * Only set session cookies on the first send, since subsequent real requests don't have access to HTTP information * Add a test for new http_user_and_session WebsocketConsumer attribute * Fix isort check
This commit is contained in:
parent
526ad65e73
commit
08ff57ac9b
|
@ -2,7 +2,7 @@ import functools
|
||||||
|
|
||||||
from django.contrib import auth
|
from django.contrib import auth
|
||||||
|
|
||||||
from .sessions import channel_session, http_session
|
from .sessions import channel_and_http_session, channel_session, http_session
|
||||||
|
|
||||||
|
|
||||||
def transfer_user(from_session, to_session):
|
def transfer_user(from_session, to_session):
|
||||||
|
@ -88,3 +88,19 @@ def channel_session_user_from_http(func):
|
||||||
transfer_user(message.http_session, message.channel_session)
|
transfer_user(message.http_session, message.channel_session)
|
||||||
return func(message, *args, **kwargs)
|
return func(message, *args, **kwargs)
|
||||||
return inner
|
return inner
|
||||||
|
|
||||||
|
|
||||||
|
def channel_and_http_session_user_from_http(func):
|
||||||
|
"""
|
||||||
|
Decorator that automatically transfers the user from HTTP sessions to
|
||||||
|
channel-based sessions, rehydrates the HTTP session, and returns the
|
||||||
|
user as message.user as well.
|
||||||
|
"""
|
||||||
|
@http_session_user
|
||||||
|
@channel_and_http_session
|
||||||
|
@functools.wraps(func)
|
||||||
|
def inner(message, *args, **kwargs):
|
||||||
|
if message.http_session is not None:
|
||||||
|
transfer_user(message.http_session, message.channel_session)
|
||||||
|
return func(message, *args, **kwargs)
|
||||||
|
return inner
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django.core.serializers.json import DjangoJSONEncoder, json
|
from django.core.serializers.json import DjangoJSONEncoder, json
|
||||||
|
|
||||||
from ..auth import channel_session_user_from_http
|
from ..auth import channel_and_http_session_user_from_http, channel_session_user_from_http
|
||||||
from ..channel import Group
|
from ..channel import Group
|
||||||
from ..exceptions import SendNotAvailableOnDemultiplexer
|
from ..exceptions import SendNotAvailableOnDemultiplexer
|
||||||
from ..sessions import enforce_ordering
|
from ..sessions import enforce_ordering
|
||||||
|
@ -23,6 +23,7 @@ class WebsocketConsumer(BaseConsumer):
|
||||||
# Turning this on passes the user over from the HTTP session on connect,
|
# Turning this on passes the user over from the HTTP session on connect,
|
||||||
# implies channel_session_user
|
# implies channel_session_user
|
||||||
http_user = False
|
http_user = False
|
||||||
|
http_user_and_session = False
|
||||||
|
|
||||||
# Set to True if you want the class to enforce ordering for you
|
# Set to True if you want the class to enforce ordering for you
|
||||||
strict_ordering = False
|
strict_ordering = False
|
||||||
|
@ -35,13 +36,15 @@ class WebsocketConsumer(BaseConsumer):
|
||||||
adds the ordering decorator.
|
adds the ordering decorator.
|
||||||
"""
|
"""
|
||||||
# HTTP user implies channel session user
|
# HTTP user implies channel session user
|
||||||
if self.http_user:
|
if self.http_user or self.http_user_and_session:
|
||||||
self.channel_session_user = True
|
self.channel_session_user = True
|
||||||
# Get super-handler
|
# Get super-handler
|
||||||
self.path = message['path']
|
self.path = message['path']
|
||||||
handler = super(WebsocketConsumer, self).get_handler(message, **kwargs)
|
handler = super(WebsocketConsumer, self).get_handler(message, **kwargs)
|
||||||
# Optionally apply HTTP transfer
|
# Optionally apply HTTP transfer
|
||||||
if self.http_user:
|
if self.http_user_and_session:
|
||||||
|
handler = channel_and_http_session_user_from_http(handler)
|
||||||
|
elif self.http_user:
|
||||||
handler = channel_session_user_from_http(handler)
|
handler = channel_session_user_from_http(handler)
|
||||||
# Ordering decorators
|
# Ordering decorators
|
||||||
if self.strict_ordering:
|
if self.strict_ordering:
|
||||||
|
|
|
@ -24,6 +24,7 @@ class HttpClient(Client):
|
||||||
self._session = None
|
self._session = None
|
||||||
self._headers = {}
|
self._headers = {}
|
||||||
self._cookies = {}
|
self._cookies = {}
|
||||||
|
self._session_cookie = True
|
||||||
|
|
||||||
def set_cookie(self, key, value):
|
def set_cookie(self, key, value):
|
||||||
"""
|
"""
|
||||||
|
@ -42,7 +43,7 @@ class HttpClient(Client):
|
||||||
def get_cookies(self):
|
def get_cookies(self):
|
||||||
"""Return cookies"""
|
"""Return cookies"""
|
||||||
cookies = copy.copy(self._cookies)
|
cookies = copy.copy(self._cookies)
|
||||||
if apps.is_installed('django.contrib.sessions'):
|
if self._session_cookie and apps.is_installed('django.contrib.sessions'):
|
||||||
cookies[settings.SESSION_COOKIE_NAME] = self.session.session_key
|
cookies[settings.SESSION_COOKIE_NAME] = self.session.session_key
|
||||||
return cookies
|
return cookies
|
||||||
|
|
||||||
|
@ -86,6 +87,7 @@ class HttpClient(Client):
|
||||||
else:
|
else:
|
||||||
content['text'] = text
|
content['text'] = text
|
||||||
self.channel_layer.send(to, content)
|
self.channel_layer.send(to, content)
|
||||||
|
self._session_cookie = False
|
||||||
|
|
||||||
def send_and_consume(self, channel, content={}, text=None, path='/', fail_on_none=True, check_accept=True):
|
def send_and_consume(self, channel, content={}, text=None, path='/', fail_on_none=True, check_accept=True):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import unicode_literals
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from django.test import override_settings
|
from django.test import override_settings
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
from channels import route_class
|
from channels import route_class
|
||||||
from channels.exceptions import SendNotAvailableOnDemultiplexer
|
from channels.exceptions import SendNotAvailableOnDemultiplexer
|
||||||
|
@ -87,6 +88,29 @@ class GenericTests(ChannelTestCase):
|
||||||
self.assertEqual(client.consume('websocket.connect').order, 0)
|
self.assertEqual(client.consume('websocket.connect').order, 0)
|
||||||
self.assertEqual(client.consume('websocket.connect').order, 1)
|
self.assertEqual(client.consume('websocket.connect').order, 1)
|
||||||
|
|
||||||
|
def test_websockets_http_session_and_channel_session(self):
|
||||||
|
|
||||||
|
class WebsocketConsumer(websockets.WebsocketConsumer):
|
||||||
|
http_user_and_session = True
|
||||||
|
|
||||||
|
user_model = get_user_model()
|
||||||
|
user = user_model.objects.create_user(username='test', email='test@test.com', password='123456')
|
||||||
|
|
||||||
|
client = HttpClient()
|
||||||
|
client.force_login(user)
|
||||||
|
with apply_routes([route_class(WebsocketConsumer, path='/path')]):
|
||||||
|
connect = client.send_and_consume('websocket.connect', {'path': '/path'})
|
||||||
|
receive = client.send_and_consume('websocket.receive', {'path': '/path'}, text={'key': 'value'})
|
||||||
|
disconnect = client.send_and_consume('websocket.disconnect', {'path': '/path'})
|
||||||
|
self.assertEqual(
|
||||||
|
connect.message.http_session.session_key,
|
||||||
|
receive.message.http_session.session_key
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
connect.message.http_session.session_key,
|
||||||
|
disconnect.message.http_session.session_key
|
||||||
|
)
|
||||||
|
|
||||||
def test_simple_as_route_method(self):
|
def test_simple_as_route_method(self):
|
||||||
|
|
||||||
class WebsocketConsumer(websockets.WebsocketConsumer):
|
class WebsocketConsumer(websockets.WebsocketConsumer):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user