WebSockets with nginx and puma (websocket-rails) - ruby-on-rails

Using the websocket-rails gem, I'm able to successfully get a websocket connection straight through puma in development, however, when deployed to production and attempting to access the websocket through nginx (passing off to puma) I have a couple of errors: one in the nginx error log:
[info] 14340#0: *7 upstream timed out (110: Connection
timed out) while proxying upgraded connection, client: 123.45.67.89, server:
foo.com, request: "GET /websocket HTTP/1.1", upstream:
"http://unix:///opt/oneconnect/shared/tmp/sockets/puma.sock:/websocket", host:
"foo.com"
... and one on the javascript console:
WebSocket connection to 'ws://foo.com/websocket' failed: Error during WebSocket handshake: Unexpected response code: 301
I found that nginx (the version I'm using is 1.4.6) is capable of websocket use but requires special configuration, which I've already attemped (getting the errors above). Here's my nginx.conf:
upstream oneconnect {
server unix:///opt/oneconnect/shared/tmp/sockets/puma.sock;
}
server {
listen 80;
listen 443 ssl;
#ssl on;
ssl_certificate /etc/ssl/foo.com.crt;
ssl_certificate_key /etc/ssl/foo.com.key;
root /opt/oneconnect/current/public;
try_files $uri #oneconnect;
access_log /opt/oneconnect/current/log/nginx.access.log;
error_log /opt/oneconnect/current/log/nginx.error.log info;
server_name foo.com;
location ~ ^/(assets)/ {
root /opt/oneconnect/current/public;
gzip_static on;
expires max;
add_header Cache-Control public;
}
location /websocket/ {
proxy_pass http://oneconnect;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location #oneconnect {
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_redirect off;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://oneconnect;
}
}
I'm assuming that I'm missing something simple, but I'm stumped at this point and have Googled until my eyes started bleeding. If anyone could help it would be much appreciated, or maybe just point me to how to debug these connections (it seems hard to get debug info from a ws connection). Thanks for your time.

Assuming u have already initializer for eventmachine
config/initializers/eventmachine.rb
Thread.new { EventMachine.run } unless EventMachine.reactor_running? && EventMachine.reactor_thread.alive?
nginx site conf:
upstream puma_project_production {
server unix:/var/www/project/shared/tmp/sockets/puma.sock fail_timeout=0;
}
server {
listen 80;
client_max_body_size 4G;
keepalive_timeout 10;
error_page 500 502 504 /500.html;
error_page 503 #503;
server_name localhost project.local;
root /var/www/project/current/public;
try_files $uri/index.html $uri #puma_project_production;
location #puma_project_production {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://puma_project_production;
# limit_req zone=one;
access_log /var/www/project/shared/log/nginx.access.log;
error_log /var/www/project/shared/log/nginx.error.log;
}
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
location = /50x.html {
root html;
}
location = /404.html {
root html;
}
location #503 {
error_page 405 = /system/maintenance.html;
if (-f $document_root/system/maintenance.html) {
rewrite ^(.*)$ /system/maintenance.html break;
}
rewrite ^(.*)$ /503.html break;
}
if ($request_method !~ ^(GET|HEAD|PUT|PATCH|POST|DELETE|OPTIONS)$ ){
return 405;
}
if (-f $document_root/system/maintenance.html) {
return 503;
}
location /websocket {
proxy_pass http://puma_project_production;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location ~ \.(php|rb)$ {
return 405;
}
}

Related

Set multiple roots for a rails app + unicorn + nginx

