Websocket rails (Faye-websocket) 握手错误代码 200 - AWS ElasticBeanstalk、nginx、puma

Posted

技术标签:

【中文标题】Websocket rails (Faye-websocket) 握手错误代码 200 - AWS ElasticBeanstalk、nginx、puma【英文标题】:Websocket rails (Faye-websocket) handshake error code 200 - AWS ElasticBeanstalk, nginx, puma 【发布时间】:2016-09-20 14:16:44 【问题描述】:

我正在尝试让启用了 websockets 的应用程序正常工作,但在尝试连接时遇到了错误:

WebSocket connection to 'ws://[domain.com]/' failed: Error during WebSocket handshake: Unexpected response code: 200

我正在学习使用 gem faye-websocket 的教程 (https://devcenter.heroku.com/articles/ruby-websockets)。它在本地运行良好(使用 puma),但是当我尝试将其部署到运行 nginx + puma 的 AWS ElasticBeanstalk 时,websocket 请求无法连接。

我正在使用以下 javascript(使用我自己的域)创建一个新的 WebSocket 连接:

var ws = new WebSocket("ws://[domain.com]/");

按照教程,我创建了一个中间件类来检查传入的 websocket 请求,并相应地处理它。在我的本地服务器上,请求环境哈希如下所示:

"rack.errors"=>#>, "rack.multithread"=>true, "rack.multiprocess"=>false, "rack.run_once"=>false, "SCRIPT_NAME"=>"", "CONTENT_TYPE "=>"text/plain", "QUERY_STRING"=>"", "SERVER_PROTOCOL"=>"HTTP/1.1", "SERVER_SOFTWARE"=>"2.11.1", "GATEWAY_INTERFACE"=>"CGI/1.2", "REQUEST_METHOD"=>"GET", "REQUEST_PATH"=>"/", "REQUEST_URI"=>"/", "HTTP_VERSION"=>"HTTP/1.1", "HTTP_HOST"=>"localhost:3000", " HTTP_CONNECTION"=>"升级", "HTTP_PRAGMA"=>"无缓存", "HTTP_CACHE_CONTROL"=>"无缓存", "HTTP_UPGRADE"=>"websocket", "HTTP_ORIGIN"=>"http://localhost:3000", "HTTP_SEC_WEBSOCKET_VERSION"=>"13", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/50.0.2661.102 Safari/537.36", "HTTP_ACCEPT_ENCODING"= >"gzip, deflate, sdch", "HTTP_ACCEPT_LANGUAGE"=>"en-US,en;q=0.8",

而在 AWS 上它看起来像这样:

"rack.version"=>[1, 3], "rack.errors"=>#, "rack.multithread"=>true, "rack.multiprocess"=>false, "rack.run_once"= >false, "SCRIPT_NAME"=>"", "CONTENT_TYPE"=>"text/plain", "QUERY_STRING"=>"", "SERVER_PROTOCOL"=>"HTTP/1.1", "SERVER_SOFTWARE"=>"2.11.1 ", "GATEWAY_INTERFACE"=>"CGI/1.2", "REMOTE_ADDR"=>"127.0.0.1", "REQUEST_METHOD"=>"GET", "REQUEST_PATH"=>"/", "REQUEST_URI"=>"/" , "HTTP_VERSION"=>"HTTP/1.0", "HTTP_HOST"=>"[domain.com]", "HTTP_X_FORWARDED_FOR"=>"194.80.196.162", "HTTP_CONNECTION"=>"close", "HTTP_PRAGMA"=> "无缓存", "HTTP_CACHE_CONTROL"=>"无缓存", "HTTP_ORIGIN"=>"http://[domain.com]", "HTTP_SEC_WEBSOCKET_VERSION"=>"13", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36", "HTTP_ACCEPT_ENCODING"=>"gzip, deflate, sdch", "HTTP_ACCEPT_LANGUAGE"=>"en-US,en;q =0.8",

所以我可以看到,在 AWS 上,请求标头 HTTP_CONNECTION 和 HTTP_UPGRADE 没有被切换到 websockets。

但是,我尝试设置我的 nginx 配置,但我无法弄清楚它是如何工作的(例如 https://gist.github.com/KeithP/f8534c04d20c2b4e4b1d)

files:
  "/etc/nginx/conf.d/websockets.conf" :
    content: |
      upstream backend 
          server unix:///var/run/puma/my_app.sock;
      

      server 
          listen 80;

          access_log /var/log/nginx/access.log;
          error_log /var/log/nginx/error.log;

          server_name env1.t3tiiauce6.us-west-2.elasticbeanstalk.com

          # prevents 502 bad gateway error
          large_client_header_buffers 8 32k;

          location / 
              proxy_set_header X-Real-IP $remote_addr;
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
              proxy_set_header Host $http_host;
              proxy_set_header X-NginX-Proxy true;

              # prevents 502 bad gateway error
              proxy_buffers 8 32k;
              proxy_buffer_size 64k;

              proxy_pass http://backend;
              proxy_redirect off;

              location /assets 
                root /var/app/current/public;
              

              # enables WS support
              location /cable 
                proxy_pass http://backend;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
              
          
      

据我了解,这会将发往 /cable 的请求路由到后端 rails 应用程序,并升级到 websockets 的连接。但是,当我尝试 /cable 时,出现 404 错误(因为没有匹配的路由)。

那么我怎样才能让我的 rails 应用程序与 websockets 正确握手呢?任何帮助将非常感激!!我也尝试过使用 websocket-rails gem 并得到握手错误代码 500

【问题讨论】:

【参考方案1】:

终于成功了!最后,将以下文件放入 .ebextensions 文件夹中:

server 
  listen 80;
  server_name localhost; # need to listen to localhost for worker tier

  location / 
    proxy_pass http://my_app; # match the name of upstream directive which is defined in another file
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  

  location /websocket 
    proxy_pass http://my_app/websocket;
    proxy_set_header Host $host;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection Upgrade;
  

  location /assets 
    alias /var/app/current/public/assets;
    gzip_static on;
    gzip on;
    expires max;
    add_header Cache-Control public;
  

  location /public 
    alias /var/app/current/public;
    gzip_static on;
    gzip on;
    expires max;
    add_header Cache-Control public;
  

我的 Javascript 更改为:

var ws = new WebSocket("ws://[domain.com]/websocket");

希望对遇到类似问题的人有所帮助!

【讨论】:

以上是关于Websocket rails (Faye-websocket) 握手错误代码 200 - AWS ElasticBeanstalk、nginx、puma的主要内容,如果未能解决你的问题,请参考以下文章

Rails 和 Websocket

从 rails runner 内部触发/订阅 websocket-rails 事件

Rails 4 + Websocket-rails + Passenger + Nginx + 负载均衡器

websocket-rails 在生产中没有连接

用于 websocket-rails gem 的 Ruby websocket 客户端

Rails 5 - ActionCable - 无法升级到 WebSocket