### Includes/Asgi Example .. code-block:: python import os from channels.auth import AuthMiddlewareStack from channels.routing import ProtocolTypeRouter, URLRouter from channels.security.websocket import AllowedHostsOriginValidator from django.core.asgi import get_asgi_application from django.urls import path os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") # Initialize Django ASGI application early to ensure the AppRegistry # is populated before importing code that may import ORM models. django_asgi_app = get_asgi_application() from chat.consumers import AdminChatConsumer, PublicChatConsumer application = ProtocolTypeRouter({ # Django's ASGI application to handle traditional HTTP requests "http": django_asgi_app, # WebSocket chat handler "websocket": AllowedHostsOriginValidator( AuthMiddlewareStack( URLRouter([ path("chat/admin/", AdminChatConsumer.as_asgi()), path("chat/", PublicChatConsumer.as_asgi()), ]) ) ), }) --- ### Releases/1.0.0 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. --- ### Releases/1.0.1 1.0.1 Release Notes =================== Channels 1.0.1 is a minor bugfix release, released on 2017/01/09. Changes ------- * WebSocket generic views now accept connections by default in their connect handler for better backwards compatibility. Backwards Incompatible Changes ------------------------------ None. --- ### Releases/1.0.2 1.0.2 Release Notes =================== Channels 1.0.2 is a minor bugfix release, released on 2017/01/12. Changes ------- * Websockets can now be closed from anywhere using the new ``WebsocketCloseException``, available as ``channels.exceptions.WebsocketCloseException(code=None)``. There is also a generic ``ChannelSocketException`` you can base any exceptions on that, if it is caught, gets handed the current ``message`` in a ``run`` method, so you can do custom behaviours. * Calling ``Channel.send`` or ``Group.send`` from outside a consumer context (i.e. in tests or management commands) will once again send the message immediately, rather than putting it into the consumer message buffer to be flushed when the consumer ends (which never happens) * The base implementation of databinding now correctly only calls ``group_names(instance)``, as documented. Backwards Incompatible Changes ------------------------------ None. --- ### Releases/1.0.3 1.0.3 Release Notes =================== Channels 1.0.3 is a minor bugfix release, released on 2017/02/01. Changes ------- * Database connections are no longer force-closed after each test is run. * Channel sessions are not re-saved if they're empty even if they're marked as modified, allowing logout to work correctly. * WebsocketDemultiplexer now correctly does sessions for the second/third/etc. connect and disconnect handlers. * Request reading timeouts now correctly return 408 rather than erroring out. * The ``rundelay`` delay server now only polls the database once per second, and this interval is configurable with the ``--sleep`` option. Backwards Incompatible Changes ------------------------------ None. --- ### Releases/1.1.0 1.1.0 Release Notes =================== Channels 1.1.0 introduces a couple of major but backwards-compatible changes, including most notably the inclusion of a standard, framework-agnostic JavaScript library for easier integration with your site. Major Changes ------------- * Channels now includes a JavaScript wrapper that wraps reconnection and multiplexing for you on the client side. For more on how to use it, see the javascript documentation. * Test classes have been moved from ``channels.tests`` to ``channels.test`` to better match Django. Old imports from ``channels.tests`` will continue to work but will trigger a deprecation warning, and ``channels.tests`` will be removed completely in version 1.3. Minor Changes & Bugfixes ------------------------ * Bindings now support non-integer fields for primary keys on models. * The ``enforce_ordering`` decorator no longer suffers a race condition where it would drop messages under high load. * ``runserver`` no longer errors if the ``staticfiles`` app is not enabled in Django. Backwards Incompatible Changes ------------------------------ None. --- ### Releases/1.1.1 1.1.1 Release Notes =================== Channels 1.1.1 is a bugfix release that fixes a packaging issue with the JavaScript files. Major Changes ------------- None. Minor Changes & Bugfixes ------------------------ * The JavaScript binding introduced in 1.1.0 is now correctly packaged and included in builds. Backwards Incompatible Changes ------------------------------ None. --- ### Releases/1.1.2 1.1.2 Release Notes =================== Channels 1.1.2 is a bugfix release for the 1.1 series, released on April 1st, 2017. Major Changes ------------- None. Minor Changes & Bugfixes ------------------------ * Session name hash changed to SHA-1 to satisfy FIPS-140-2. * `scheme` key in ASGI-HTTP messages now translates into `request.is_secure()` correctly. * WebsocketBridge now exposes the underlying WebSocket as `.socket`. Backwards Incompatible Changes ------------------------------ * When you upgrade all current channel sessions will be invalidated; you should make sure you disconnect all WebSockets during upgrade. --- ### Releases/1.1.3 1.1.3 Release Notes =================== Channels 1.1.3 is a bugfix release for the 1.1 series, released on April 5th, 2017. Major Changes ------------- None. Minor Changes & Bugfixes ------------------------ * ``enforce_ordering`` now works correctly with the new-style process-specific channels * ASGI channel layer versions are now explicitly checked for version compatibility Backwards Incompatible Changes ------------------------------ None. --- ### Releases/1.1.4 1.1.4 Release Notes =================== Channels 1.1.4 is a bugfix release for the 1.1 series, released on June 15th, 2017. Major Changes ------------- None. Minor Changes & Bugfixes ------------------------ * Pending messages correctly handle retries in backlog situations * Workers in threading mode now respond to ctrl-C and gracefully exit. * ``request.meta['QUERY_STRING']`` is now correctly encoded at all times. * Test client improvements * ``ChannelServerLiveTestCase`` added, allows an equivalent of the Django ``LiveTestCase``. * Decorator added to check ``Origin`` headers (``allowed_hosts_only``) * New ``TEST_CONFIG`` setting in ``CHANNEL_LAYERS`` that allows varying of the channel layer for tests (e.g. using a different Redis install) Backwards Incompatible Changes ------------------------------ None. --- ### Releases/1.1.5 1.1.5 Release Notes =================== Channels 1.1.5 is a packaging release for the 1.1 series, released on June 16th, 2017. Major Changes ------------- None. Minor Changes & Bugfixes ------------------------ * The Daphne dependency requirement was bumped to 1.3.0. Backwards Incompatible Changes ------------------------------ None. --- ### Releases/1.1.6 1.1.6 Release Notes =================== Channels 1.1.5 is a packaging release for the 1.1 series, released on June 28th, 2017. Major Changes ------------- None. Minor Changes & Bugfixes ------------------------ * The ``runserver`` ``server_cls`` override no longer fails with more modern Django versions that pass an ``ipv6`` parameter. Backwards Incompatible Changes ------------------------------ None. --- ### Releases/2.0.0 2.0.0 Release Notes =================== Channels 2.0 is a major rewrite of Channels, introducing a large amount of changes to the fundamental design and architecture of Channels. Notably: * Data is no longer transported over a channel layer between protocol server and application; instead, applications run inside their protocol servers (like with WSGI). * To achieve this, the entire core of channels is now built around Python's ``asyncio`` framework and runs async-native down until it hits either a Django view or a synchronous consumer. * Python 2.7 and 3.4 are no longer supported. More detailed information on the changes and tips on how to port your applications can be found in our ``/one-to-two`` documentation in the 2.x docs version. Backwards Incompatible Changes ------------------------------ Channels 2 is regrettably not backwards-compatible at all with Channels 1 applications due to the large amount of re-architecting done to the code and the switch from synchronous to asynchronous runtimes. A migration guide is available in the 2.x docs version, and a lot of the basic concepts are the same, but the basic class structure and imports have changed. Our apologies for having to make a breaking change like this, but it was the only way to fix some of the fundamental design issues in Channels 1. Channels 1 will continue to receive security and data-loss fixes for the foreseeable future, but no new features will be added. --- ### Releases/2.0.1 2.0.1 Release Notes =================== Channels 2.0.1 is a patch release of channels, adding a couple of small new features and fixing one bug in URL resolution. As always, when updating Channels make sure to also update its dependencies (``asgiref`` and ``daphne``) as these also get their own bugfix updates, and some bugs that may appear to be part of Channels are actually in those packages. New Features ------------ * There are new async versions of the Websocket generic consumers, ``AsyncWebsocketConsumer`` and ``AsyncJsonWebsocketConsumer``. Read more about them in :doc:`/topics/consumers`. * The old ``allowed_hosts_only`` decorator has been removed (it was accidentally included in the 2.0 release but didn't work) and replaced with a new ``OriginValidator`` and ``AllowedHostsOriginValidator`` set of ASGI middleware. Read more in :doc:`/topics/security`. Bugfixes -------- * A bug in ``URLRouter`` which didn't allow you to match beyond the first URL in some situations has been resolved, and a test suite was added for URL resolution to prevent it happening again. Backwards Incompatible Changes ------------------------------ None. --- ### Releases/2.0.2 2.0.2 Release Notes =================== Channels 2.0.2 is a patch release of Channels, fixing a bug in the database connection handling. As always, when updating Channels make sure to also update its dependencies (``asgiref`` and ``daphne``) as these also get their own bugfix updates, and some bugs that may appear to be part of Channels are actually in those packages. New Features ------------ * There is a new ``channels.db.database_sync_to_async`` wrapper that is like ``sync_to_async`` but also closes database connections for you. You can read more about usage in :doc:`/topics/databases`. Bugfixes -------- * SyncConsumer and all its descendant classes now close database connections when they exit. Backwards Incompatible Changes ------------------------------ None. --- ### Releases/2.1.0 2.1.0 Release Notes =================== Channels 2.1 brings a few new major changes to Channels as well as some more minor fixes. In addition, if you've not yet seen it, we now have a long-form :doc:`tutorial ` to better introduce some of the concepts and sync versus async styles of coding. Major Changes ------------- Async HTTP Consumer ~~~~~~~~~~~~~~~~~~~ There is a new native-async HTTP consumer class, ``channels.generic.http.AsyncHttpConsumer``. This allows much easier writing of long-poll endpoints or other long-lived HTTP connection handling that benefits from native async support. You can read more about it in the :doc:`/topics/consumers` documentation. WebSocket Consumers ~~~~~~~~~~~~~~~~~~~ These consumer classes now all have built-in group join and leave functionality, which will make a consumer join all group names that are in the iterable ``groups`` on the consumer class (this can be a static list or a ``@property`` method). In addition, the ``accept`` methods on both variants now take an optional ``subprotocol`` argument, which will be sent back to the WebSocket client as the subprotocol the server has selected. The client's advertised subprotocols can, as always, be found in the scope as ``scope["subprotocols"]``. Nested URL Routing ~~~~~~~~~~~~~~~~~~ ``URLRouter`` instances can now be nested inside each other and, like Django's URL handling and ``include``, will strip off the matched part of the URL in the outer router and leave only the unmatched portion for the inner router, allowing reusable routing files. Note that you **cannot** use the Django ``include`` function inside of the ``URLRouter`` as it assumes a bit too much about what it is given as its left-hand side and will terminate your regular expression/URL pattern wrongly. Login and Logout ~~~~~~~~~~~~~~~~ As well as overhauling the internals of the ``AuthMiddleware``, there are now also ``login`` and ``logout`` async functions you can call in consumers to log users in and out of the current session. Due to the way cookies are sent back to clients, these come with some caveats; read more about them and how to use them properly in :doc:`/topics/authentication`. In-Memory Channel Layer ~~~~~~~~~~~~~~~~~~~~~~~ The in-memory channel layer has been extended to have full expiry and group support so it should now be suitable for drop-in replacement for most test scenarios. Testing ~~~~~~~ The ``ChannelsLiveServerTestCase`` has been rewritten to use a new method for launching Daphne that should be more resilient (and faster), and now shares code with the Daphne test suite itself. Ports are now left up to the operating system to decide rather than being picked from within a set range. It also now supports static files when the Django ``staticfiles`` app is enabled. In addition, the Communicator classes have gained a ``receive_nothing`` method that allows you to assert that the application didn't send anything, rather than writing this yourself using exception handling. See more in the :doc:`/topics/testing` documentation. Origin header validation ~~~~~~~~~~~~~~~~~~~~~~~~ As well as removing the ``print`` statements that accidentally got into the last release, this has been overhauled to more correctly match against headers according to the Origin header spec and align with Django's ``ALLOWED_HOSTS`` setting. It can now also enforce protocol (``http`` versus ``https``) and port, both optionally. Bugfixes & Small Changes ------------------------ * ``print`` statements that accidentally got left in the ``Origin`` validation code were removed. * The ``runserver`` command now shows the version of Channels you are running. * Orphaned tasks that may have caused warnings during test runs or occasionally live site traffic are now correctly killed off rather than letting them die later on and print warning messages. * ``WebsocketCommunicator`` now accepts a query string passed into the constructor and adds it to the scope rather than just ignoring it. * Test handlers will correctly handle changing the ``CHANNEL_LAYERS`` setting via decorators and wipe the internal channel layer cache. * ``SessionMiddleware`` can be safely nested inside itself rather than causing a runtime error. Backwards Incompatible Changes ------------------------------ * The format taken by the ``OriginValidator`` for its domains has changed and ``*.example.com`` is no longer allowed; instead, use ``.example.com`` to match a domain and all its subdomains. * If you previously nested ``URLRouter`` instances inside each other both would have been matching on the full URL before, whereas now they will match on the unmatched portion of the URL, meaning your URL routes would break if you had intended this usage. --- ### Releases/2.1.1 2.1.1 Release Notes =================== Channels 2.1.1 is a bugfix release for an important bug in the new async authentication code. Major Changes ------------- None. Bugfixes & Small Changes ------------------------ Previously, the object in ``scope["user"]`` was one of Django's SimpleLazyObjects, which then called our ``get_user`` async function via ``async_to_sync``. This worked fine when called from SyncConsumers, but because async environments do not run attribute access in an async fashion, when the body of an async consumer tried to call it, the ``asgiref`` library flagged an error where the code was trying to call a synchronous function during a async context. To fix this, the User object is now loaded non-lazily on application startup. This introduces a blocking call during the synchronous application constructor, so the ASGI spec has been updated to recommend that constructors for ASGI apps are called in a threadpool and Daphne 2.1.1 implements this and is recommended for use with this release. Backwards Incompatible Changes ------------------------------ None. --- ### Releases/2.1.2 2.1.2 Release Notes =================== Channels 2.1.2 is another bugfix release in the 2.1 series. Special thanks to people at the DjangoCon Europe sprints who helped out with several of these fixes. Major Changes ------------- Session and authentication middleware has been overhauled to be non-blocking. Previously, these middlewares potentially did database or session store access in the synchronous ASGI constructor, meaning they would block the entire event loop while doing so. Instead, they have now been modified to add LazyObjects into the scope in the places where the session or user will be, and then when the processing goes through their asynchronous portion, those stores are accessed in a non-blocking fashion. This should be an un-noticeable change for end users, but if you see weird behaviour or an unresolved LazyObject, let us know. Bugfixes & Small Changes ------------------------ * AsyncHttpConsumer now has a disconnect() method you can override if you want to perform actions (such as leaving groups) when a long-running HTTP request disconnects. * URL routing context now includes default arguments from the URLconf in the context's ``url_route`` key, alongside captured arguments/groups from the URL pattern. * The FORCE_SCRIPT_NAME setting is now respected in ASGI mode, and lets you override where Django thinks the root URL of your application is mounted. * ALLOWED_HOSTS is now set correctly during LiveServerTests, meaning you will no longer get ``400 Bad Request`` errors during these test runs. Backwards Incompatible Changes ------------------------------ None. --- ### Releases/2.1.3 2.1.3 Release Notes =================== Channels 2.1.3 is another bugfix release in the 2.1 series. Bugfixes & Small Changes ------------------------ * An ALLOWED_ORIGINS value of "*" will now also allow requests without a Host header at all (especially important for tests) * The request.path value is now correct in cases when a server has SCRIPT_NAME set. * Errors that happen inside channel listeners inside a runworker or Worker class are now raised rather than suppressed. Backwards Incompatible Changes ------------------------------ None. --- ### Releases/2.1.4 2.1.4 Release Notes =================== Channels 2.1.4 is another bugfix release in the 2.1 series. Bugfixes & Small Changes ------------------------ * Django middleware is now cached rather than instantiated per request resulting in a significant speed improvement. Some middleware took seconds to load and as a result Channels was unusable for HTTP serving before. * ChannelServerLiveTestCase now serves static files again. * Improved error message resulting from bad Origin headers. * ``runserver`` logging now goes through the Django logging framework to match modern Django. * Generic consumers can now have non-default channel layers - set the ``channel_layer_alias`` property on the consumer class * Improved error when accessing ``scope['user']`` before it's ready - the user is not accessible in the constructor of ASGI apps as it needs an async environment to load in. Previously it raised a generic error when you tried to access it early; now it tells you more clearly what's happening. Backwards Incompatible Changes ------------------------------ None. --- ### Releases/2.1.5 2.1.5 Release Notes =================== Channels 2.1.5 is another bugfix release in the 2.1 series. Bugfixes & Small Changes ------------------------ * Django middleware caching now works on Django 1.11 and Django 2.0. The previous release only ran on 2.1. Backwards Incompatible Changes ------------------------------ None. --- ### Releases/2.1.6 2.1.6 Release Notes =================== Channels 2.1.6 is another bugfix release in the 2.1 series. Bugfixes & Small Changes ------------------------ * HttpCommunicator now extracts query strings correctly from its provided arguments * AsyncHttpConsumer provides channel layer attributes following the same conventions as other consumer classes * Prevent late-Daphne import errors where importing ``daphne.server`` didn't work due to a bad linter fix. Backwards Incompatible Changes ------------------------------ None. --- ### Releases/2.1.7 2.1.7 Release Notes =================== Channels 2.1.7 is another bugfix release in the 2.1 series, and the last release (at least for a long while) with Andrew Godwin as the primary maintainer. Thanks to everyone who has used, supported, and contributed to Channels over the years, and I hope we can keep it going with community support for a good while longer. Bugfixes & Small Changes ------------------------ * HTTP request body size limit is now enforced (the one set by the ``DATA_UPLOAD_MAX_MEMORY_SIZE`` setting) * ``database_sync_to_async`` now closes old connections before it runs code, which should prevent some connection errors in long-running pages or tests. * The auth middleware closes old connections before it runs, to solve similar old-connection issues. Backwards Incompatible Changes ------------------------------ None. --- ### Releases/2.2.0 2.2.0 Release Notes =================== Channels 2.2.0 updates the requirements for ASGI version 3, and the supporting Daphne v2.3 release. Backwards Incompatible Changes ------------------------------ None. --- ### Releases/2.3.0 2.3.0 Release Notes =================== Channels 2.3.0 updates the ``AsgiHandler`` HTTP request body handling to use a spooled temporary file, rather than reading the whole request body into memory. This significantly reduces the maximum memory requirements when serving Django views, and protects from DoS attacks, whilst still allowing large file uploads — a combination that had previously been *difficult*. Many thanks to Ivan Ergunov for his work on the improvements! 🎩 Backwards Incompatible Changes ------------------------------ As a result of the reworked body handling, ``AsgiRequest.__init__()`` is adjusted to expect a file-like ``stream``, rather than the whole ``body`` as bytes. Test cases instantiating requests directly will likely need to be updated to wrap the provided ``body`` in, e.g., ``io.BytesIO``. Next Up... ---------- We're looking to address a few issues around ``AsyncHttpConsumer``. Any human-power available to help on that, truly appreciated. 🙂 --- ### Releases/2.4.0 2.4.0 Release Notes =================== Channels 2.4 brings compatibility with Django 3.0s ``async_unsafe()`` checks. (Specifically we ensure session save calls are made inside an asgiref ``database_sync_to_async()``.) If you are using Daphne, it is recommended that you install Daphne version 2.4.1 or later for full compatibility with Django 3.0. Backwards Incompatible Changes ------------------------------ In line with the guidance provided by Django's supported versions policy we now also drop support for all Django versions before 2.2, which is the current LTS. --- ### Releases/3.0.0 3.0.0 Release Notes =================== The Channels 3 update brings Channels into line with Django's own async ASGI support, introduced with Django 3.0. Channels now integrates with Django's async HTTP handling, whilst continuing to support WebSockets and other exciting consumer types. Channels 3 supports Django 3.x and beyond, as well continuing to support the Django 2.2 LTS. We will support Django 2.2 at least until the Django 3.2 LTS is released, yet may drop support after that, but before Django 2.2 is officially end-of-life. Likewise, we support Python 3.6+ but we **strongly advise** you to update to the latest Python versions, so 3.9 at the time of release. In both our Django and Python support, we reflect the reality that async Python and async Django are still both evolving rapidly. Many issues we see simply disappear if you update. Whatever you are doing with async, you should make sure you're on the latest versions. The highlight of this release is the upgrade to ASGI v3, which allows integration with Django's ASGI support. There are also two additional deprecations that you will need to deal with if you are updating an existing application. Update to ASGI 3 ---------------- * Consumers are now ASGI 3 *single-callables* with the signature:: application(scope, receive, send) For generic consumers this change should be largely transparent, but you will need to update ``__init__()`` (no longer taking the scope) and ``__call__()`` (now taking the scope) **if you implemented these yourself**. * Consumers now have an ``as_asgi()`` class method you need to call when setting up your routing:: websocket_urlpatterns = [ re_path(r'ws/chat/(?P\w+)/$', consumers.ChatConsumer.as_asgi()), ] This returns an ASGI application that will instantiate the consumer per-request. It's similar to Django's ``as_view()``, which serves the same purpose. You can pass in keyword arguments for initialization if your consumer requires them. * Middleware will also need to be updated to the ASGI v3 signature. The ``channels.middleware.BaseMiddleware`` class is simplified, and available as an example. You probably don't need to actually subclass it under ASGI 3. Deprecations ------------ * Using ``ProtocolTypeRouter`` without an explicit ``"http"`` key is now deprecated. Following Django conventions, your entry point script should be named ``asgi.py``, and you should use Django's ``get_asgi_application()``, that is used by Django's default ``asgi.py`` template to route the ``"http"`` handler:: from django.core.asgi import get_asgi_application application = ProtocolTypeRouter({ "http": get_asgi_application(), # Other protocols here. }) Once the deprecation is removed, when we drop support for Django 2.2, not specifying an ``"http"`` key will mean that your application will not handle HTTP requests. * The Channels built-in HTTP protocol ``AsgiHandler`` is also deprecated. You should update to Django 3.0 or higher and use Django's ``get_asgi_application()``. Channel's ``AsgiHandler`` will be removed when we drop support for Django 2.2. --- ### Releases/3.0.1 3.0.1 Release Notes =================== Channels 3.0.1 fixes a bug in Channels 3.0. Bugfixes -------- * Fixes a bug in Channels 3.0 where ``SessionMiddleware`` would not correctly isolate per-instance scopes. --- ### Releases/3.0.2 3.0.2 Release Notes =================== Channels 3.0.2 fixes a bug in Channels 3.0.1 Bugfixes -------- * Fixes a bug in Channels 3.0 where `StaticFilesWrapper` was not updated to the ASGI 3 single-callable interface. * Users of the ``runworker`` command should ensure to update ``asgiref`` to version 3.3.1 or later, where an issue in ``asgiref.server.StatelessServer`` was addressed. --- ### Releases/3.0.3 3.0.3 Release Notes =================== Channels 3.0.3 fixes a security issue in Channels 3.0.2 CVE-2020-35681: Potential leakage of session identifiers using legacy ``AsgiHandler`` ------------------------------------------------------------------------------------- The legacy ``channels.http.AsgiHandler`` class, used for handling HTTP type requests in an ASGI environment prior to Django 3.0, did not correctly separate request scopes in Channels 3.0. In many cases this would result in a crash but, with correct timing responses could be sent to the wrong client, resulting in potential leakage of session identifiers and other sensitive data. This issue affects Channels 3.0.x before 3.0.3, and is resolved in Channels 3.0.3. Users of ``ProtocolTypeRouter`` not explicitly specifying the handler for the ``'http'`` key, or those explicitly using ``channels.http.AsgiHandler``, likely to support Django v2.2, are affected and should update immediately. Note that both an unspecified handler for the ``'http'`` key and using ``channels.http.AsgiHandler`` are deprecated, and will raise a warning, from Channels v3.0.0 This issue affects only the legacy channels provided class, and not Django's similar ``ASGIHandler``, available from Django 3.0. It is recommended to update to Django 3.0+ and use the Django provided ``ASGIHandler``. A simplified ``asgi.py`` script will look like this: .. code-block:: python import os from django.core.asgi import get_asgi_application # Fetch Django ASGI application early to ensure AppRegistry is populated # before importing consumers and AuthMiddlewareStack that may import ORM # models. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") django_asgi_app = get_asgi_application() # Import other Channels classes and consumers here. from channels.routing import ProtocolTypeRouter, URLRouter application = ProtocolTypeRouter({ # Explicitly set 'http' key using Django's ASGI application. "http": django_asgi_app, ), }) Please see :doc:`/deploying` for a more complete example. --- ### Releases/3.0.4 3.0.4 Release Notes =================== Channels 3.0.4 is a bugfix release in the 3.0 series. Bugfixes & Small Changes ------------------------ * Usage of ``urlparse`` in ``OriginValidator`` is corrected to maintain compatibility with recent point-releases of Python. * The import of ``django.contrib.auth.models.AnonymousUser`` in ``channels.auth`` is deferred until runtime, in order to avoid errors if ``AuthMiddleware`` or ``AuthMiddlewareStack`` were imported before ``django.setup()`` was run. * ``CookieMiddleware`` adds support for the ``samesite`` flag. * ``WebsocketConsumer.init()`` and ``AsyncWebsocketConsumer.init()`` no longer make a bad `super()` call to ``object.init()``. Backwards Incompatible Changes ------------------------------ None. --- ### Releases/3.0.5 3.0.5 Release Notes =================== Channels 3.0.5 is a bugfix release in the 3.0 series. Bugfixes & Small Changes ------------------------ * Removed use of ``providing_args`` keyword argument to consumer started signal, as support for this was removed in Django 4.0. Backwards Incompatible Changes ------------------------------ * Drops support for end-of-life Python 3.6 and Django 3.0 and 3.1. --- ### Releases/4.0.0 4.0.0 Release Notes =================== Channels 4 is the next major version of the Channels package. Together with the matching Daphne v4 and channels-redis v4 releases, it updates dependencies, fixes issues, and removes outdated code. It so provides the foundation for Channels development going forward. In most cases, you can update now by updating ``channels``, ``daphne``, and ``channels-redis`` as appropriate, with ``pip``, and by adding ``daphne`` at the top of your ``INSTALLED_APPS`` setting. First ``pip``:: pip install -U 'channels[daphne]' channels-redis Then in your Django settings file:: INSTALLED_APPS = [ "daphne", ... ] Read on for the details. Updated Python and Django support --------------------------------- In general Channels will try to follow Python and Django supported versions. As of release, that means Python 3.7, 3.8, 3.9, and 3.10, as well as Django 3.2, 4.0, and 4.1 are currently supported. As a note, we reserve the right to drop older Python versions, or the older Django LTS, once the newer one is released, before their official end-of-life if this is necessary to ease development. Dropping older Python and Django versions will be done in minor version releases, and will not be considered to require a major version change. The async support in both Python and Django continues to evolve rapidly. We advise you to always upgrade to the latest versions in order to avoid issues in older versions if you're building an async application. * Dropped support for Python 3.6. * Minimum Django version is now Django 3.2. * Added compatibility with Django 4.1. Decoupling of the Daphne application server ------------------------------------------- In order to allow users of other ASGI servers to use Channels without the overhead of Daphne and Twisted, the Daphne application server is now an optional dependency, installable either directly or with the ``daphne`` extra, as per the ``pip`` example above. * Where Daphne is used ``daphne>=4.0.0`` is required. The ``channels[daphne]`` extra assures this. * The ``runserver`` command is moved to the ``daphne`` package. In order to use the ``runserver`` command, add ``daphne`` to your ``INSTALLED_APPS``, before ``django.contrib.staticfiles``:: INSTALLED_APPS = [ "daphne", ... ] There is a new system check to ensure this ordering. Note, the ``runworker`` command remains a part of the ``channels`` app. * Use of ``ChannelsLiveServerTestCase`` still requires Daphne. Removal of the Django application wrappers ------------------------------------------ In order to add initial ASGI support to Django, Channels originally provided tools for wrapping your Django application and serving it under ASGI. This included an ASGI handler class, an ASGI HTTP request object, and an ASGI compatible version of the staticfiles handler for use with ``runserver`` Improved equivalents to all of these are what has been added to Django since Django version 3.0. As such serving of Django HTTP applications (whether using sync or async views) under ASGI is now Django's responsibility, and the matching Channels classes have been removed. Use of these classes was deprecated in Channels v3 and, if you've already moved to the Django equivalents there is nothing further to do. * Removed deprecated static files handling in favor of ``django.contrib.staticfiles``. * Removed the deprecated AsgiHandler, which wrapped Django views, in favour of Django's own ASGI support. You should use Django's ``get_asgi_application`` to provide the ``http`` handler for ProtocolTypeRouter, or an appropriate path for URLRouter, in order to route your Django application. * The supporting ``AsgiRequest`` is also removed, as it was only used for ``AsgiHandler``. * Removed deprecated automatic routing of ``http`` protocol handler in ``ProtocolTypeRouter``. You must explicitly register the ``http`` handler in your application if using ``ProtocolTypeRouter``. The minimal ``asgi.py`` file routing the Django ASGI application under a ``ProtocolTypeRouter`` will now look something like this:: import os from channels.routing import ProtocolTypeRouter from django.core.asgi import get_asgi_application os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings') application = ProtocolTypeRouter({ "http": get_asgi_application(), }) i.e. We use Django's ``get_asgi_application()``, and explicitly route an ``http`` handler for ``ProtocolTypeRouter``. This is merely for illustration of the changes. Please see the docs for more complete examples. Other changes ------------- * The use of the ``guarantee_single_callable()`` compatibility shim is removed. All applications must be ASGI v3 single-callables. * Removed the ``consumer_started`` and ``consumer_finished`` signals, unused since the 2.0 rewrite. * Fixed ``ChannelsLiveServerTestCase`` when running on systems using the ``spawn`` multiprocessing start method, such as macOS and Windows. --- ### Releases/4.1.0 4.1.0 Release Notes =================== Channels 4.1 is maintenance release in the 4.x series. Python and Django support ------------------------- * A Python version of 3.8 or higher is required. * Django 4.2 is now the minimum supported version. Bugfixes & Small Changes ------------------------ * Exceptions in ``HttpConsumer`` are now correctly propagated. Thanks to Adam Johnson. * URLRouter is updated for compatibility with in-development changes in Django. Thanks to Adam Johnson. * URLRouter is updated to correctly handle ``root_path``. Thanks to Alejandro R. Sedeño. * Websocket consumers are updated for newer ASGI spec versions, adding the ``headers`` parameter for the ``accept`` event, and ``reason`` for the ``close`` event. Thanks to Kristján Valur Jónsson. --- ### Releases/4.2.0 4.2.0 Release Notes =================== Channels 4.2 introduces a couple of major but backwards-compatible changes, including most notably enhanced async support and fixing a long-standing bug where tests would try and close db connections and erroneously fail. Additionally, support has been added for Django 5.1. Enhanced Async Support ---------------------- Support for asynchronous consumers has been greatly improved. The documentation has been updated to reflect the async ORM features added in Django 4.2. A new `channels.db.aclose_old_connections` function has been added to easily close old database connections in async consumers. Warning: Channels now automatically closes connections in async consumers before a new connection, after receiving message (but before dispatching to consumer code), and after disconnecting. This change has been made to more closely align with Django's request/response cycle, and to help users avoid attempting to use stale/broken connections. Notably, Channels does NOT close connections before or after a consumer **sends** data. This is to avoid database churn and more closely align with user expectations. Instead, users are expected to call `aclose_old_connections` occasionally during long-lived async connections. Additionally, channels will automatically use the new async interface for sessions if Django 5.1 or greater is installed. This new interface can be slightly faster in certain cases as it does not always need to context-switch into synchronous execution. This does require a backwards-incompatible change to `channels.sessions.InstanceSessionWrapper`: the `save_session` function is now `async`. If `InstanceSessionWrapper` was being subclassed in some way (note that this class is an implementation detail and not documented) and `save_session` was being called or overridden, it will need to be updated to be called with `await` or defined as `async`, respectively. Bugfixes & Small Changes ------------------------ * InMemoryChannelLayer has been greatly improved: it now honors expiry times and per-channel capacities, has parallel sending and a safer internal implementation. Note: queue capacities can no longer be changed after a channel has been created. Thanks to @devkral (Alexander) * Database connections are no longer closed inside tests, which prevents erroneous "Cannot operate on a closed database" errors when running tets. Thanks to Jon Janzen. * An old import override and an unused deprecation message were removed Thanks to @sevdog (Devid) and Jon Janzen. * WebsocketCommunicator now has informative `assert` error messages Thanks to Karel Hovorka. * WebsocketConsumer now checks that "text" is not None before attempting to use it. This improves support for Hypercorn. Thanks to Joaquín Ossandon. * BaseChannelLayer now has prototypes on all its methods to improve the hit-rate for smart autocompleters when users need to author their own channel layer and need to implement all required methods. Thanks to Jophy Ye. --- ### Releases/4.2.1 4.2.1 Release Notes =================== Channels 4.2.1 is a bugfix release in the 4.2 series. Bugfixes & Small Changes ------------------------ * Added official support for Django 5.2 LTS. * Added official support for Python 3.13. * Added a warning for the length of the channel layer group names. --- ### Releases/4.2.2 4.2.2 Release Notes =================== Channels 4.2.2 is a bugfix release in the 4.2 series. Bugfixes & Small Changes ------------------------ * Added fallbacks for old valid channel/group name checks. These (internal) methods were renamed in v4.2.1 without deprecation. This release adds (deprecated) fallback aliases to allow time for channel layers to update. --- ### Releases/4.3.0 4.3.0 Release Notes =================== Channels 4.3 is a maintenance release in the 4.x series. Bugfixes & Small Changes ------------------------ * Updated asgiref dependency to v3.9+. The ``ApplicationCommunicator`` testing utility will now return its result if the application is finished when sending input. Assert the ``CancelledError``` rather than allowing a timeout in your tests if you're affected by this change. * Dropped support for EOL Python and Django versions. Python 3.9 is now the minimum supported version. * Fixed compatibility of ``ChannelsLiveServerTestCase`` with Django 5.2. * Fixed DB setup for spawned testing subprocess, typically on Windows and macOS. --- ### Releases/4.3.1 4.3.1 Release Notes =================== Channels 4.3.1 is a bugfix release in the 4.3 series. Bugfixes & Small Changes ------------------------ * Fixed testing live server setup when test DB name was not set. --- ### Releases/4.3.2 4.3.2 Release Notes =================== Channels 4.3.1 is a bugfix release in the 4.3 series. Bugfixes & Small Changes ------------------------ * Confirmed support for Django 6.0. * Confirmed support for Python 3.14. * Added ``types`` extra for ``types-channels`` stubs. See installation docs. --- ### Releases/Index Release Notes ============= .. toctree:: :maxdepth: 1 4.3.2 4.3.1 4.3.0 4.2.2 4.2.1 4.2.0 4.1.0 4.0.0 3.0.5 3.0.4 3.0.3 3.0.2 3.0.1 3.0.0 2.4.0 2.3.0 2.2.0 2.1.7 2.1.6 2.1.5 2.1.4 2.1.3 2.1.2 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.3 1.0.2 1.0.1 1.0.0 --- ### Topics/Authentication Authentication ============== Channels supports standard Django authentication out-of-the-box for HTTP and WebSocket consumers, and you can write your own middleware or handling code if you want to support a different authentication scheme (for example, tokens in the URL). Django authentication --------------------- The ``AuthMiddleware`` in Channels supports standard Django authentication, where the user details are stored in the session. It allows read-only access to a user object in the ``scope``. ``AuthMiddleware`` requires ``SessionMiddleware`` to function, which itself requires ``CookieMiddleware``. For convenience, these are also provided as a combined callable called ``AuthMiddlewareStack`` that includes all three. To use the middleware, wrap it around the appropriate level of consumer in your ``asgi.py``: .. code-block:: python from django.urls import re_path from channels.routing import ProtocolTypeRouter, URLRouter from channels.auth import AuthMiddlewareStack from channels.security.websocket import AllowedHostsOriginValidator from myapp import consumers application = ProtocolTypeRouter({ "websocket": AllowedHostsOriginValidator( AuthMiddlewareStack( URLRouter([ re_path(r"^front(end)/$", consumers.AsyncChatConsumer.as_asgi()), ]) ) ), }) While you can wrap the middleware around each consumer individually, it's recommended you wrap it around a higher-level application component, like in this case the ``URLRouter``. Note that the ``AuthMiddleware`` will only work on protocols that provide HTTP headers in their ``scope`` - by default, this is HTTP and WebSocket. To access the user, just use ``self.scope["user"]`` in your consumer code: .. code-block:: python class ChatConsumer(WebsocketConsumer): def connect(self): self.user = self.scope["user"] self.accept() Custom Authentication --------------------- If you have a custom authentication scheme, you can write a custom middleware to parse the details and put a user object (or whatever other object you need) into your scope. Middleware is written as a callable that takes an ASGI application and wraps it to return another ASGI application. Most authentication can just be done on the scope, so all you need to do is override the initial constructor that takes a scope, rather than the event-running coroutine. Here's a simple example of a middleware that just takes a user ID out of the query string and uses that: .. code-block:: python from channels.db import database_sync_to_async @database_sync_to_async def get_user(user_id): try: return User.objects.get(id=user_id) except User.DoesNotExist: return AnonymousUser() class QueryAuthMiddleware: """ Custom middleware (insecure) that takes user IDs from the query string. """ def __init__(self, app): # Store the ASGI application we were passed self.app = app async def __call__(self, scope, receive, send): # Look up user from query string (you should also do things like # checking if it is a valid user ID, or if scope["user"] is already # populated). scope['user'] = await get_user(int(scope["query_string"])) return await self.app(scope, receive, send) The same principles can be applied to authenticate over non-HTTP protocols; for example, you might want to use someone's chat username from a chat protocol to turn it into a user. How to log a user in/out ------------------------ Channels provides direct login and logout functions (much like Django's ``contrib.auth`` package does) as ``channels.auth.login`` and ``channels.auth.logout``. Within your consumer you can await ``login(scope, user, backend=None)`` to log a user in. This requires that your scope has a ``session`` object; the best way to do this is to ensure your consumer is wrapped in a ``SessionMiddlewareStack`` or a ``AuthMiddlewareStack``. You can logout a user with the ``logout(scope)`` async function. If you are in a WebSocket consumer, or logging-in after the first response has been sent in a http consumer, the session is populated **but will not be saved automatically** - you must call ``scope["session"].save()`` after login in your consumer code: .. code-block:: python from channels.auth import login class ChatConsumer(AsyncWebsocketConsumer): ... async def receive(self, text_data): ... # login the user to this session. await login(self.scope, user) # save the session (if the session backend does not access the db you can use `sync_to_async`) await database_sync_to_async(self.scope["session"].save)() When calling ``login(scope, user)``, ``logout(scope)`` or ``get_user(scope)`` from a synchronous function you will need to wrap them in ``async_to_sync``, as we only provide async versions: .. code-block:: python from asgiref.sync import async_to_sync from channels.auth import login class SyncChatConsumer(WebsocketConsumer): ... def receive(self, text_data): ... async_to_sync(login)(self.scope, user) self.scope["session"].save() .. note:: If you are using a long running consumer, websocket or long-polling HTTP it is possible that the user will be logged out of their session elsewhere while your consumer is running. You can periodically use ``get_user(scope)`` to be sure that the user is still logged in. --- ### Topics/Channel Layers Channel Layers ============== Channel layers allow you to talk between different instances of an application. They're a useful part of making a distributed realtime application if you don't want to have to shuttle all of your messages or events through a database. Additionally, they can also be used in combination with a worker process to make a basic task queue or to offload tasks - read more in :doc:`/topics/worker`. .. note:: Channel layers are an entirely optional part of Channels. If you don't want to use them, just leave ``CHANNEL_LAYERS`` unset, or set it to the empty dict ``{}``. .. warning:: Channel layers have a purely async interface (for both send and receive); you will need to wrap them in a converter if you want to call them from synchronous code (see below). Configuration ------------- Channel layers are configured via the ``CHANNEL_LAYERS`` Django setting. You can get the default channel layer from a project with ``channels.layers.get_channel_layer()``, but if you are using consumers, then a copy is automatically provided for you on the consumer as ``self.channel_layer``. Redis Channel Layer ******************* `channels_redis`_ is the only official Django-maintained channel layer supported for production use. The layer uses Redis as its backing store, and it supports both a single-server and sharded configurations as well as group support. To use this layer you'll need to install the `channels_redis`_ package. .. _`channels_redis`: https://pypi.org/project/channels-redis/ In this example, Redis is running on localhost (127.0.0.1) port 6379: .. code-block:: python CHANNEL_LAYERS = { "default": { "BACKEND": "channels_redis.core.RedisChannelLayer", "CONFIG": { "hosts": [("127.0.0.1", 6379)], }, }, } In-Memory Channel Layer *********************** Channels also comes packaged with an in-memory Channels Layer. This layer can be helpful in :doc:`/topics/testing` or for local-development purposes: .. code-block:: python CHANNEL_LAYERS = { "default": { "BACKEND": "channels.layers.InMemoryChannelLayer" } } .. warning:: **Do Not Use In Production** In-memory channel layers operate with each process as a separate layer. This means that no cross-process messaging is possible. As the core value of channel layers is to provide distributed messaging, in-memory usage will result in sub-optimal performance, and ultimately data-loss in a multi-instance environment. Synchronous Functions --------------------- By default the ``send()``, ``group_send()``, ``group_add()`` and other functions are async functions, meaning you have to ``await`` them. If you need to call them from synchronous code, you'll need to use the handy ``asgiref.sync.async_to_sync`` wrapper: .. code-block:: python from asgiref.sync import async_to_sync async_to_sync(channel_layer.send)("channel_name", {...}) What To Send Over The Channel Layer ----------------------------------- The channel layer is for high-level application-to-application communication. When you send a message, it is received by the consumers listening to the group or channel on the other end. What this means is that you should send high-level events over the channel layer, and then have consumers handle those events, and do appropriate low-level networking to their attached client. For example, a chat application could send events like this over the channel layer: .. code-block:: python await self.channel_layer.group_send( room.group_name, { "type": "chat.message", "room_id": room_id, "username": self.scope["user"].username, "message": message, } ) And then the consumers define a handling function to receive those events and turn them into WebSocket frames: .. code-block:: python async def chat_message(self, event): """ Called when someone has messaged our chat. """ # Send a message down to the client await self.send_json( { "msg_type": settings.MSG_TYPE_MESSAGE, "room": event["room_id"], "username": event["username"], "message": event["message"], }, ) Any consumer based on Channels' ``SyncConsumer`` or ``AsyncConsumer`` will automatically provide you a ``self.channel_layer`` and ``self.channel_name`` attribute, which contains a pointer to the channel layer instance and the channel name that will reach the consumer respectively. Any message sent to that channel name - or to a group the channel name was added to - will be received by the consumer much like an event from its connected client, and dispatched to a named method on the consumer. The name of the method will be the ``type`` of the event with periods replaced by underscores - so, for example, an event coming in over the channel layer with a ``type`` of ``chat.join`` will be handled by the method ``chat_join``. .. note:: If you are inheriting from the ``AsyncConsumer`` class tree, all your event handlers, including ones for events over the channel layer, must be asynchronous (``async def``). If you are in the ``SyncConsumer`` class tree instead, they must all be synchronous (``def``). Single Channels --------------- Each application instance - so, for example, each long-running HTTP request or open WebSocket - results in a single Consumer instance, and if you have channel layers enabled, Consumers will generate a unique *channel name* for themselves, and start listening on it for events. This means you can send those consumers events from outside the process - from other consumers, maybe, or from management commands - and they will react to them and run code just like they would events from their client connection. The channel name is available on a consumer as ``self.channel_name``. Here's an example of writing the channel name into a database upon connection, and then specifying a handler method for events on it: .. code-block:: python class ChatConsumer(WebsocketConsumer): def connect(self): # Make a database row with our channel name Clients.objects.create(channel_name=self.channel_name) def disconnect(self, close_code): # Note that in some rare cases (power loss, etc) disconnect may fail # to run; this naive example would leave zombie channel names around. Clients.objects.filter(channel_name=self.channel_name).delete() def chat_message(self, event): # Handles the "chat.message" event when it's sent to us. self.send(text_data=event["text"]) Note that, because you're mixing event handling from the channel layer and from the protocol connection, you need to make sure that your type names do not clash. It's recommended you prefix type names (like we did here with ``chat.``) to avoid clashes. To send to a single channel, just find its channel name (for the example above, we could crawl the database), and use ``channel_layer.send``: .. code-block:: python from channels.layers import get_channel_layer channel_layer = get_channel_layer() await channel_layer.send("channel_name", { "type": "chat.message", "text": "Hello there!", }) .. _groups: Groups ------ Obviously, sending to individual channels isn't particularly useful - in most cases you'll want to send to multiple channels/consumers at once as a broadcast. Not only for cases like a chat where you want to send incoming messages to everyone in the room, but even for sending to an individual user who might have more than one browser tab or device connected. You can construct your own solution for this if you like using your existing datastores, or you can use the Groups system built-in to some channel layers. Groups is a broadcast system that: * Allows you to add and remove channel names from named groups, and send to those named groups. * Provides group expiry for clean-up of connections whose disconnect handler didn't get to run (e.g. power failure) They do not allow you to enumerate or list the channels in a group; it's a pure broadcast system. If you need more precise control or need to know who is connected, you should build your own system or use a suitable third-party one. You use groups by adding a channel to them during connection, and removing it during disconnection, illustrated here on the WebSocket generic consumer: .. code-block:: python # This example uses WebSocket consumer, which is synchronous, and so # needs the async channel layer functions to be converted. from asgiref.sync import async_to_sync class ChatConsumer(WebsocketConsumer): def connect(self): async_to_sync(self.channel_layer.group_add)("chat", self.channel_name) def disconnect(self, close_code): async_to_sync(self.channel_layer.group_discard)("chat", self.channel_name) .. note:: Group names are restricted to ASCII alphanumerics, hyphens, and periods only and are limited to a maximum length of 100 in the default backend. Then, to send to a group, use ``group_send``, like in this small example which broadcasts chat messages to every connected socket when combined with the code above: .. code-block:: python class ChatConsumer(WebsocketConsumer): ... def receive(self, text_data): async_to_sync(self.channel_layer.group_send)( "chat", { "type": "chat.message", "text": text_data, }, ) def chat_message(self, event): self.send(text_data=event["text"]) Using Outside Of Consumers -------------------------- You'll often want to send to the channel layer from outside of the scope of a consumer, and so you won't have ``self.channel_layer``. In this case, you should use the ``get_channel_layer`` function to retrieve it: .. code-block:: python from channels.layers import get_channel_layer channel_layer = get_channel_layer() Then, once you have it, you can just call methods on it. Remember that **channel layers only support async methods**, so you can either call it from your own asynchronous context: .. code-block:: python for chat_name in chats: await channel_layer.group_send( chat_name, {"type": "chat.system_message", "text": announcement_text}, ) Or you'll need to use async_to_sync: .. code-block:: python from asgiref.sync import async_to_sync async_to_sync(channel_layer.group_send)("chat", {"type": "chat.force_disconnect"}) --- ### Topics/Consumers Consumers ========= Channels is built around a basic low-level spec called :doc:`ASGI `. ASGI is more designed for interoperability than for writing complex applications in. So, Channels provides you with Consumers, a rich abstraction that allows you to create ASGI applications easily. Consumers do a couple of things in particular: * Structure your code as a series of functions to be called whenever an event happens, rather than making you write an event loop. * Allow you to write synchronous or async code, and deal with handoffs and threading for you. Of course, you are free to ignore consumers and use the other parts of Channels - like routing, session handling and authentication - with any ASGI app, but they're generally the best way to write your application code. .. _sync_to_async: Basic Layout ------------ A consumer is a subclass of either ``channels.consumer.AsyncConsumer`` or ``channels.consumer.SyncConsumer``. As these names suggest, one will expect you to write async-capable code, while the other will run your code synchronously in a threadpool. Let's look at a basic example of a ``SyncConsumer``: .. code-block:: python from channels.consumer import SyncConsumer class EchoConsumer(SyncConsumer): def websocket_connect(self, event): self.send({ "type": "websocket.accept", }) def websocket_receive(self, event): self.send({ "type": "websocket.send", "text": event["text"], }) This is a very simple WebSocket echo server - it will accept all incoming WebSocket connections, and then reply to all incoming WebSocket text frames with the same text. Consumers are structured around a series of named methods corresponding to the ``type`` value of the messages they are going to receive, with any ``.`` replaced by ``_``. The two handlers above are handling ``websocket.connect`` and ``websocket.receive`` messages respectively. How did we know what event types we were going to get and what would be in them (like ``websocket.receive`` having a ``text``) key? That's because we designed this against the ASGI WebSocket specification, which tells us how WebSockets are presented - read more about it in :doc:`ASGI ` - and protected this application with a router that checks for a scope type of ``websocket`` - see more about that in :doc:`/topics/routing`. Apart from that, the only other basic API is ``self.send(event)``. This lets you send events back to the client or protocol server as defined by the protocol - if you read the WebSocket protocol, you'll see that the dict we send above is how you send a text frame to the client. The ``AsyncConsumer`` is laid out very similarly, but all the handler methods must be coroutines, and ``self.send`` is a coroutine: .. code-block:: python from channels.consumer import AsyncConsumer class EchoConsumer(AsyncConsumer): async def websocket_connect(self, event): await self.send({ "type": "websocket.accept", }) async def websocket_receive(self, event): await self.send({ "type": "websocket.send", "text": event["text"], }) When should you use ``AsyncConsumer`` and when should you use ``SyncConsumer``? The main thing to consider is what you're talking to. If you call a slow synchronous function from inside an ``AsyncConsumer`` you're going to hold up the entire event loop, so they're only useful if you're also calling async code (for example, using ``HTTPX`` to fetch 20 pages in parallel). If you're calling any part of Django's ORM or other synchronous code, you should use a ``SyncConsumer``, as this will run the whole consumer in a thread and stop your ORM queries blocking the entire server. We recommend that you **write SyncConsumers by default**, and only use AsyncConsumers in cases where you know you are doing something that would be improved by async handling (long-running tasks that could be done in parallel) *and* you are only using async-native libraries. If you really want to call a synchronous function from an ``AsyncConsumer``, take a look at ``asgiref.sync.sync_to_async``, which is the utility that Channels uses to run ``SyncConsumers`` in threadpools, and can turn any synchronous callable into an asynchronous coroutine. .. important:: If you want to call the Django ORM from an ``AsyncConsumer`` (or any other asynchronous code), you should use the ``database_sync_to_async`` adapter or use the async versions of the methods (prefixed with ``a``, like ``aget``). See :doc:`/topics/databases` for more. Closing Consumers ~~~~~~~~~~~~~~~~~ When the socket or connection attached to your consumer is closed - either by you or the client - you will likely get an event sent to you (for example, ``http.disconnect`` or ``websocket.disconnect``), and your application instance will be given a short amount of time to act on it. Once you have finished doing your post-disconnect cleanup, you need to raise ``channels.exceptions.StopConsumer`` to halt the ASGI application cleanly and let the server clean it up. If you leave it running - by not raising this exception - the server will reach its application close timeout (which is 10 seconds by default in Daphne) and then kill your application and raise a warning. The generic consumers below do this for you, so this is only needed if you are writing your own consumer class based on ``AsyncConsumer`` or ``SyncConsumer``. However, if you override their ``__call__`` method, or block the handling methods that it calls from returning, you may still run into this; take a look at their source code if you want more information. Additionally, if you launch your own background coroutines, make sure to also shut them down when the connection is finished, or you'll leak coroutines into the server. Channel Layers ~~~~~~~~~~~~~~ Consumers also let you deal with Channel's *channel layers*, to let them send messages between each other either one-to-one or via a broadcast system called groups. Consumers will use the channel layer ``default`` unless the ``channel_layer_alias`` attribute is set when subclassing any of the provided ``Consumer`` classes. To use the channel layer ``echo_alias`` we would set it like so: .. code-block:: python from channels.consumer import SyncConsumer class EchoConsumer(SyncConsumer): channel_layer_alias = "echo_alias" You can read more in :doc:`/topics/channel_layers`. .. _scope: Scope ----- Consumers receive the connection's ``scope`` when they are called, which contains a lot of the information you'd find on the ``request`` object in a Django view. It's available as ``self.scope`` inside the consumer's methods. Scopes are part of the :doc:`ASGI specification `, but here are some common things you might want to use: * ``scope["path"]``, the path on the request. *(HTTP and WebSocket)* * ``scope["headers"]``, raw name/value header pairs from the request *(HTTP and WebSocket)* * ``scope["method"]``, the method name used for the request. *(HTTP)* If you enable things like :doc:`authentication`, you'll also be able to access the user object as ``scope["user"]``, and the URLRouter, for example, will put captured groups from the URL into ``scope["url_route"]``. In general, the scope is the place to get connection information and where middleware will put attributes it wants to let you access (in the same way that Django's middleware adds things to ``request``). For a full list of what can occur in a connection scope, look at the basic ASGI spec for the protocol you are terminating, plus any middleware or routing code you are using. The web (HTTP and WebSocket) scopes are available in `the Web ASGI spec `_. Generic Consumers ----------------- What you see above is the basic layout of a consumer that works for any protocol. Much like Django's *generic views*, Channels ships with *generic consumers* that wrap common functionality up so you don't need to rewrite it, specifically for HTTP and WebSocket handling. WebsocketConsumer ~~~~~~~~~~~~~~~~~ Available as ``channels.generic.websocket.WebsocketConsumer``, this wraps the verbose plain-ASGI message sending and receiving into handling that just deals with text and binary frames: .. code-block:: python from channels.generic.websocket import WebsocketConsumer class MyConsumer(WebsocketConsumer): groups = ["broadcast"] def connect(self): # Called on connection. # To accept the connection call: self.accept() # Or accept the connection and specify a chosen subprotocol. # A list of subprotocols specified by the connecting client # will be available in self.scope['subprotocols'] self.accept("subprotocol") # To reject the connection, call: self.close() def receive(self, text_data=None, bytes_data=None): # Called with either text_data or bytes_data for each frame # You can call: self.send(text_data="Hello world!") # Or, to send a binary frame: self.send(bytes_data="Hello world!") # Want to force-close the connection? Call: self.close() # Or add a custom WebSocket error code! self.close(code=4123) def disconnect(self, close_code): # Called when the socket closes You can also raise ``channels.exceptions.AcceptConnection`` or ``channels.exceptions.DenyConnection`` from anywhere inside the ``connect`` method in order to accept or reject a connection, if you want reusable authentication or rate-limiting code that doesn't need to use mixins. A ``WebsocketConsumer``'s channel will automatically be added to (on connect) and removed from (on disconnect) any groups whose names appear in the consumer's ``groups`` class attribute. ``groups`` must be an iterable, and a channel layer with support for groups must be set as the channel backend (``channels.layers.InMemoryChannelLayer`` and ``channels_redis.core.RedisChannelLayer`` both support groups). If no channel layer is configured or the channel layer doesn't support groups, connecting to a ``WebsocketConsumer`` with a non-empty ``groups`` attribute will raise ``channels.exceptions.InvalidChannelLayerError``. See :ref:`groups` for more. AsyncWebsocketConsumer ~~~~~~~~~~~~~~~~~~~~~~ Available as ``channels.generic.websocket.AsyncWebsocketConsumer``, this has the exact same methods and signature as ``WebsocketConsumer`` but everything is async, and the functions you need to write have to be as well: .. code-block:: python from channels.generic.websocket import AsyncWebsocketConsumer class MyConsumer(AsyncWebsocketConsumer): groups = ["broadcast"] async def connect(self): # Called on connection. # To accept the connection call: await self.accept() # Or accept the connection and specify a chosen subprotocol. # A list of subprotocols specified by the connecting client # will be available in self.scope['subprotocols'] await self.accept("subprotocol") # To reject the connection, call: await self.close() async def receive(self, text_data=None, bytes_data=None): # Called with either text_data or bytes_data for each frame # You can call: await self.send(text_data="Hello world!") # Or, to send a binary frame: await self.send(bytes_data="Hello world!") # Want to force-close the connection? Call: await self.close() # Or add a custom WebSocket error code! await self.close(code=4123) async def disconnect(self, close_code): # Called when the socket closes JsonWebsocketConsumer ~~~~~~~~~~~~~~~~~~~~~ Available as ``channels.generic.websocket.JsonWebsocketConsumer``, this works like ``WebsocketConsumer``, except it will auto-encode and decode to JSON sent as WebSocket text frames. The only API differences are: * Your ``receive_json`` method must take a single argument, ``content``, that is the decoded JSON object. * ``self.send_json`` takes only a single argument, ``content``, which will be encoded to JSON for you. If you want to customise the JSON encoding and decoding, you can override the ``encode_json`` and ``decode_json`` classmethods. AsyncJsonWebsocketConsumer ~~~~~~~~~~~~~~~~~~~~~~~~~~ An async version of ``JsonWebsocketConsumer``, available as ``channels.generic.websocket.AsyncJsonWebsocketConsumer``. Note that even ``encode_json`` and ``decode_json`` are async functions. AsyncHttpConsumer ~~~~~~~~~~~~~~~~~ Available as ``channels.generic.http.AsyncHttpConsumer``, this offers basic primitives to implement a HTTP endpoint: .. code-block:: python from channels.generic.http import AsyncHttpConsumer class BasicHttpConsumer(AsyncHttpConsumer): async def handle(self, body): await asyncio.sleep(10) await self.send_response(200, b"Your response bytes", headers=[ (b"Content-Type", b"text/plain"), ]) You are expected to implement your own ``handle`` method. The method receives the whole request body as a single bytestring. Headers may either be passed as a list of tuples or as a dictionary. The response body content is expected to be a bytestring. You can also implement a ``disconnect`` method if you want to run code on disconnect - for example, to shut down any coroutines you launched. This will run even on an unclean disconnection, so don't expect that ``handle`` has finished running cleanly. If you need more control over the response, e.g. for implementing long polling, use the lower level ``self.send_headers`` and ``self.send_body`` methods instead. This example already mentions channel layers which will be explained in detail later: .. code-block:: python import json from channels.generic.http import AsyncHttpConsumer class LongPollConsumer(AsyncHttpConsumer): async def handle(self, body): await self.send_headers(headers=[ (b"Content-Type", b"application/json"), ]) # Headers are only sent after the first body event. # Set "more_body" to tell the interface server to not # finish the response yet: await self.send_body(b"", more_body=True) async def chat_message(self, event): # Send JSON and finish the response: await self.send_body(json.dumps(event).encode("utf-8")) Of course you can also use those primitives to implement a HTTP endpoint for `Server-sent events `_: .. code-block:: python from datetime import datetime from channels.generic.http import AsyncHttpConsumer class ServerSentEventsConsumer(AsyncHttpConsumer): async def handle(self, body): await self.send_headers(headers=[ (b"Cache-Control", b"no-cache"), (b"Content-Type", b"text/event-stream"), (b"Transfer-Encoding", b"chunked"), ]) while True: payload = "data: %s\n\n" % datetime.now().isoformat() await self.send_body(payload.encode("utf-8"), more_body=True) await asyncio.sleep(1) --- ### Topics/Databases Database Access =============== The Django ORM is a synchronous piece of code, and so if you want to access it from asynchronous code you need to do special handling to make sure its connections are closed properly. If you're using ``SyncConsumer``, or anything based on it - like ``JsonWebsocketConsumer`` - you don't need to do anything special, as all your code is already run in a synchronous mode and Channels will do the cleanup for you as part of the ``SyncConsumer`` code. If you are writing asynchronous code, however, you will need to call database methods in a safe, synchronous context, using ``database_sync_to_async`` or by using the asynchronous methods prefixed with ``a`` like ``Model.objects.aget()``. Database Connections -------------------- Channels can potentially open a lot more database connections than you may be used to if you are using threaded consumers (synchronous ones) - it can open up to one connection per thread. If you wish to control the maximum number of threads used, set the ``ASGI_THREADS`` environment variable to the maximum number you wish to allow. By default, the number of threads is set to "the number of CPUs * 5" for Python 3.7 and below, and `min(32, os.cpu_count() + 4)` for Python 3.8+. To avoid having too many threads idling in connections, you can instead rewrite your code to use async consumers and only dip into threads when you need to use Django's ORM (using ``database_sync_to_async``). When using async consumers Channels will automatically call Django's ``close_old_connections`` method when a new connection is started, when a connection is closed, and whenever anything is received from the client. This mirrors Django's logic for closing old connections at the start and end of a request, to the extent possible. Connections are *not* automatically closed when sending data from a consumer since Channels has no way to determine if this is a one-off send (and connections could be closed) or a series of sends (in which closing connections would kill performance). Instead, if you have a long-lived async consumer you should periodically call ``aclose_old_connections`` (see below). database_sync_to_async ---------------------- ``channels.db.database_sync_to_async`` is a version of ``asgiref.sync.sync_to_async`` that also cleans up database connections on exit. To use it, write your ORM queries in a separate function or method, and then call it with ``database_sync_to_async`` like so: .. code-block:: python from channels.db import database_sync_to_async async def connect(self): self.username = await database_sync_to_async(get_name)() def get_name(self): return User.objects.all()[0].name You can also use it as a decorator: .. code-block:: python from channels.db import database_sync_to_async async def connect(self): self.username = await get_name() @database_sync_to_async def get_name(self): return User.objects.all()[0].name aclose_old_connections ---------------------- ``channels.db.aclose_old_connections`` is an async wrapper around Django's ``close_old_connections``. When using a long-lived ``AsyncConsumer`` that calls the Django ORM it is important to call this function periodically. Preferrably, this function should be called before making the first query in a while. For example, it should be called if the Consumer is woken up by a channels layer event and needs to make a few ORM queries to determine what to send to the client. This function should be called *before* making those queries. Calling this function more than necessary is not necessarily a bad thing, but it does require a context switch to synchronous code and so incurs a small penalty. --- ### Topics/Routing Routing ======= While consumers are valid :doc:`ASGI ` applications, you don't want to just write one and have that be the only thing you can give to protocol servers like Daphne. Channels provides routing classes that allow you to combine and stack your consumers (and any other valid ASGI application) to dispatch based on what the connection is. .. important:: Channels routers only work on the *scope* level, not on the level of individual *events*, which means you can only have one consumer for any given connection. Routing is to work out what single consumer to give a connection, not how to spread events from one connection across multiple consumers. Routers are themselves valid ASGI applications, and it's possible to nest them. We suggest that you have a ``ProtocolTypeRouter`` as the root application of your project - the one that you pass to protocol servers - and nest other, more protocol-specific routing underneath there. Channels expects you to be able to define a single *root application*, and provide the path to it as the ``ASGI_APPLICATION`` setting (think of this as being analogous to the ``ROOT_URLCONF`` setting in Django). There's no fixed rule as to where you need to put the routing and the root application, but we recommend following Django's conventions and putting them in a project-level file called ``asgi.py``, next to ``urls.py``. You can read more about deploying Channels projects and settings in :doc:`/deploying`. Here's an example of what that ``asgi.py`` might look like: .. include:: ../includes/asgi_example.rst .. note:: We call the ``as_asgi()`` classmethod when routing our consumers. This returns an ASGI wrapper application that will instantiate a new consumer instance for each connection or scope. This is similar to Django's ``as_view()``, which plays the same role for per-request instances of class-based views. It's possible to have routers from third-party apps, too, or write your own, but we'll go over the built-in Channels ones here. ProtocolTypeRouter ------------------ ``channels.routing.ProtocolTypeRouter`` This should be the top level of your ASGI application stack and the main entry in your routing file. It lets you dispatch to one of a number of other ASGI applications based on the ``type`` value present in the ``scope``. Protocols will define a fixed type value that their scope contains, so you can use this to distinguish between incoming connection types. It takes a single argument - a dictionary mapping type names to ASGI applications that serve them: .. code-block:: python ProtocolTypeRouter({ "http": some_app, "websocket": some_other_app, }) If you want to split HTTP handling between long-poll handlers and Django views, use a URLRouter using Django's ``get_asgi_application()`` specified as the last entry with a match-everything pattern. .. _urlrouter: URLRouter --------- ``channels.routing.URLRouter`` Routes ``http`` or ``websocket`` type connections via their HTTP path. Takes a single argument, a list of Django URL objects (either ``path()`` or ``re_path()``): .. code-block:: python URLRouter([ re_path(r"^longpoll/$", LongPollConsumer.as_asgi()), re_path(r"^notifications/(?P\w+)/$", LongPollConsumer.as_asgi()), re_path(r"", get_asgi_application()), ]) Any captured groups will be provided in ``scope`` as the key ``url_route``, a dict with a ``kwargs`` key containing a dict of the named regex groups and an ``args`` key with a list of positional regex groups. Note that named and unnamed groups cannot be mixed: Positional groups are discarded as soon as a single named group is matched. For example, to pull out the named group ``stream`` in the example above, you would do this: .. code-block:: python stream = self.scope["url_route"]["kwargs"]["stream"] Please note that ``URLRouter`` nesting will not work properly with ``path()`` routes if inner routers are wrapped by additional middleware. See `Issue #1428 `__. ChannelNameRouter ----------------- ``channels.routing.ChannelNameRouter`` Routes ``channel`` type scopes based on the value of the ``channel`` key in their scope. Intended for use with the :doc:`/topics/worker`. It takes a single argument - a dictionary mapping channel names to ASGI applications that serve them: .. code-block:: python ChannelNameRouter({ "thumbnails-generate": some_app, "thumbnails-delete": some_other_app, }) --- ### Topics/Security Security ======== This covers basic security for protocols you're serving via Channels and helpers that we provide. WebSockets ---------- WebSockets start out life as a HTTP request, including all the cookies and headers, and so you can use the standard :doc:`/topics/authentication` code in order to grab current sessions and check user IDs. There is also a risk of cross-site request forgery (CSRF) with WebSockets though, as they can be initiated from any site on the internet to your domain, and will still have the user's cookies and session from your site. If you serve private data down the socket, you should restrict the sites which are allowed to open sockets to you. This is done via the ``channels.security.websocket`` package, and the two ASGI middlewares it contains, ``OriginValidator`` and ``AllowedHostsOriginValidator``. ``OriginValidator`` lets you restrict the valid options for the ``Origin`` header that is sent with every WebSocket to say where it comes from. Just wrap it around your WebSocket application code like this, and pass it a list of valid domains as the second argument. You can pass only a single domain (for example, ``.allowed-domain.com``) or a full origin, in the format ``scheme://domain[:port]`` (for example, ``http://allowed-domain.com:80``). Port is optional, but recommended: .. code-block:: python from channels.security.websocket import OriginValidator application = ProtocolTypeRouter({ "websocket": OriginValidator( AuthMiddlewareStack( URLRouter([ ... ]) ), [".goodsite.com", "http://.goodsite.com:80", "http://other.site.com"], ), }) Note: If you want to resolve any domain, then use the origin ``*``. Often, the set of domains you want to restrict to is the same as the Django ``ALLOWED_HOSTS`` setting, which performs a similar security check for the ``Host`` header, and so ``AllowedHostsOriginValidator`` lets you use this setting without having to re-declare the list: .. code-block:: python from channels.security.websocket import AllowedHostsOriginValidator application = ProtocolTypeRouter({ "websocket": AllowedHostsOriginValidator( AuthMiddlewareStack( URLRouter([ ... ]) ), ), }) ``AllowedHostsOriginValidator`` will also automatically allow local connections through if the site is in ``DEBUG`` mode, much like Django's host validation. --- ### Topics/Sessions Sessions ======== Channels supports standard Django sessions using HTTP cookies for both HTTP and WebSocket. There are some caveats, however. Basic Usage ----------- The ``SessionMiddleware`` in Channels supports standard Django sessions, and like all middleware, should be wrapped around the ASGI application that needs the session information in its scope (for example, a ``URLRouter`` to apply it to a whole collection of consumers, or an individual consumer). ``SessionMiddleware`` requires ``CookieMiddleware`` to function. For convenience, these are also provided as a combined callable called ``SessionMiddlewareStack`` that includes both. All are importable from ``channels.session``. To use the middleware, wrap it around the appropriate level of consumer in your ``asgi.py``: .. code-block:: python from channels.routing import ProtocolTypeRouter, URLRouter from channels.security.websocket import AllowedHostsOriginValidator from channels.sessions import SessionMiddlewareStack from myapp import consumers application = ProtocolTypeRouter({ "websocket": AllowedHostsOriginValidator( SessionMiddlewareStack( URLRouter([ path("frontend/", consumers.AsyncChatConsumer.as_asgi()), ]) ) ), }) ``SessionMiddleware`` will only work on protocols that provide HTTP headers in their ``scope`` - by default, this is HTTP and WebSocket. To access the session, use ``self.scope["session"]`` in your consumer code: .. code-block:: python class ChatConsumer(WebsocketConsumer): def connect(self, event): self.scope["session"]["seed"] = random.randint(1, 1000) ``SessionMiddleware`` respects all the same Django settings as the default Django session framework, like ``SESSION_COOKIE_NAME`` and ``SESSION_COOKIE_DOMAIN``. Session Persistence ------------------- Within HTTP consumers or ASGI applications, session persistence works as you would expect from Django HTTP views - sessions are saved whenever you send a HTTP response that does not have status code ``500``. This is done by overriding any ``http.response.start`` messages to inject cookie headers into the response as you send it out. If you have set the ``SESSION_SAVE_EVERY_REQUEST`` setting to ``True``, it will save the session and send the cookie on every response, otherwise it will only save whenever the session is modified. If you are in a WebSocket consumer, however, the session is populated **but will never be saved automatically** - you must call ``scope["session"].save()`` (or the asynchronous version, ``scope["session"].asave()``) yourself whenever you want to persist a session to your session store. If you don't save, the session will still work correctly inside the consumer (as it's stored as an instance variable), but other connections or HTTP views won't be able to see the changes. .. note:: If you are in a long-polling HTTP consumer, you might want to save changes to the session before you send a response. If you want to do this, call ``scope["session"].save()``. --- ### Topics/Testing Testing ======= Testing Channels consumers is a little trickier than testing normal Django views due to their underlying asynchronous nature. To help with testing, Channels provides test helpers called *Communicators*, which allow you to wrap up an ASGI application (like a consumer) into its own event loop and ask it questions. `You can test asynchronous code `_ using Django's ``TestCase``. Alternately, you can use ``pytest`` with its `pytest-asyncio `_ plugin. Setting Up Async Tests ---------------------- To use Django's ``TestCase`` you simply define an ``async def`` test method in order to provide the appropriate async context:: from django.test import TestCase from channels.testing import HttpCommunicator from myproject.myapp.consumers import MyConsumer class MyTests(TestCase): async def test_my_consumer(self): communicator = HttpCommunicator(MyConsumer.as_asgi(), "GET", "/test/") response = await communicator.get_response() self.assertEqual(response["body"], b"test response") self.assertEqual(response["status"], 200) To use ``pytest`` you need to set it up with async test support, and presumably Django test support as well. You can do this by installing the ``pytest-django`` and ``pytest-asyncio`` packages: .. code-block:: sh python -m pip install -U pytest-django pytest-asyncio Then, you need to decorate the tests you want to run async with ``pytest.mark.asyncio``. Note that you can't mix this with ``unittest.TestCase`` subclasses; you have to write async tests as top-level test functions in the native ``pytest`` style: .. code-block:: python import pytest from channels.testing import HttpCommunicator from myproject.myapp.consumers import MyConsumer @pytest.mark.asyncio async def test_my_consumer(): communicator = HttpCommunicator(MyConsumer.as_asgi(), "GET", "/test/") response = await communicator.get_response() assert response["body"] == b"test response" assert response["status"] == 200 There's a few variants of the Communicator - a plain one for generic usage, and one each for HTTP and WebSockets specifically that have shortcut methods, ApplicationCommunicator ----------------------- ``ApplicationCommunicator`` is the generic test helper for any ASGI application. It provides several basic methods for interaction as explained below. You should only need this generic class for non-HTTP/WebSocket tests, though you might need to fall back to it if you are testing things like HTTP chunked responses or long-polling, which aren't supported in ``HttpCommunicator`` yet. .. note:: ``ApplicationCommunicator`` extends the class provided by the base ``asgiref`` package. Channels adds support for running unit tests with async consumers. To construct it, pass it an application and a scope: .. code-block:: python from channels.testing import ApplicationCommunicator communicator = ApplicationCommunicator(MyConsumer.as_asgi(), {"type": "http", ...}) send_input ~~~~~~~~~~ Call it to send an event to the application: .. code-block:: python await communicator.send_input({ "type": "http.request", "body": b"chunk one \x01 chunk two", }) receive_output ~~~~~~~~~~~~~~ Call it to receive an event from the application: .. code-block:: python event = await communicator.receive_output(timeout=1) assert event["type"] == "http.response.start" .. _application_communicator-receive_nothing: receive_nothing ~~~~~~~~~~~~~~~ Call it to check that there is no event waiting to be received from the application: .. code-block:: python assert await communicator.receive_nothing(timeout=0.1, interval=0.01) is False # Receive the rest of the http request from above event = await communicator.receive_output() assert event["type"] == "http.response.body" assert event.get("more_body") is True event = await communicator.receive_output() assert event["type"] == "http.response.body" assert event.get("more_body") is None # Check that there isn't another event assert await communicator.receive_nothing() is True # You could continue to send and receive events # await communicator.send_input(...) The method has two optional parameters: * ``timeout``: number of seconds to wait to ensure the queue is empty. Defaults to 0.1. * ``interval``: number of seconds to wait for another check for new events. Defaults to 0.01. wait ~~~~ Call it to wait for an application to exit (you'll need to either do this or wait for it to send you output before you can see what it did using mocks or inspection): .. code-block:: python await communicator.wait(timeout=1) If you're expecting your application to raise an exception, use ``pytest.raises`` around ``wait``: .. code-block:: python with pytest.raises(ValueError): await communicator.wait() HttpCommunicator ---------------- ``HttpCommunicator`` is a subclass of ``ApplicationCommunicator`` specifically tailored for HTTP requests. You need only instantiate it with your desired options: .. code-block:: python from channels.testing import HttpCommunicator communicator = HttpCommunicator(MyHttpConsumer.as_asgi(), "GET", "/test/") And then wait for its response: .. code-block:: python response = await communicator.get_response() assert response["body"] == b"test response" You can pass the following arguments to the constructor: * ``method``: HTTP method name (unicode string, required) * ``path``: HTTP path (unicode string, required) * ``body``: HTTP body (bytestring, optional) The response from the ``get_response`` method will be a dict with the following keys: * ``status``: HTTP status code (integer) * ``headers``: List of headers as (name, value) tuples (both bytestrings) * ``body``: HTTP response body (bytestring) WebsocketCommunicator --------------------- ``WebsocketCommunicator`` allows you to more easily test WebSocket consumers. It provides several convenience methods for interacting with a WebSocket application, as shown in this example: .. code-block:: python from channels.testing import WebsocketCommunicator communicator = WebsocketCommunicator(SimpleWebsocketApp.as_asgi(), "/testws/") connected, subprotocol = await communicator.connect() assert connected # Test sending text await communicator.send_to(text_data="hello") response = await communicator.receive_from() assert response == "hello" # Close await communicator.disconnect() .. note:: All of these methods are coroutines, which means you must ``await`` them. If you do not, your test will either time out (if you forgot to await a send) or try comparing things to a coroutine object (if you forgot to await a receive). .. important:: If you don't call ``WebsocketCommunicator.disconnect()`` before your test suite ends, you may find yourself getting ``RuntimeWarnings`` about things never being awaited, as you will be killing your app off in the middle of its lifecycle. You do not, however, have to ``disconnect()`` if your app already raised an error. You can also pass an ``application`` built with ``URLRouter`` instead of the plain consumer class. This lets you test applications that require positional or keyword arguments in the ``scope``: .. code-block:: python from channels.testing import WebsocketCommunicator application = URLRouter([ path("testws//", KwargsWebSocketApp.as_asgi()), ]) communicator = WebsocketCommunicator(application, "/testws/test/") connected, subprotocol = await communicator.connect() assert connected # Test on connection welcome message message = await communicator.receive_from() assert message == 'test' # Close await communicator.disconnect() .. note:: Since the ``WebsocketCommunicator`` class takes a URL in its constructor, a single Communicator can only test a single URL. If you want to test multiple different URLs, use multiple Communicators. connect ~~~~~~~ Triggers the connection phase of the WebSocket and waits for the application to either accept or deny the connection. Takes no parameters and returns either: * ``(True, )`` if the socket was accepted. ``chosen_subprotocol`` defaults to ``None``. * ``(False, )`` if the socket was rejected. ``close_code`` defaults to ``1000``. send_to ~~~~~~~ Sends a data frame to the application. Takes exactly one of ``bytes_data`` or ``text_data`` as parameters, and returns nothing: .. code-block:: python await communicator.send_to(bytes_data=b"hi\0") This method will type-check your parameters for you to ensure what you are sending really is text or bytes. send_json_to ~~~~~~~~~~~~ Sends a JSON payload to the application as a text frame. Call it with an object and it will JSON-encode it for you, and return nothing: .. code-block:: python await communicator.send_json_to({"hello": "world"}) receive_from ~~~~~~~~~~~~ Receives a frame from the application and gives you either ``bytes`` or ``text`` back depending on the frame type: .. code-block:: python response = await communicator.receive_from() Takes an optional ``timeout`` argument with a number of seconds to wait before timing out, which defaults to 1. It will typecheck your application's responses for you as well, to ensure that text frames contain text data, and binary frames contain binary data. receive_json_from ~~~~~~~~~~~~~~~~~ Receives a text frame from the application and decodes it for you: .. code-block:: python response = await communicator.receive_json_from() assert response == {"hello": "world"} Takes an optional ``timeout`` argument with a number of seconds to wait before timing out, which defaults to 1. receive_nothing ~~~~~~~~~~~~~~~ Checks that there is no frame waiting to be received from the application. For details see :ref:`ApplicationCommunicator `. disconnect ~~~~~~~~~~ Closes the socket from the client side. Takes nothing and returns nothing. You do not need to call this if the application instance you're testing already exited (for example, if it errored), but if you do call it, it will just silently return control to you. ChannelsLiveServerTestCase -------------------------- If you just want to run standard Selenium or other tests that require a webserver to be running for external programs, you can use ``ChannelsLiveServerTestCase``, which is a drop-in replacement for the standard Django ``LiveServerTestCase``: .. code-block:: python from channels.testing import ChannelsLiveServerTestCase class SomeLiveTests(ChannelsLiveServerTestCase): def test_live_stuff(self): call_external_testing_thing(self.live_server_url) .. note:: You can't use an in-memory database for your live tests. Therefore include a test database file name in your settings to tell Django to use a file database if you use SQLite: .. code-block:: python DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": os.path.join(BASE_DIR, "db.sqlite3"), "TEST": { "NAME": os.path.join(BASE_DIR, "db_test.sqlite3"), }, }, } serve_static ~~~~~~~~~~~~ Subclass ``ChannelsLiveServerTestCase`` with ``serve_static = True`` in order to serve static files (comparable to Django's ``StaticLiveServerTestCase``, you don't need to run collectstatic before or as a part of your tests setup). --- ### Topics/Troubleshooting Troubleshooting =============== ImproperlyConfigured exception ------------------------------ .. code-block:: text django.core.exceptions.ImproperlyConfigured: Requested setting INSTALLED_APPS, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings. This exception occurs when your application tries to import any models before Django finishes `its initialization process `_ aka ``django.setup()``. ``django.setup()`` `should be called only once `_, and should be called manually only in case of standalone apps. In context of Channels usage, ``django.setup()`` is called automatically in ``get_asgi_application()``, which means it needs to be called before any ORM models are imported. The working code order would look like this: .. include:: ../includes/asgi_example.rst --- ### Topics/Worker Worker and Background Tasks =========================== While :doc:`channel layers ` are primarily designed for communicating between different instances of ASGI applications, they can also be used to offload work to a set of worker servers listening on fixed channel names, as a simple, very-low-latency task queue. .. note:: The worker/background tasks system in Channels is simple and very fast, and achieves this by not having some features you may find useful, such as retries or return values. We recommend you use it for work that does not need guarantees around being complete (at-most-once delivery), and for work that needs more guarantees, look into a separate dedicated task queue. This feature does not work with the in-memory channel layer. Setting up background tasks works in two parts - sending the events, and then setting up the consumers to receive and process the events. Sending ------- To send an event, just send it to a fixed channel name. For example, let's say we want a background process that pre-caches thumbnails: .. code-block:: python # Inside a consumer self.channel_layer.send( "thumbnails-generate", { "type": "generate", "id": 123456789, }, ) Note that the event you send **must** have a ``type`` key, even if only one type of message is being sent over the channel, as it will turn into an event a consumer has to handle. Also remember that if you are sending the event from a synchronous environment, you have to use the ``asgiref.sync.async_to_sync`` wrapper as specified in :doc:`channel layers `. Receiving and Consumers ----------------------- Channels will present incoming worker tasks to you as events inside a scope with a ``type`` of ``channel``, and a ``channel`` key matching the channel name. We recommend you use ProtocolTypeRouter and ChannelNameRouter (see :doc:`/topics/routing` for more) to arrange your consumers: .. code-block:: python application = ProtocolTypeRouter({ ... "channel": ChannelNameRouter({ "thumbnails-generate": consumers.GenerateConsumer.as_asgi(), "thumbnails-delete": consumers.DeleteConsumer.as_asgi(), }), }) You'll be specifying the ``type`` values of the individual events yourself when you send them, so decide what your names are going to be and write consumers to match. For example, here's a basic consumer that expects to receive an event with ``type`` ``test.print``, and a ``text`` value containing the text to print: .. code-block:: python class PrintConsumer(SyncConsumer): def test_print(self, message): print("Test: " + message["text"]) Once you've hooked up the consumers, all you need to do is run a process that will handle them. In lieu of a protocol server - as there are no connections involved here - Channels instead provides you this with the ``runworker`` command: .. code-block:: text python manage.py runworker thumbnails-generate thumbnails-delete Note that ``runworker`` will only listen to the channels you pass it on the command line. If you do not include a channel, or forget to run the worker, your events will not be received and acted upon. --- ### Tutorial/Index Tutorial ======== Channels allows you to use WebSockets and other non-HTTP protocols in your Django site. For example you might want to use WebSockets to allow a page on your site to immediately receive updates from your Django server without using HTTP long-polling or other expensive techniques. In this tutorial we will build a simple chat server, where you can join an online room, post messages to the room, and have others in the same room see those messages immediately. .. toctree:: :maxdepth: 1 part_1 part_2 part_3 part_4 ---