Channels routes all WebSocket connections to a single set of consumers (the `websocket.*` consumers). This means that if you want multiple WebSocket URLs in a single app you need to manually parse the path. And, to make things more complicated, you only get the WebSocket path passed in the connection message, so you have to also use a channel session to keep track.
Yes, this seems like a major pain point, especially since the routing does not scale if you add another app, ie you need to add a wrapper which then dispatches to the individual connect routines. In a best case scenario I'd just have to add another router in the settings and get everything dispatched to the correct handlers (in that sense, the ROUTING setting should allow for multiple entries).
Maybe something along the lines of:
I'd considered an API like this, and it's certainly clean and straightforward. However, we've already got a URL routing library in Django, so I think I'd like to try to find a way to re-use it for websockets. It'd be a shame to have two different URL-routing-things in Django (especially since they'd almost certainly have subtlety different semantics).
1. Debugging errors:I found debugging errors that happen in a consumer to be *really* difficult -- errors mostly presented as things just silently not working. It took a ton of messing around with logging setups before I could get anything of use dumped to the console. This isn't strictly a Channels issue (I've noted similar problems with AJAX views and errors in Celery tasks), but I think Channels sorta brings the issue to a head.I think we need some better defaults, and simple, clear documentation, to make sure that exceptions go somewhere useful.
2. Static files:I had trouble getting static files served. I'm used to using Whitenoise (http://whitenoise.evans.io/en/stable/) for small-to-medium-ish sites that don't need a proper static server, but of course it doesn't work with Channels since Channels doesn't use WSGI! I found the (undocumented) StaticFilesConsumer (https://github.com/jacobian/channels-example/blob/master/chat/routing.py#L5-L8), but that feels less than ideal.I think this might be an opportunity, however. If Daphne learns how to serve static files (perhaps via optional integration with Whitenoise?), this would actually make static media in Django a bit easier by default.[I would be happy to work on this if I get a thumbsup.]
3. WebSocket routing:Channels routes all WebSocket connections to a single set of consumers (the `websocket.*` consumers). This means that if you want multiple WebSocket URLs in a single app you need to manually parse the path. And, to make things more complicated, you only get the WebSocket path passed in the connection message, so you have to also use a channel session to keep track.This feels like a distinct step back from the URL routing we already have in Django, and it was surprising to me to have to do this by hand. It definitely felt like Channels is missing some sort of WebSocket URL router.I had a brief chat with Andrew, who indicates that he'd planned for this to be a post-1.0 feature. I'm not sure I agree - it feels pretty fundamental - but I'd like to hear other thoughts.
Perhaps it is a bit early for this but Is there anywhere origin is checked against ALLOWED_HOSTS ?
Middleware support would be nice to but I guess you'll come to that when implementing user authentication.
Keep up the great work !
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/747fcf54-ff68-470c-ba0d-9e52b4cb85f6%40googlegroups.com.--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-developers.
On Mar 18, 2016, at 9:58 AM, Andrew Godwin <and...@aeracode.org> wrote:routing = [route("http.request", ViewConsumer),route("websocket.connect", path="^chat/(?P<room>[^/]+)/$", ChatConnect),route("sms.receive", sender="+44(?P<local_number>[0-9]+)$", UkSmsConsumer),include(path="^notifications", "notification.routing.routing"),]
--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-developers.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/68C930D0-1C2E-4CF4-B3B8-DA8450C468AB%40ryanhiebert.com.
Hey Andrew,
Thanks for looking through all that, and for the reply.
I like the simplicity of your updated examples. I started to make a counter-example to suggest that `include` be inside of a `route` (https://gist.github.com/orokusaki/c0c934013ee7911071ef).
But then, as I thought through this, I think I like your example almost* exactly like it is, but I'm afraid there might be a problem:
Given your example, any message how would a 'websocket.connect' message at the path `notifications/foo/` be routed, giving this example:routing = [route("websocket.connect", ChatConnect),include(path="^notifications", "notification.routing.routing"),]
Given that the chat route is the first match, wouldn't the notifications route never be used? Would path need to be required, so that the matching would be similar to `urlpatterns`? Otherwise, we're allowing routing based on channel name or path again? Maybe I'm misunderstanding.
--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-developers.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/9ce52a05-88d6-412f-9cfe-59c600c35df7%40googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/67476f8c-6cce-428d-93bd-562ffaabe2ff%40googlegroups.com.
--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-developers.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/ba29cc7a-90fc-4cf2-a65d-da782e83b3ca%40googlegroups.com.
On the static files question, I'm about to release v3 of WhiteNoise (http://whitenoise.evans.io/en/latest/changelog.html) which provides the option to integrate via Django middlware, rather than wsgi middleware. (It uses the FileResponse class, which didn't exist when I first wrote WhiteNoise.) I'm hoping (though I haven't tested it yet) that this will work out of the box with Channels, as it's only using standard Django APIs.
How does the new channels model handle requests with sensitive information? Say you have a login view and the user is submitting a username/password. Does the password get serialized into the message queue as plain text? If so is that a security concern users should be aware of?
The channel layer could indeed use SECRET_KEY, since it's loaded in via a Django codepath in both the protocol server and the worker server.
It's better done as a channel layer feature rather than in Channels/Daphne directly, as then it requires no extra supporting code in anything else that does ASGI (like the WSGI adapter). The layer could also have options to turn it on only for certain channels (e.g. only http.request) - it would be harder to make deeper encryption choices, though, as things like the body content and headers both come through as a single message key. Maybe let people provide a regex to match against e.g. http.request and websocket.connect's `path` key.
It's probably something we could pay for someone to work on for the "main" (Redis-backed) layer implementation? I could add it in too, but it's not as high priority as some other stuff I have to get to.
Andrew
--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-developers.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/3002448c-f36d-4f70-a2fd-c4d5e9bb2c34%40googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/CAFwN1urDr0LWvgK%2BRx9maEPezoFxqyFXCzNQbHedJwwvJM9pgw%40mail.gmail.com.
On Mar 22, 2016, at 1:24 PM, Jacob Kaplan-Moss <ja...@jacobian.org> wrote:I do think encrypting the Redis channel layer is something we should offer: Redis out of the box doesn't do transport-layer encryption, which is going to make Channels a hard sell to anyone with any for of regulatory/compliance requirements. [1]
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/CAK8PqJEMx6Mk4_O1cfYECxechKXMViNyLXXSoaK4eGRQjpEdxw%40mail.gmail.com.
Indeed, we run Redis over TLS tunnels at work to fulfill this requirement, so I know transport security is required, but at the same time reinventing it might be more work than we need - would you trust our internal symmetric encryption system, or go for TLS tunnels instead?
If not provided out of the box, there needs to be a supported way of wiring in encryption. The security/compliance person at my job stated that only securing the transport was not good enough for our compliance requirements when I was dealing with HIPAA (and some other compliance regulations) a few months ago.
Hi,
Finally found the time to go through this discussion.
The first note that comes to mind is:
Although it has already been pointed out more than once that positional
arguments cannot follow keyword arguments, you both (Andrew and Vincent) keep
giving examples such as
# SYNTAX ERROR
route('sms.receive', sender=r'\+44(?P<local_number>[0-9]+)$', Consumer)
I believe the reason for that is the clash between the will to preserve the
current `route(channel, consumer)` syntax, which is like the parallel
`url(path_regex, view, **kw)` syntax, on one hand; and the instinct that the
identifying parameters should be together, on the other hand.
I think that the latter consideration prevails; unlike the kw parameters in
url(), which are essentially parameters to the view, the kw parameters
proposed for channel help select the consumer; and so they must be adjacent to
the channel name -- and hence, the consumer must be the first argument.
A second thought is about multiple keyword arguments:
It isn't clear if more than one keyword argument is allowed, and if so, what
should be the semantics of the combination. When thinking about routing, it is
almost obvious that if there are many kw arguments, they must all match for
the whole route to match. But when thinking about includes, a different
consideration arises: How do I specify routing in an app which deals with
several incompatible channels?
For argument's sake, let's say I have an app which can handle SMS's, mails and
websocket.connect's. I want it to handle all of these if they come from within
my company -- so, based on partial phone number, sender domain and client IP
addresses, respectively. Would I need to have the app's routing.py module
include()'ed three times? Or would it make more sense to give include() kw
parameters an "or" semantics -- that is, match as soon as one argument
matches?
I think both of these solutions are bad, and the only solution that makes
sense is to allow a more complex structure of argument -- so that multiple kw
args are all required to match, and for a disjunction of channels we use
positional channel-spec args -- something like,
include('my_app.routing',
chan_spec('sms.receive', sender=r'\1212555(?:[0-9]{4})$'),
chan_spec('mail.receive', sender=r'^(?:\W+)@my_org.com$),
chan_spec('websocket.connect', remote_ip='^172.10'),
)
where a set of kw parameters are considered to all belong to one implicit
chan_spec.
Well, in that case I would consider defining the consumer as a required, but
keyword (and keyword-only in Python3) argument, specified in the end by
convention. Putting it between the channel and channel parameters is ugly IMO.