diff --git a/channels/database_layer.py b/channels/database_layer.py index 72f1a58..9caef7e 100644 --- a/channels/database_layer.py +++ b/channels/database_layer.py @@ -74,10 +74,11 @@ class DatabaseChannelLayer(object): def new_channel(self, pattern): assert isinstance(pattern, six.text_type) + assert pattern.endswith("!") # Keep making channel names till one isn't present. while True: - random_string = "".join(random.choice(string.ascii_letters) for i in range(8)) - new_name = pattern.replace("?", random_string) + random_string = "".join(random.choice(string.ascii_letters) for i in range(10)) + new_name = pattern + random_string if not self.channel_model.objects.filter(channel=new_name).exists(): return new_name diff --git a/docs/asgi.rst b/docs/asgi.rst index 9590dbb..04ddcfe 100644 --- a/docs/asgi.rst +++ b/docs/asgi.rst @@ -112,14 +112,17 @@ In order to aid with scaling and network architecture, a distinction is made between channels that have multiple readers (such as the ``http.request`` channel that web applications would listen on from every application worker process) and *single-reader channels* -(such as a ``http.response.ABCDEF`` channel tied to a client socket). +(such as a ``http.response!ABCDEF`` channel tied to a client socket). -*Single-reader channel* names are prefixed with an exclamation mark +*Single-reader channel* names contain an exclamation mark (``!``) character in order to indicate to the channel layer that it may have to route the data for these channels differently to ensure it reaches the single process that needs it; these channels are nearly always tied to -incoming connections from the outside world. Some channel layers may not -need this, and can simply treat the prefix as part of the name. +incoming connections from the outside world. The ``!`` is always preceded by +the main channel name (e.g. ``http.response``) and followed by the +per-client/random portion - channel layers can split on the ``!`` and use just +the right hand part to route if they desire, or can ignore it if they don't +need to use different routing rules. Messages should expire after a set time sitting unread in a channel; the recommendation is one minute, though the best value depends on the @@ -156,7 +159,7 @@ standard keys in the ``environ`` dict for WSGI. The design pattern is that most protocols will share a few channels for incoming data (for example, ``http.request``, ``websocket.connect`` and ``websocket.receive``), but will have individual channels for sending to -each client (such as ``!http.response.kj2daj23``). This allows incoming +each client (such as ``http.response!kj2daj23``). This allows incoming data to be dispatched into a cluster of application servers that can all handle it, while responses are routed to the individual protocol server that has the other end of the client's socket. @@ -275,11 +278,13 @@ A *channel layer* must provide an object with these attributes * ``new_channel(pattern)``, a callable that takes a unicode string pattern, and returns a new valid channel name that does not already exist, by - substituting any occurrences of the question mark character ``?`` in - ``pattern`` with a single random unicode string and checking for - existence of that name in the channel layer. This is NOT called prior to + adding a single random unicode string after the ``!`` character in ``pattern``, + and checking for existence of that name in the channel layer. The ``pattern`` + MUST end with ``!`` or this function must error. This is NOT called prior to a message being sent on a channel, and should not be used for channel - initialization. + initialization, and is also not guaranteed to be called by the same channel + client that then reads the messages, so you cannot put process identifiers in + it for routing. * ``MessageTooLarge``, the exception raised when a send operation fails because the encoded message is over the layer's size limit. @@ -391,7 +396,7 @@ top-level outgoing channel. Messages are specified here along with the channel names they are expected on; if a channel name can vary, such as with reply channels, the varying -portion will be replaced by ``?``, such as ``http.response.?``, which matches +portion will be represented by ``!``, such as ``http.response!``, which matches the format the ``new_channel`` callable takes. There is no label on message types to say what they are; their type is implicit @@ -435,8 +440,8 @@ Channel: ``http.request`` Keys: -* ``reply_channel``: Channel name for responses and server pushes, in - format ``http.response.?`` +* ``reply_channel``: Channel name for responses and server pushes, starting with + ``http.response!`` * ``http_version``: Unicode string, one of ``1.0``, ``1.1`` or ``2``. @@ -485,7 +490,7 @@ Request Body Chunk Must be sent after an initial Response. -Channel: ``http.request.body.?`` +Channel: ``http.request.body!`` Keys: @@ -503,7 +508,7 @@ Response Send after any server pushes, and before any response chunks. -Channel: ``http.response.?`` +Channel: ``http.response!`` Keys: @@ -528,7 +533,7 @@ Response Chunk Must be sent after an initial Response. -Channel: ``http.response.?`` +Channel: ``http.response!`` Keys: @@ -551,7 +556,7 @@ When a server receives this message, it must treat the Request message in the received from the network. A server may, if it chooses, apply all of its internal logic to handling this request (e.g. the server may want to try to satisfy the request from a cache). Regardless, if the server is unable to -satisfy the request itself it must create a new ``http.response.?`` channel for +satisfy the request itself it must create a new ``http.response!`` channel for the application to send the Response message on, fill that channel in on the ``reply_channel`` field of the message, and then send the Request back to the application on the ``http.request`` channel. @@ -565,7 +570,7 @@ If the remote peer does not support server push, either because it's not a HTTP/2 peer or because SETTINGS_ENABLE_PUSH is set to 0, the server must do nothing in response to this message. -Channel: ``http.response.?`` +Channel: ``http.response!`` Keys: @@ -611,8 +616,7 @@ Channel: ``websocket.connect`` Keys: -* ``reply_channel``: Channel name for sending data, in - format ``websocket.send.?`` +* ``reply_channel``: Channel name for sending data, start with ``websocket.send!`` * ``scheme``: Unicode string URL scheme portion (likely ``ws`` or ``wss``). Optional (but must not be empty), default is ``ws``. @@ -651,8 +655,7 @@ Channel: ``websocket.receive`` Keys: -* ``reply_channel``: Channel name for sending data, in - format ``websocket.send.?`` +* ``reply_channel``: Channel name for sending data, starting with ``websocket.send!`` * ``path``: Path sent during ``connect``, sent to make routing easier for apps. @@ -677,8 +680,8 @@ Channel: ``websocket.disconnect`` Keys: -* ``reply_channel``: Channel name that was used for sending data, in - format ``websocket.send.?``. Cannot be used to send at this point; provided +* ``reply_channel``: Channel name that was used for sending data, starting + with ``websocket.send!``. Cannot be used to send at this point; provided as a way to identify the connection only. * ``path``: Path sent during ``connect``, sent to make routing easier for apps. @@ -693,7 +696,7 @@ Send/Close Sends a data frame to the client and/or closes the connection from the server end. -Channel: ``websocket.send.?`` +Channel: ``websocket.send!`` Keys: @@ -732,7 +735,7 @@ Channel: ``udp.receive`` Keys: -* ``reply_channel``: Channel name for sending data, in format ``udp.send.?`` +* ``reply_channel``: Channel name for sending data, starts with ``udp.send!`` * ``data``: Byte string of UDP datagram payload. @@ -750,7 +753,7 @@ Send Sent to send out a UDP datagram to a client. -Channel: ``udp.send.?`` +Channel: ``udp.send!`` Keys: @@ -834,7 +837,8 @@ limitation that they only use the following characters: * Hyphen ``-`` * Underscore ``_`` * Period ``.`` -* Exclamation mark ``!`` (only at the start of a channel name) +* Exclamation mark ``!`` (only to deliniate single-reader channel names, + and only one per name) WSGI Compatibility diff --git a/docs/concepts.rst b/docs/concepts.rst index 1accddd..2cc7652 100644 --- a/docs/concepts.rst +++ b/docs/concepts.rst @@ -155,9 +155,9 @@ the message - but response channels would have to have their messages sent to the channel server they're listening on. For this reason, Channels treats these as two different *channel types*, and -denotes a *response channel* by having the first character of the channel name -be the character ``!`` - e.g. ``!http.response.f5G3fE21f``. *Normal -channels* have no special prefix, but along with the rest of the response +denotes a *response channel* by having the channel name contain +the character ``!`` - e.g. ``http.response!f5G3fE21f``. *Normal +channels* have do not contain it, but along with the rest of the response channel name, they must contain only the characters ``a-z A-Z 0-9 - _``, and be less than 200 characters long. @@ -244,7 +244,7 @@ Of course, you should still remove things from the group on disconnect if you can; the expiry code is there to catch cases where the disconnect message doesn't make it for some reason. -Groups are generally only useful for response channels (ones starting with +Groups are generally only useful for response channels (ones containing the character ``!``), as these are unique-per-client, but can be used for normal channels as well if you wish.