Implement send_channel_session

This commit is contained in:
Andrew Godwin 2015-07-13 17:58:52 -07:00
parent 6e91ea0040
commit 804a4c561e
2 changed files with 45 additions and 11 deletions

View File

@ -1,5 +1,8 @@
import functools
import hashlib
from importlib import import_module
from django.conf import settings
from django.utils import six
from channels import channel_backends, DEFAULT_CHANNEL_BACKEND
@ -24,3 +27,34 @@ def consumer(*channels, **kwargs):
# TODO: Sessions, auth
def send_channel_session(func):
"""
Provides a session-like object called "channel_session" to consumers
as a message attribute that will auto-persist across consumers with
the same incoming "send_channel" value.
"""
@functools.wraps(func)
def inner(*args, **kwargs):
# Make sure there's a send_channel in kwargs
if "send_channel" not in kwargs:
raise ValueError("No send_channel sent to consumer; this decorator can only be used on messages containing it.")
# Turn the send_channel into a valid session key length thing.
# We take the last 24 bytes verbatim, as these are the random section,
# and then hash the remaining ones onto the start, and add a prefix
# TODO: See if there's a better way of doing this
session_key = "skt" + hashlib.md5(kwargs['send_channel'][:-24]).hexdigest()[:8] + kwargs['send_channel'][-24:]
# Make a session storage
session_engine = import_module(settings.SESSION_ENGINE)
session = session_engine.SessionStore(session_key=session_key)
# If the session does not already exist, save to force our session key to be valid
if not session.exists(session.session_key):
session.save()
kwargs['channel_session'] = session
# Run the consumer
result = func(*args, **kwargs)
# Persist session if needed (won't be saved if error happens)
if session.modified:
session.save()
return result
return inner

View File

@ -275,7 +275,7 @@ However, that session is based on cookies, and so follows the user round the
site - it's great for information that should persist across all WebSocket and
HTTP connections, but not great for information that is specific to a single
WebSocket (such as "which chatroom should this socket be connected to"). For
this reason, Channels also provides a ``websocker_channel_session`` decorator,
this reason, Channels also provides a ``send_channel_session`` decorator,
which adds a ``channel_session`` attribute to the message; this works just like
the normal ``session`` attribute, and persists to the same storage, but varies
per-channel rather than per-cookie.
@ -284,10 +284,10 @@ Let's use it now to build a chat server that expects you to pass a chatroom
name in the path of your WebSocket request (we'll ignore auth for now)::
from channels import Channel
from channels.decorators import consumer, websocket_channel_session
from channels.decorators import consumer, send_channel_session
@consumer("django.websocket.connect")
@websocket_channel_session
@send_channel_session
def ws_connect(channel, send_channel, path, channel_session, **kwargs):
# Work out room name from path (ignore slashes)
room = path.strip("/")
@ -296,17 +296,17 @@ name in the path of your WebSocket request (we'll ignore auth for now)::
Group("chat-%s" % room).add(send_channel)
@consumer("django.websocket.keepalive")
@websocket_channel_session
@send_channel_session
def ws_add(channel, send_channel, channel_session, **kwargs):
Group("chat-%s" % channel_session['room']).add(send_channel)
@consumer("django.websocket.receive")
@websocket_channel_session
@send_channel_session
def ws_message(channel, send_channel, content, channel_session, **kwargs):
Group("chat-%s" % channel_session['room']).send(content=content)
@consumer("django.websocket.disconnect")
@websocket_channel_session
@send_channel_session
def ws_disconnect(channel, send_channel, channel_session, **kwargs):
Group("chat-%s" % channel_session['room']).discard(send_channel)
@ -340,7 +340,7 @@ Let's see what that looks like, assuming we
have a ChatMessage model with ``message`` and ``room`` fields::
from channels import Channel
from channels.decorators import consumer, websocket_channel_session
from channels.decorators import consumer, send_channel_session
from .models import ChatMessage
@consumer("chat-messages")
@ -351,7 +351,7 @@ have a ChatMessage model with ``message`` and ``room`` fields::
Group("chat-%s" % room).send(message)
@consumer("django.websocket.connect")
@websocket_channel_session
@send_channel_session
def ws_connect(channel, send_channel, path, channel_session, **kwargs):
# Work out room name from path (ignore slashes)
room = path.strip("/")
@ -360,18 +360,18 @@ have a ChatMessage model with ``message`` and ``room`` fields::
Group("chat-%s" % room).add(send_channel)
@consumer("django.websocket.keepalive")
@websocket_channel_session
@send_channel_session
def ws_add(channel, send_channel, channel_session, **kwargs):
Group("chat-%s" % channel_session['room']).add(send_channel)
@consumer("django.websocket.receive")
@websocket_channel_session
@send_channel_session
def ws_message(channel, send_channel, content, channel_session, **kwargs):
# Stick the message onto the processing queue
Channel("chat-messages").send(room=channel_session['room'], message=content)
@consumer("django.websocket.disconnect")
@websocket_channel_session
@send_channel_session
def ws_disconnect(channel, send_channel, channel_session, **kwargs):
Group("chat-%s" % channel_session['room']).discard(send_channel)