Hi all,

I'm trying use Channels to add websockets functionality to an existing 
Django project.

At this stage, my websockets functionality is a simple echo of whatever 
text is sent by the client.

The  existing project runs on http://domain.com/, served by Apache+mod_wsgi 
behind a Nginx front-end proxy.

I'm trying to route websockets to http://domain.com/ws/, served by daphne 
behind the same Nginx front-end proxy.

The Nginx config looks like this:

  # daphne
  location /ws/ {
      proxy_pass  http://localhost:22222;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
  }

  # apache+mod_wsgi
  location / {
      proxy_pass  http://localhost:11111;
  }

When I do this, I can connect to the websocket on http://domain.com/ws/, 
but nothing gets echoed back.

Here's what I see from daphne in the console:

  2017-09-22 16:52:44,654 DEBUG    WebSocket 
daphne.response.npTbOappnj!BnXbgObcDS open and established
  127.0.0.1:43270 - - [22/Sep/2017:16:52:44] "WSCONNECT /" - -
  2017-09-22 16:52:44,654 DEBUG    WebSocket 
daphne.response.npTbOappnj!BnXbgObcDS accepted by application
  2017-09-22 16:52:52,569 DEBUG    WebSocket incoming frame on 
daphne.response.npTbOappnj!BnXbgObcDS
  2017-09-22 16:52:54,539 DEBUG    WebSocket incoming frame on 
daphne.response.npTbOappnj!BnXbgObcDS
  2017-09-22 16:52:55,229 DEBUG    WebSocket incoming frame on 
daphne.response.npTbOappnj!BnXbgObcDS
  2017-09-22 16:52:55,703 DEBUG    WebSocket incoming frame on 
daphne.response.npTbOappnj!BnXbgObcDS

The "WebSocket incoming frame on..." entries were logged each time my WS 
client sent a message.

I thought "WSCONNECT /" looked suspicious, since I'm trying to connect to 
"/ws", so I tried connecting to http://domain.com/ws/ws/ instead.

Sure enough, the echo worked when connected to http://domain.com/ws/ws/, 
and requests were logged as /ws.

So I think the problem seems to be that daphne isn't aware that it's 
running on a sub-path /ws.

I tried using the root-path parameter documented here 
https://github.com/django/daphne#root-path-script_name but that had no 
effect. The connections were still logged as / instead of /ws, and nothing 
gets echoed back.

As a test, I reconfigured Nginx to serve the whole site from daphne, eg:

  location / {
      proxy_pass  http://localhost:22222;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
  }

The app worked perfectly in that configuration - but I really need this to 
work alongside mod_wsgi.

The docs at 
https://channels.readthedocs.io/en/stable/deploying.html#running-asgi-alongside-wsgi
 
specifically state that this is possible:

To do this, just set up your Daphne to serve as we discussed above, and 
> then configure your load-balancer or front HTTP server process to dispatch 
> requests to the correct server - based on either path, domain, or if you 
> can, the Upgrade header. 

 

Dispatching based on path or domain means you’ll need to design your 
> WebSocket URLs carefully so you can always tell how to route them at the 
> load-balancer level; the ideal thing is to be able to look for the Upgrade: 
> WebSocket header and distinguish connections by this, but not all software 
> supports this and it doesn’t help route long-poll HTTP connections at all.


I suspect what's tripping me up is the "dispatching based on path or domain 
means you’ll need to design your WebSocket URLs carefully" bit, but so far 
I can't see what I'm doing wrong.

My project is structured like this:

  myproject
  ├── db.sqlite3
  ├── manage.py
  ├── myapp
  │   ├── admin.py
  │   ├── apps.py
  │   ├── consumers.py
  │   ├── __init__.py
  │   ├── migrations
  │   │   ├── __init__.py
  │   ├── models.py
  │   ├── routing.py
  │   ├── tests.py
  │   └── views.py
  └── myproject
      ├── asgi.py
      ├── __init__.py
      ├── routing.py
      ├── settings.py
      ├── urls.py
      └── wsgi.py

myproject is a default Django project, and myapp is a default Django app, 
both with the minimum extra bits required for Channels.

myproject/asgi.py is:

  import os
  from channels.asgi import get_channel_layer
  os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
  channel_layer = get_channel_layer()


myproject/settings.py CHANNEL_LAYERS has this:

  "ROUTING": "myproject.routing.channel_routing",


myproject/routing.py has this:

  channel_routing = [
      # Include sub-routing from an app.
      include("myapp.routing.channel_routing", path=r"^/ws"),
  ]


myapp/routing.py has this:

  channel_routing = [
      route("websocket.receive", ws_message),
  ]

myapp/consumers.py:

  def ws_message(message):
      # ASGI WebSocket packet-received and send-packet message types
      # both have a "text" key for their textual data.
      message.reply_channel.send({
          "text": 'You said %s' % (message.content['text'],),
      })


Like I said, this all works great when I serve the entire project from 
Daphne, but I want mod_wsgi to serve HTTP stuff, as the docs suggest is 
possible.

What am I doing wrong here?

Thanks for reading this far,

Sean

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-users+unsubscr...@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-users/53072bc0-9d1c-4d0d-b1ee-c4b043630660%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to