I need a help to set some roots in a same server when use unicorn+nginx with rails apps.
My app works when I set only a root.
upstream contab_teste {
server unix:/home/ubuntu/apps/contab/shared/sockets/unicorn.sock fail_timeout=0;
}
server {
listen 80;
server_name default_server;
rails_env production;
try_files $uri $uri/index.html #app;
root /home/ubuntu/apps/contab/public;
location #app {
proxy_pass http://contab_teste;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
}
When I set another root directory its not works.
I tried in some ways to do like this:
upstream contab_teste {
server unix:/home/ubuntu/apps/contab/shared/sockets/unicorn.sock fail_timeout=0;
}
upstream contab_apresentacao {
server unix:/home/ubuntu/apps/contab_apresentacao/shared/sockets/unicorn.sock fail_timeout=0;
}
server {
listen 80;
server_name default_server;
rails_env production;
try_files $uri $uri/index.html #app;
root /home/ubuntu/apps/contab/public;
location #app {
proxy_pass http://contab_teste;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
}
location /apresentacao {
#rewrite ^/apresentacao(.*) /$1 break;
root /home/ubuntu/apps/contab_apresentacao/public;
proxy_pass http://contab_apresentacao;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
}
Someone can help me please?
thanks!

Actioncable Nginx and Puma WebSocket handshake: Unexpected response

I am trying to configure the server with rails 5, Nginx and Puma. The application is running fine but Actioncable is giving
WebSocket connection to 'ws://server_name.com/cable' failed:
Error during WebSocket handshake: Unexpected response code: 200
Below are my nginx settings,
upstream app {
server unix:/tmp/app.sock fail_timeout=0;
}
server {
listen 80;
server_name server_name.com;
try_files $uri/index.html $uri #app;
client_max_body_size 100M;
location #app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app;
client_max_body_size 10M;
}
location /cable {
proxy_pass http://app/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
location ~ ^/(assets|uploads)/ {
root assets_path;
gzip_static on;
expires max;
add_header Cache-Control public;
add_header ETag "";
break;
}
error_page 500 502 503 504 /500.html;
}
In rails in production.rb, I did the settings like below.
config.action_cable.url = 'ws://server_name.com/cable'
Any help will be appreciated.
try to add:
config.action_cable.allowed_request_origins = ['*']
config.action_cable.disable_request_forgery_protection = true
to your config/environments/production.rb file
it works with me, you can check this link also.
and this question
Try using,
location /cable {
proxy_pass http://app; # not http://app/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
And also make sure that you have config.action_cable.allowed_request_origins = [/http:\/\/*/, /https:\/\/*/] in your production.rb if you don't have ssl

POST request it being turned into GET request when using http instead of https

After adding SSL I have noticed a strange behaviour: All API POST requests using HTTP protocol in the address are being turned into GET requests and this is why I am getting ActionController::RoutingError (No route matches [GET] "/api/v2/users") error.
My nginx.conf:
upstream unicorn_staging.xxxxx.com {
server unix:/srv/www/xxxxx/shared/sockets/unicorn.sock fail_timeout=0;
}
server {
listen 80;
server_name staging.xxxxx.com xxxxx dublin;
access_log /var/log/nginx/staging.xxxxx.com.access.log;
keepalive_timeout 5;
root /srv/www/xxxxx/current/public/;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
# If you don't find the filename in the static files
# Then request it from the unicorn server
if (!-f $request_filename) {
proxy_pass http://unicorn_staging.xxxxx.com;
break;
}
}
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
error_page 500 502 503 504 /500.html;
location = /500.html {
root /srv/www/xxxxx/current/public/;
}
}
server {
listen 443;
server_name staging.xxxxx.com xxxxx dublin;
access_log /var/log/nginx/staging.xxxxx.com-ssl.access.log;
ssl on;
ssl_certificate /etc/nginx/ssl/staging.xxxxx.com.crt;
ssl_certificate_key /etc/nginx/ssl/staging.xxxxx.com.key;
keepalive_timeout 5;
root /srv/www/xxxxx/current/public/;
location / {
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
# If you don't find the filename in the static files
# Then request it from the unicorn server
if (!-f $request_filename) {
proxy_pass http://unicorn_staging.xxxxx.com;
break;
}
}
error_page 500 502 503 504 /500.html;
location = /500.html {
root /srv/www/xxxxx/current/public/;
}
}
I am using Ruby on Rails, Nginx and unicorn. I have set force_ssl to true in rails so it automatically redirects all the requests to https.
Access log when using https:
[01/Apr/2016:08:58:28 +0000] "GET /api/v2/users HTTP/1.1" 404 15384 "-" "XxxxStaging/28 CFNetwork/758.2.8 Darwin/15.0.0"
Access log when using http:
[01/Apr/2016:09:00:08 +0000] "POST /api/v2/users HTTP/1.1" 201 1620 "-" "curl/7.43.0"

