Make WebSocket work with Ruby on Rails vs Puma and Nginx - ruby-on-rails

There is a staging environment which is setup using Ubunut, Nginx, Puma and Ruby on Rails application. It is working fine. I'm trying to create same separate environment and can't setup WebSockets. I need some help with figuring out what is wrong, why WebSocket connection fails. Currently it is showing following error:
WebSocket connection to 'wss://upgrade.mysite.com/cable' failed: application-2c9281fcfd42a4b226b2bec3c0a6f9aaca5f7295cefd1099d252d3689e9e19d0.js:49276
The Nginx server is configured for basic authentication and SSL
Following is the WebSocket configuration in the sites-available/mysite:
upstream app {
server unix:/home/myuser/mysite/current/tmp/sockets/puma.sock fail_timeout=0;
}
server {
...
location #app {
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://app;
}
location /cable {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Upgrade websocket;
proxy_set_header Connection Upgrade;
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;
}
...
}
I'm not sure whether any other setup is need or how to debug WebSocket connection. Any help is appreciated. Even suggestion how to debug it would matter for me.
The ruby on rails site works fine, only WebSocket fails to connect. Deployments are done using Capistrano.

The problem was in the config/application.rb file. I've set correctly the following config option: config.action_cable.url = https://test.mysite.com however didn't set the config.action_cable.allowed_request_origins
Correct config/application.rb file should look like this:
require 'active_model/railtie'
require 'active_record/railtie'
require 'action_controller/railtie'
require 'action_mailer/railtie'
require 'action_view/railtie'
require 'action_cable/engine'
require 'sprockets/railtie'
Bundler.require(*Rails.groups)
module MyModule
class Application < Rails::Application
...
config.action_cable.url = https://test.mysite.com
config.action_cable.allowed_request_origins = [/https:\/\/test.mysite.com/]
...
end
end

Related

Configure Litespeed to serve as a frontend proxy for chatwoot or any docker

My docker chatwoot is now fully activated, but I don't know how to convert my litespeed(As part of my Cpanel installation, I installed Litespeed) to a frontend proxy like the setting below belongs to Nginx.
server {
server_name <yourdomain.com>;
# Point upstream to Chatwoot App Server
set $upstream 127.0.0.1:3000;
# Nginx strips out underscore in headers by default
# Chatwoot relies on underscore in headers for API
# Make sure that the config is set to on.
underscores_in_headers on;
location /.well-known {
alias /var/www/ssl-proof/chatwoot/.well-known;
}
location / {
proxy_pass_header Authorization;
proxy_pass http://$upstream;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Ssl on; # Optional
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Connection “”;
proxy_buffering off;
client_max_body_size 0;
proxy_read_timeout 36000s;
proxy_redirect off;
}
listen 80;
}
Would anyone be able to help me?
Testing led me to the answer.
A .htaccess file with this content can be created in the public_html of that domain or subdomain if someone has this problem.
# php -- BEGIN cPanel-generated handler, do not edit
# Set the “ea-php81” package as the default “PHP” programming language.
<IfModule mime_module>
AddHandler application/x-httpd-ea-php81 .php .php8 .phtml
</IfModule>
# php -- END cPanel-generated handler, do not edit
#ProxyPreserveHost On
#ProxyPassMatch "/(.*)$" "http://127.0.0.1:3000/$1"
RewriteEngine On
RewriteRule ^(.*)$ http://127.0.0.1:3000/$1 [P,L]

Websocket connection failed with nginx and faye

I'm trying to introduce chatting to my Rails app. For this purpose I used gem private_pub and it works perfectly in development mode.
In production I was using Apache + Passenger, but I couldn't configure Faye with it, so I changed Apache to Nginx. My main app is still on Apache server, and this demo on Nginx with port 8080 (just for test).
I'm able to connect to faye.js by entering http://chat.mysite.com:8080/faye.js, but the connection from app throws an error (browser console).
WebSocket connection to 'ws://localhost:9292/faye' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED
After this error, another error appears every 5 seconds.
faye.js:2 GET http://localhost:9292/faye?message=%5B%7B%22channel%22%3A%22%2Fmeta%2Fhands…22%2C%22callback-polling%22%5D%2C%22id%22%3A%221%22%7D%5D&jsonp=jsonp2 net::ERR_CONNECTION_REFUSED
My private_pub.yml
production:
server: "http://localhost:9292/faye"
secret_token: "mysecret"
signature_expiration: 3600 # one hour
My private_pub.ru
require "bundler/setup"
require "yaml"
require "faye"
require "private_pub"
Faye::WebSocket.load_adapter('thin')
PrivatePub.load_config(File.expand_path("../config/private_pub.yml", __FILE__), ENV["RAILS_ENV"] || "development")
run PrivatePub.faye_app
My nginx site.conf
server {
listen 8080;
server_name www.chat.mysite.com;
passenger_enabled on;
passenger_app_env production;
root /var/www/mysite/public;
location ^~ /faye {
proxy_pass http://127.0.0.1:9292;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_buffering off;
proxy_redirect off;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
#proxy_set_header X-Forwarded-Proto https;
proxy_cache_bypass $http_pragma $http_authorization;
proxy_no_cache $http_pragma $http_authorization;
break;
}
}
If I change private_pub.yml to http://localhost:9292/faye/faye, I saw error like "can't load resource /faye/faye.js".
How should I change my Nginx conf or app yml to resolve websocket error?
I see private_pub is quite similar to ActionCable in its design. Before you go too far down the road, you may want read my blog post on "ActionAcable - The good and bad parts" as it addresses suitable use cases when a system like private_pub is good, and when it's not.
If of course you are aware of the shortcomings already, then good luck!
I tried to configure my private_pub.yml as #niceman said. Now all is working good.
production:
server: "http://my-ip:8080/faye"

