Action cable unable to connect (Failed to upgrade to WebSocket )

2016-07-06 ruby-on-rails nginx ruby-on-rails-5 actioncable

I am having a issue in connecting to websocket in non-development environments with these log messages

Failed to upgrade to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: close, HTTP_UPGRADE: )

Finished "/cable/"[non-WebSocket] for 127.0.0.1 at 2016-07-06 09:44:29 +1000

I debuggged a little and figured out the request sent by the browser/javascript is not exactly the same as the request received by unicorn(running with nginx).

The request header by the browser is

GET ws://cc-uat.com.au/cable HTTP/1.1
Host: cc-uat.com.au
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://cc-uat.com.au
Sec-WebSocket-Version: 13
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6
Cookie: <Lot of cookies>
Sec-WebSocket-Key: QGdJkYIA2u7vtmMVXfHKtQ==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Protocol: actioncable-v1-json, actioncable-unsupported

The connection here is 'upgrade' but the websocket request has the connection 'closed'(probably nginx is messing it up?)

And this piece of code in websocket driver is failing

def self.websocket?(env)
      connection = env['HTTP_CONNECTION'] || ''
      upgrade    = env['HTTP_UPGRADE']    || ''

      env['REQUEST_METHOD'] == 'GET' and
      connection.downcase.split(/ *, */).include?('upgrade') and
      upgrade.downcase == 'websocket'
end

Updates

This is my nginx configuration

upstream app {
    server unix:/home/osboxes/sites/actioncable-examples/shared/sockets/unicorn.sock fail_timeout=0;
}

server {
    listen 80;
    server_name localhost;

    root /home/osboxes/sites/actioncable-example/public;

    try_files $uri/index.html $uri @app;

    location @app {
        proxy_pass http://app;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_http_version 1.1;
     proxy_set_header Upgrade $http_upgrade;
     proxy_set_header Connection "upgrade";
    }


    error_page 500 502 503 504 /500.html;
    client_max_body_size 4G;
    keepalive_timeout 10;
}

I have mounted actioncable server on /cable

mount ActionCable.server => "/cable"

With the nginx changes i am able to successfully have the handshake but the server is not able to send the heart beats and the connection keeps dropping.

Started GET "/cable" for 127.0.0.1 at 2016-07-07 05:48:06 +0100
Started GET "/cable/" [WebSocket] for 127.0.0.1 at 2016-07-07 05:48:06 +0100
Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: upgrade, HTTP_UPGRADE: websocket)

Answers

Have you set the config.action_cable.allowed_request_origins in production.rb to allow connections from your production domain? In my nginx.conf there is also

proxy_set_header X-Real-IP $remote_addr;  
proxy_set_header X-Forwarded-Proto http;

I am not entirely sure if they are really required, but it works for me.

Rails 5 Action Cable CORS:

create a ruby file i.e action_cable.rb in my_rails_project/config/initializers and add the following code.

if Rails.env.development?
  Rails.application.config.action_cable.allowed_request_origins =  ['http://localhost:3001', 'http://127.0.0.1:3001']
end

you are done.

Thanks to above answer i was getting the similar error. Issue was that i kept backend url address in config.action_cable.allowed_request_origins on changing it to frontend url issue got resolved. To be more clear i am having font-end,backend in entirely different domains

Related