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: .. code-block:: python 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 ``/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 persistence; see ``/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: .. code-block:: python 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: .. code-block:: python 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.