Websockets with Nginx and ssl handshake

I have the application written in Rails and Ember frontend for it. It is accessible by nginx server. Here is configuration for Rails part:
upstream app_project_app {
server unix:///tmp/project.sock fail_timeout=0;
}
And here is configuration for ember part:
server {
listen 80;
server_name project.demo.domain.pl;
root /home/lunar/apps/project-ember/current;
try_files /system/maintenance.html $uri/index.html $uri.html $uri #app;
access_log /var/log/nginx/project_app_access.log;
error_log /var/log/nginx/project_app_error.log;
keepalive_timeout 5;
proxy_read_timeout 60;
proxy_send_timeout 60;
proxy_connect_timeout 60;
if ($request_method !~ ^(GET|HEAD|PUT|POST|DELETE|OPTIONS)$ ){
return 405;
}
location ~ ^/assets/ {
expires max;
add_header Cache-Control public;
add_header ETag "";
break;
}
location = /favicon.ico {
expires max;
add_header Cache-Control public;
}
location / {
try_files $uri/index.html $uri.html $uri #app;
error_page 404 /404.html;
error_page 422 /422.html;
error_page 500 502 503 504 /500.html;
error_page 403 /403.html;
}
location #app {
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://app_project_app;
}
}
Now the application grown and has websockets server (using faye). And the client can't connect to the server:
WebSocket connection to 'ws://project.demo.domain.pl/faye' failed: Error during WebSocket handshake: Unexpected response code: 400
I've read, that I need to enable SSL for this handshake. How can I do this in nginx? I also read, that I don't need to use https and I can use SSL only for websockets, is it true? And if yes, how should look configuration for nginx in this case?
For websocket support you need add the following directives in your #app location block
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgradeā€;
Read more here

Nginx shows "Welcome to nginx" for http request in Rails app

I am using following nginx configurations:
user www-data;
worker_processes 1;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
# multi_accept on;
}
http {
include /etc/nginx/mime.types;
access_log /var/log/nginx/access.log;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
tcp_nodelay on;
gzip on;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
upstream myapp.co {
server 127.0.0.1:8080;
}
server{
listen 80;
server_name myapp.co;
rewrite ^ https://myapp.co$request_uri? permanent;
}
server {
listen 443 ssl;
server_name myapp.co;
root /home/deployer/myapp/public;
ssl on;
ssl_certificate /etc/nginx/certs/myapp.co.crt;
ssl_certificate_key /etc/nginx/certs/myapp.co.private.key;
#server_name myapp.co _;
#root /home/deployer/myapp/public;
location / {
proxy_set_header X_FORWARDED_PROTO $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header CLIENT_IP $remote_addr;
proxy_redirect http:// https://;
if (!-f $request_filename) {
proxy_pass http://myapp.co;
break;
}
if (-f $document_root/system/maintenance.html) {
return 503;
}
}
}
}
The issue: when I load http://www.myapp.co, I get the error message
Welcome to nginx
But if I set to the browser
https://www.myapp.co
https://myapp.co
http://myapp.co
Everything is working well.
How can I fix up the proper displaying of the Rails app also for the request http://www.myapp.co?
I am quite amateur with setting up of nginx, so I'll be grateful for every advice.
Thank you
I think, you should set your server_name (in both server sections) like this:
server_name myapp.co www.myapp.co;

Resources