mirror of
https://github.com/django/daphne.git
synced 2025-11-02 00:27:35 +03:00
225 lines
8.6 KiB
ReStructuredText
225 lines
8.6 KiB
ReStructuredText
1.0.0 Release Notes
|
|
===================
|
|
|
|
Channels 1.0.0 brings together a number of design changes, including some
|
|
breaking changes, into our first fully stable release, and also brings the
|
|
databinding code out of alpha phase. It was released on 2017/01/08.
|
|
|
|
The result is a faster, easier to use, and safer Channels, including one major
|
|
change that will fix almost all problems with sessions and connect/receive
|
|
ordering in a way that needs no persistent storage.
|
|
|
|
It was unfortunately not possible to make all of the changes backwards
|
|
compatible, though most code should not be too affected and the fixes are
|
|
generally quite easy.
|
|
|
|
You **must also update Daphne** to at least 1.0.0 to have this release of
|
|
Channels work correctly.
|
|
|
|
|
|
Major Features
|
|
--------------
|
|
|
|
Channels 1.0 introduces a couple of new major features.
|
|
|
|
|
|
WebSocket accept/reject flow
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Rather than be immediately accepted, WebSockets now pause during the handshake
|
|
while they send over a message on ``websocket.connect``, and your application
|
|
must either accept or reject the connection before the handshake is completed
|
|
and messages can be received.
|
|
|
|
You **must** update Daphne to at least 1.0.0 to make this work correctly.
|
|
|
|
This has several advantages:
|
|
|
|
* You can now reject WebSockets before they even finish connecting, giving
|
|
appropriate error codes to browsers and not letting the browser-side socket
|
|
ever get into a connected state and send messages.
|
|
|
|
* Combined with Consumer Atomicity (below), it means there is no longer any need
|
|
for the old "slight ordering" mode, as the connect consumer must run to
|
|
completion and accept the socket before any messages can be received and
|
|
forwarded onto ``websocket.receive``.
|
|
|
|
* Any ``send`` message sent to the WebSocket will implicitly accept the connection,
|
|
meaning only a limited set of ``connect`` consumers need changes (see
|
|
Backwards Incompatible Changes below)
|
|
|
|
|
|
Consumer Atomicity
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
Consumers will now buffer messages you try to send until the consumer completes
|
|
and then send them once it exits and the outbound part of any decorators have
|
|
been run (even if an exception is raised).
|
|
|
|
This makes the flow of messages much easier to reason about - consumers can now
|
|
be reasoned about as atomic blocks that run and then send messages, meaning that
|
|
if you send a message to start another consumer you're guaranteed that the
|
|
sending consumer has finished running by the time it's acted upon.
|
|
|
|
If you want to send messages immediately rather than at the end of the consumer,
|
|
you can still do that by passing the ``immediately`` argument::
|
|
|
|
Channel("thumbnailing-tasks").send({"id": 34245}, immediately=True)
|
|
|
|
This should be mostly backwards compatible, and may actually fix race
|
|
conditions in some apps that were pre-existing.
|
|
|
|
|
|
Databinding Group/Action Overhaul
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Previously, databinding subclasses had to implement
|
|
``group_names(instance, action)`` to return what groups to send an instance's
|
|
change to of the type ``action``. This had flaws, most notably when what was
|
|
actually just a modification to the instance in question changed its
|
|
permission status so more clients could see it; to those clients, it should
|
|
instead have been "created".
|
|
|
|
Now, Channels just calls ``group_names(instance)``, and you should return what
|
|
groups can see the instance at the current point in time given the instance
|
|
you were passed. Channels will actually call the method before and after changes,
|
|
comparing the groups you gave, and sending out create, update or delete messages
|
|
to clients appropriately.
|
|
|
|
Existing databinding code will need to be adapted; see the
|
|
"Backwards Incompatible Changes" section for more.
|
|
|
|
|
|
Demultiplexer Overhaul
|
|
~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Demuliplexers have changed to remove the behaviour where they re-sent messages
|
|
onto new channels without special headers, and instead now correctly split out
|
|
incoming messages into sub-messages that still look like ``websocket.receive``
|
|
messages, and directly dispatch these to the relevant consumer.
|
|
|
|
They also now forward all ``websocket.connect`` and ``websocket.disconnect``
|
|
messages to all of their sub-consumers, so it's much easier to compose things
|
|
together from code that also works outside the context of multiplexing.
|
|
|
|
For more, read the updated :doc:`/generic` docs.
|
|
|
|
|
|
Delay Server
|
|
~~~~~~~~~~~~
|
|
|
|
A built-in delay server, launched with `manage.py rundelay`, now ships if you
|
|
wish to use it. It needs some extra initial setup and uses a database for
|
|
persistance; see :doc:`/delay` for more information.
|
|
|
|
|
|
Minor Changes
|
|
-------------
|
|
|
|
* Serializers can now specify fields as ``__all__`` to auto-include all fields,
|
|
and ``exclude`` to remove certain unwanted fields.
|
|
|
|
* ``runserver`` respects ``FORCE_SCRIPT_NAME``
|
|
|
|
* Websockets can now be closed with a specific code by calling ``close(status=4000)``
|
|
|
|
* ``enforce_ordering`` no longer has a ``slight`` mode (because of the accept
|
|
flow changes), and is more efficient with session saving.
|
|
|
|
* ``runserver`` respects ``--nothreading`` and only launches one worker, takes
|
|
a ``--http-timeout`` option if you want to override it from the default ``60``,
|
|
|
|
* A new ``@channel_and_http_session`` decorator rehydrates the HTTP session out
|
|
of the channel session if you want to access it inside receive consumers.
|
|
|
|
* Streaming responses no longer have a chance of being cached.
|
|
|
|
* ``request.META['SERVER_PORT']`` is now always a string.
|
|
|
|
* ``http.disconnect`` now has a ``path`` key so you can route it.
|
|
|
|
* Test client now has a ``send_and_consume`` method.
|
|
|
|
|
|
Backwards Incompatible Changes
|
|
------------------------------
|
|
|
|
Connect Consumers
|
|
~~~~~~~~~~~~~~~~~
|
|
|
|
If you have a custom consumer for ``websocket.connect``, you must ensure that
|
|
it either:
|
|
|
|
* Sends at least one message onto the ``reply_channel`` that generates a
|
|
WebSocket frame (either ``bytes`` or ``text`` is set), either directly
|
|
or via a group.
|
|
* Sends a message onto the ``reply_channel`` that is ``{"accept": True}``,
|
|
to accept a connection without sending data.
|
|
* Sends a message onto the ``reply_channel`` that is ``{"close": True}``,
|
|
to reject a connection mid-handshake.
|
|
|
|
Many consumers already do the former, but if your connect consumer does not
|
|
send anything you MUST now send an accept message or the socket will remain
|
|
in the handshaking phase forever and you'll never get any messages.
|
|
|
|
All built-in Channels consumers (e.g. in the generic consumers) have been
|
|
upgraded to do this.
|
|
|
|
You **must** update Daphne to at least 1.0.0 to make this work correctly.
|
|
|
|
|
|
Databinding group_names
|
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
If you have databinding subclasses, you will have implemented
|
|
``group_names(instance, action)``, which returns the groups to use based on the
|
|
instance and action provided.
|
|
|
|
Now, instead, you must implement ``group_names(instance)``, which returns the
|
|
groups that can see the instance as it is presented for you; the action
|
|
results will be worked out for you. For example, if you want to only show
|
|
objects marked as "admin_only" to admins, and objects without it to everyone,
|
|
previously you would have done::
|
|
|
|
def group_names(self, instance, action):
|
|
if instance.admin_only:
|
|
return ["admins"]
|
|
else:
|
|
return ["admins", "non-admins"]
|
|
|
|
Because you did nothing based on the ``action`` (and if you did, you would
|
|
have got incomplete messages, hence this design change), you can just change
|
|
the signature of the method like this::
|
|
|
|
def group_names(self, instance):
|
|
if instance.admin_only:
|
|
return ["admins"]
|
|
else:
|
|
return ["admins", "non-admins"]
|
|
|
|
Now, when an object is updated to have ``admin_only = True``, the clients
|
|
in the ``non-admins`` group will get a ``delete`` message, while those in
|
|
the ``admins`` group will get an ``update`` message.
|
|
|
|
|
|
Demultiplexers
|
|
~~~~~~~~~~~~~~
|
|
|
|
Demultiplexers have changed from using a ``mapping`` dict, which mapped stream
|
|
names to channels, to using a ``consumers`` dict which maps stream names
|
|
directly to consumer classes.
|
|
|
|
You will have to convert over to using direct references to consumers, change
|
|
the name of the dict, and then you can remove any channel routing for the old
|
|
channels that were in ``mapping`` from your routes.
|
|
|
|
Additionally, the Demultiplexer now forwards messages as they would look from
|
|
a direct connection, meaning that where you previously got a decoded object
|
|
through you will now get a correctly-formatted ``websocket.receive`` message
|
|
through with the content as a ``text`` key, JSON-encoded. You will also
|
|
now have to handle ``websocket.connect`` and ``websocket.disconnect`` messages.
|
|
|
|
Both of these issues can be solved using the ``JsonWebsocketConsumer`` generic
|
|
consumer, which will decode for you and correctly separate connection and
|
|
disconnection handling into their own methods.
|