Skip to content

Commit

Permalink
Rename channels and change message format docs
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewgodwin committed Sep 10, 2015
1 parent fc52e3c commit 70caf7d
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 90 deletions.
14 changes: 7 additions & 7 deletions channels/interfaces/websocket_twisted.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
class InterfaceProtocol(WebSocketServerProtocol):
"""
Protocol which supports WebSockets and forwards incoming messages to
the django.websocket channels.
the websocket channels.
"""

def onConnect(self, request):
Expand All @@ -23,22 +23,22 @@ def onConnect(self, request):

def onOpen(self):
# Make sending channel
self.reply_channel = Channel.new_name("!django.websocket.send")
self.reply_channel = Channel.new_name("!websocket.send")
self.request_info["reply_channel"] = self.reply_channel
self.last_keepalive = time.time()
self.factory.protocols[self.reply_channel] = self
# Send news that this channel is open
Channel("django.websocket.connect").send(self.request_info)
Channel("websocket.connect").send(self.request_info)

def onMessage(self, payload, isBinary):
if isBinary:
Channel("django.websocket.receive").send(dict(
Channel("websocket.receive").send(dict(
self.request_info,
content = payload,
binary = True,
))
else:
Channel("django.websocket.receive").send(dict(
Channel("websocket.receive").send(dict(
self.request_info,
content = payload.decode("utf8"),
binary = False,
Expand All @@ -62,13 +62,13 @@ def serverClose(self):
def onClose(self, wasClean, code, reason):
if hasattr(self, "reply_channel"):
del self.factory.protocols[self.reply_channel]
Channel("django.websocket.disconnect").send(self.request_info)
Channel("websocket.disconnect").send(self.request_info)

def sendKeepalive(self):
"""
Sends a keepalive packet on the keepalive channel.
"""
Channel("django.websocket.keepalive").send(self.request_info)
Channel("websocket.keepalive").send(self.request_info)
self.last_keepalive = time.time()


Expand Down
4 changes: 2 additions & 2 deletions channels/interfaces/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def __init__(self, channel_backend, *args, **kwargs):
super(WSGIInterface, self).__init__(*args, **kwargs)

def get_response(self, request):
request.reply_channel = Channel.new_name("django.wsgi.response")
Channel("django.wsgi.request", channel_backend=self.channel_backend).send(request.channel_encode())
request.reply_channel = Channel.new_name("http.response")
Channel("http.request", channel_backend=self.channel_backend).send(request.channel_encode())
channel, message = self.channel_backend.receive_many_blocking([request.reply_channel])
return HttpResponse.channel_decode(message)
7 changes: 3 additions & 4 deletions channels/response.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from django.http import HttpResponse
from django.http.cookie import SimpleCookie
from six import PY3


Expand All @@ -12,7 +11,7 @@ def encode_response(response):
"content": response.content,
"status_code": response.status_code,
"headers": list(response._headers.values()),
"cookies": {k: v.output(header="") for k, v in response.cookies.items()}
"cookies": [v.output(header="") for _, v in response.cookies.items()]
}
if PY3:
value["content"] = value["content"].decode('utf8')
Expand All @@ -29,9 +28,9 @@ def decode_response(value):
content_type = value['content_type'],
status = value['status_code'],
)
for cookie in value['cookies'].values():
for cookie in value['cookies']:
response.cookies.load(cookie)
response._headers = {k.lower: (k, v) for k, v in value['headers']}
response._headers = {k.lower(): (k, v) for k, v in value['headers']}
return response


Expand Down
16 changes: 8 additions & 8 deletions docs/concepts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ and producers running in different processes or on different machines.

Inside a network, we identify channels uniquely by a name string - you can
send to any named channel from any machine connected to the same channel
backend. If two different machines both write to the ``django.wsgi.request``
backend. If two different machines both write to the ``http.request``
channel, they're writing into the same channel.

How do we use channels?
Expand Down Expand Up @@ -102,12 +102,12 @@ slightly more complex abstraction than that presented by Django views.
A view takes a request and returns a response; a consumer takes a channel
message and can write out zero to many other channel messages.

Now, let's make a channel for requests (called ``django.wsgi.request``),
and a channel per client for responses (e.g. ``django.wsgi.response.o4F2h2Fd``),
Now, let's make a channel for requests (called ``http.request``),
and a channel per client for responses (e.g. ``http.response.o4F2h2Fd``),
with the response channel a property (``reply_channel``) of the request message.
Suddenly, a view is merely another example of a consumer::

# Listens on django.wsgi.request.
# Listens on http.request
def my_consumer(message):
# Decode the request from JSON-compat to a full object
django_request = Request.decode(message.content)
Expand Down Expand Up @@ -154,7 +154,7 @@ to the channel server they're listening on.

For this reason, Channels treats these as two different *channel types*, and
denotes a *response channel* by having the first character of the channel name
be the character ``!`` - e.g. ``!django.wsgi.response.f5G3fE21f``. *Normal
be the character ``!`` - e.g. ``!http.response.f5G3fE21f``. *Normal
channels* have no special prefix, but along with the rest of the response
channel name, they must contain only the characters ``a-z A-Z 0-9 - _``,
and be less than 200 characters long.
Expand Down Expand Up @@ -186,14 +186,14 @@ set of channels (here, using Redis) to send updates to::
content=instance.content,
)

# Connected to django.websocket.connect
# Connected to websocket.connect
def ws_connect(message):
# Add to reader set
redis_conn.sadd("readers", message.reply_channel.name)

While this will work, there's a small problem - we never remove people from
the ``readers`` set when they disconnect. We could add a consumer that
listens to ``django.websocket.disconnect`` to do that, but we'd also need to
listens to ``websocket.disconnect`` to do that, but we'd also need to
have some kind of expiry in case an interface server is forced to quit or
loses power before it can send disconnect signals - your code will never
see any disconnect notification but the response channel is completely
Expand Down Expand Up @@ -222,7 +222,7 @@ we don't need to; Channels has it built in, as a feature called Groups::
content=instance.content,
)

# Connected to django.websocket.connect and django.websocket.keepalive
# Connected to websocket.connect and websocket.keepalive
def ws_connect(message):
# Add to reader group
Group("liveblog").add(message.reply_channel)
Expand Down
50 changes: 25 additions & 25 deletions docs/getting-started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ First Consumers
---------------

Now, by default, Django will run things through Channels but it will also
tie in the URL router and view subsystem to the default ``django.wsgi.request``
tie in the URL router and view subsystem to the default ``http.request``
channel if you don't provide another consumer that listens to it - remember,
only one consumer can listen to any given channel.

Expand Down Expand Up @@ -48,7 +48,7 @@ custom consumer we wrote above. Here's what that looks like::
"default": {
"BACKEND": "channels.backends.database.DatabaseChannelBackend",
"ROUTING": {
"django.wsgi.request": "myproject.myapp.consumers.http_consumer",
"http.request": "myproject.myapp.consumers.http_consumer",
},
},
}
Expand All @@ -74,19 +74,19 @@ serve HTTP requests from now on - and make this WebSocket consumer instead::
def ws_add(message):
Group("chat").add(message.reply_channel)

Hook it up to the ``django.websocket.connect`` channel like this::
Hook it up to the ``websocket.connect`` channel like this::

CHANNEL_BACKENDS = {
"default": {
"BACKEND": "channels.backends.database.DatabaseChannelBackend",
"ROUTING": {
"django.websocket.connect": "myproject.myapp.consumers.ws_add",
"websocket.connect": "myproject.myapp.consumers.ws_add",
},
},
}

Now, let's look at what this is doing. It's tied to the
``django.websocket.connect`` channel, which means that it'll get a message
``websocket.connect`` channel, which means that it'll get a message
whenever a new WebSocket connection is opened by a client.

When it gets that message, it takes the ``reply_channel`` attribute from it, which
Expand All @@ -100,12 +100,12 @@ don't keep track of the open/close states of the potentially thousands of
connections you have open at any one time.

The solution to this is that the WebSocket interface servers will send
periodic "keepalive" messages on the ``django.websocket.keepalive`` channel,
periodic "keepalive" messages on the ``websocket.keepalive`` channel,
so we can hook that up to re-add the channel (it's safe to add the channel to
a group it's already in - similarly, it's safe to discard a channel from a
group it's not in)::

# Connected to django.websocket.keepalive
# Connected to websocket.keepalive
def ws_keepalive(message):
Group("chat").add(message.reply_channel)

Expand All @@ -114,16 +114,16 @@ just route both channels to the same consumer::

...
"ROUTING": {
"django.websocket.connect": "myproject.myapp.consumers.ws_add",
"django.websocket.keepalive": "myproject.myapp.consumers.ws_add",
"websocket.connect": "myproject.myapp.consumers.ws_add",
"websocket.keepalive": "myproject.myapp.consumers.ws_add",
},
...

And, even though channels will expire out, let's add an explicit ``disconnect``
handler to clean up as people disconnect (most channels will cleanly disconnect
and get this called)::

# Connected to django.websocket.disconnect
# Connected to websocket.disconnect
def ws_disconnect(message):
Group("chat").discard(message.reply_channel)

Expand All @@ -134,15 +134,15 @@ any message sent in to all connected clients. Here's all the code::

from channels import Channel, Group

# Connected to django.websocket.connect and django.websocket.keepalive
# Connected to websocket.connect and websocket.keepalive
def ws_add(message):
Group("chat").add(message.reply_channel)

# Connected to django.websocket.receive
# Connected to websocket.receive
def ws_message(message):
Group("chat").send(message.content)

# Connected to django.websocket.disconnect
# Connected to websocket.disconnect
def ws_disconnect(message):
Group("chat").discard(message.reply_channel)

Expand All @@ -152,10 +152,10 @@ And what our routing should look like in ``settings.py``::
"default": {
"BACKEND": "channels.backends.database.DatabaseChannelBackend",
"ROUTING": {
"django.websocket.connect": "myproject.myapp.consumers.ws_add",
"django.websocket.keepalive": "myproject.myapp.consumers.ws_add",
"django.websocket.receive": "myproject.myapp.consumers.ws_message",
"django.websocket.disconnect": "myproject.myapp.consumers.ws_disconnect",
"websocket.connect": "myproject.myapp.consumers.ws_add",
"websocket.keepalive": "myproject.myapp.consumers.ws_add",
"websocket.receive": "myproject.myapp.consumers.ws_message",
"websocket.disconnect": "myproject.myapp.consumers.ws_disconnect",
},
},
}
Expand Down Expand Up @@ -332,7 +332,7 @@ name in the path of your WebSocket request (we'll ignore auth for now)::
from channels import Channel
from channels.decorators import channel_session

# Connected to django.websocket.connect
# Connected to websocket.connect
@channel_session
def ws_connect(message):
# Work out room name from path (ignore slashes)
Expand All @@ -341,17 +341,17 @@ name in the path of your WebSocket request (we'll ignore auth for now)::
message.channel_session['room'] = room
Group("chat-%s" % room).add(message.reply_channel)

# Connected to django.websocket.keepalive
# Connected to websocket.keepalive
@channel_session
def ws_add(message):
Group("chat-%s" % message.channel_session['room']).add(message.reply_channel)

# Connected to django.websocket.receive
# Connected to websocket.receive
@channel_session
def ws_message(message):
Group("chat-%s" % message.channel_session['room']).send(content)

# Connected to django.websocket.disconnect
# Connected to websocket.disconnect
@channel_session
def ws_disconnect(message):
Group("chat-%s" % message.channel_session['room']).discard(message.reply_channel)
Expand Down Expand Up @@ -400,7 +400,7 @@ have a ChatMessage model with ``message`` and ``room`` fields::
"content": message.content['message'],
})

# Connected to django.websocket.connect
# Connected to websocket.connect
@channel_session
def ws_connect(message):
# Work out room name from path (ignore slashes)
Expand All @@ -409,12 +409,12 @@ have a ChatMessage model with ``message`` and ``room`` fields::
message.channel_session['room'] = room
Group("chat-%s" % room).add(message.reply_channel)

# Connected to django.websocket.keepalive
# Connected to websocket.keepalive
@channel_session
def ws_add(message):
Group("chat-%s" % message.channel_session['room']).add(message.reply_channel)

# Connected to django.websocket.receive
# Connected to websocket.receive
@channel_session
def ws_message(message):
# Stick the message onto the processing queue
Expand All @@ -423,7 +423,7 @@ have a ChatMessage model with ``message`` and ``room`` fields::
"message": content,
})

# Connected to django.websocket.disconnect
# Connected to websocket.disconnect
@channel_session
def ws_disconnect(message):
Group("chat-%s" % message.channel_session['room']).discard(message.reply_channel)
Expand Down

0 comments on commit 70caf7d

Please sign in to comment.