setting Rails environment hash variables for Rack middleware

I have a Rails app being served on standalone Passenger behind an nginx proxy. Because of this setup, the environment hash needs a bit of tweaking to work with a piece of rack middleware we're using (rack-cas). Specifically I have to set env['SERVER_PORT'] = '443' and env['HTTPS'] = 'on' in the middleware's call method (we don't want Passenger using SSL, would rather the nginx proxy handle it).
I can do all that easy enough in the middleware but can I do it in the Rails app so I don't have to customize the middleware?
Turns out it can all be done from the nginx proxy's config, don't have to touch the app or middleware at all:
location /foo/ {
proxy_pass http://0.0.0.0:SOME_PORT/foo/;
proxy_buffering off;
proxy_http_version 1.1;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host; # more robust than http_host
proxy_set_header Upgrade $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # this ensures your app's env is correct
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto https; # add this if you always want the redirects to go to HTTPS
}

Nginx, Faye, Private...trying to figure out configuration

Using Nginx 1.6 with private_pub gem
Here are my config files:
private_pub.ru
# Run with: rackup private_pub.ru -s thin -E production
require "bundler/setup"
require "yaml"
require "faye"
require "private_pub"
Faye::WebSocket.load_adapter('thin')
PrivatePub.load_config(File.expand_path("../config/private_pub.yml", __FILE__), ENV["RAILS_ENV"] || "development")
run PrivatePub.faye_app
Private_pub.yml
development:
server: "http://localhost:9292/faye/faye"
secret_token: "secret"
test:
server: "http://localhost:9292/faye/faye"
secret_token: "secret"
production:
server: "http://xxxxx.com/faye/faye"
secret_token: "my secret token"
signature_expiration: 3600 # one hour
in my Nginx.conf
location /faye {
proxy_pass http://0.0.0.0:9292;
break;
}
The service is running but really really slow and I get those errors on safari:
WebSocket connection to 'ws://xxxxx.com/faye' failed: Unexpected response code: 400
Failed to load resource: the server responded with a status of 404 (Not Found)
Failed to load resource: the server responded with a status of 502 (Bad Gateway)
Any thoughts?
OK.. I found the solution for those who ever want to install Faye/Private_pub on Nginx running thin and unicorn.
First:
You have to understand that your upstream server is your localhost:9292 (127.0.0.1:9292)
you set your upstream in your Nginx conf by adding the following:
location /faye {
proxy_pass http://127.0.0.1:9292;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_buffering off;
proxy_redirect off;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_set_header X-Forwarded-Proto https;
break;
}
Also for those who have a 504 after that change the config file in the Nginx and php.fmp (if you have it) so that the timeout is increased.
Don't forget to reload your Nginx. If you still have errors check your Nginx error.log

https redirect for rails app behind proxy?

server declaration in my nginx.conf:
listen 1.2.3.4:443 ssl;
root /var/www/myapp/current/public;
ssl on;
ssl_certificate /etc/nginx-cert/server.crt;
ssl_certificate_key /etc/nginx-cert/server.key;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
if (!-f $request_filename) {
proxy_pass http://upstreamy;
break;
}
}
upstream declaration in nginx.conf:
upstream upstreamy {
server unix:/var/www//myapp/shared/sockets/unicorn.sock fail_timeout=0;
}
this works fine, myapp is reachable as https://somehost
but the app is generating http url's for redirects, so for instance when authenticating with devise, the / is redirected to http://somehost/user/sign_in instead of https (from the viewpoint of the rails app, it's all http anyway).
I tried
proxy_pass https://upstreamy;
but that just tries to encrypt traffic between nginx and the unicorns that run the rails app.
I also tried, in application_helper.rb:
# http://stackoverflow.com/questions/1662262/rails-redirect-with-https
def url_options
super
#_url_options.dup.tap do |options|
options[:protocol] = Rails.env.production? ? "https://" : "http://"
options.freeze
end
but it seems to not work.
How would one solve this?
Edit: so, the goal is not to make the rails app to require ssl, or to be forced to use ssl; the goal is to make the rails app generate https:// urls when redirecting... (I think all other urls are relative).
You need to add the following line:
proxy_set_header X-Forwarded-Proto https;
as in
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto https;
proxy_redirect off;
if (!-f $request_filename) {
proxy_pass http://upstreamy;
break;
}
}
You can pass :protocol => "https", to redirect_to.
You can set this as a default by adding the following to application.rb
Rails.application.routes.default_url_options[:protocol]= 'https'
Reference: https://stackoverflow.com/a/6101147/446203

Resources