I have a Rails 5 app which uses Action Cable for websocket functionality.
In my development environment everything works as expected and the browser clients successfully connect to the Action Cable channels.
In my production environment Action Cable was working at some point, but then suddenly stopped functioning without any immediate apparent cause.
If I change the RAILS_ENV to production while running the app on my development machine Action Cable works fine. Something seems different when running the app on the actual production machine although the basic environment is the same.
The specific error I see in the Chrome console:
mydomain.com/:1 WebSocket connection to 'wss://mydomain.com/cable' failed: WebSocket is closed before the connection is established. I get a similar error in other browsers so it doesn't appear to be browser related. I disabled any adblockers while testing just to be sure they do not interfere.
Development.rb ENV related setup:
config.action_cable.url = "ws://localhost:#{port}/cable"
Production.rb ENV related setup:
hostname = ENV.fetch('HOSTNAME')
port = ENV.fetch('PORT')
base_url = "#{hostname}:#{port}"
config.action_cable.url = "wss://#{hostname}/cable"
config.action_cable.allowed_request_origins = ["https://#{base_url}", "https://#{hostname}"]
I use Puma as a webserver. The webserver serves a SSL connection for which a valid certificate is installed. On the production machine Puma serves the application on port 3000 but this is forwarded to port 443 in the router.
The only notable difference with running the app on my dev machine and production is that in production SSL is used.
I can now safely conclude this is a bug, probably in Rails/ActionCable itself. This is corroborated by other reports and as I told at one point it worked fine, this was when I used Rails 5.0.0.1. When I updated to 5.0.1 it broke and it remains broken on 5.0.2. I'm opening an issue on the GitHub issue tracker of the Rails project.
Edit July 2017: Rails did change something as to how it reads and writes to the Rack socket, however the actual webserver software you use needs to support these nonblocking read and write methods. In my case, Puma did not at that time hence websockets didn't work. For Puma there is now a new release with a workaround for this issue.
As per your problem statement
Your Development environment works because you have set it to handle insecure web traffic but on your Production environment is set to handle secure traffic.
Development.rb ENV related setup:
config.action_cable.url = "ws://localhost:#{port}/cable"
Production.rb ENV related setup:
config.action_cable.url = "wss://#{hostname}/cable"
According to your network setup you have configured your SSL port redirection to port 3000 and serving web request from PUMA rb server.
Now the problem i see over here is wss://#{hostname} will try to listen on default port 443 for secure traffic instead your redirected port 3000
Related
I have a Rails application that uses subdomains (legacy application, I've been wanting to change that, not yet). I deployed my app to Heroku and I've started to test Puma because it's the recommended choice for Heroku and the default in the upcoming release of Rails. When I used WEBrick (locally) I was able to test my subdomains using a DNS record that pointed to 127.0.0.1 such as vcap.me, specifically http://vcap.me:3000/ would point to my app and http://abcde.vcap.me:3000/ will correctly set the subdomian to "abcde".
Simply adding gem 'puma' to my Gemfile and runnning bundle, causes rails server to start Puma. Except none of the test domains work: http://localhost:3000/ works, but not http://vcap.me:3000/ or http://lvh.me:3000/
Chrome simply says:
"This webpage is not available
ERR_CONNECTION_REFUSED"
Firefox:
"Unable to connect
Firefox can't establish a connection to the server at vcap.me:3000.
..."
I haven't found a cause/solution, but I suspect it has to do with non HTTP TCP requests supported by Puma, except right know, I'm simply trying a HTTP request through the browser.
Just for the curious, if you haven't heard about vcap.me and similar domains, it's simply a DNS record that points to localhost:
$ dig vcap.me
...
vcap.me. 3048 IN A 127.0.0.1
...
$ dig a.vcap.me
...
a.vcap.me. 3600 IN A 127.0.0.1
...
I feel ashamed, #maxd posted a solution to a very similar question: https://stackoverflow.com/a/28745407/637094 and it works. I still don't understand why I need to bind to vcap.me and I didn't before when I used WEBrick.
rails server -p 3000 -b vcap.me
I'll leave the question open, so maybe someone can expand and we all get a better picture of what's going on
This was issue #782 in the Puma server that was solved on July 18, 2016 here.
Your use of domains like vcap.me was not the issue. That domain be resolved to 127.0.0.1 by the DNS server. The issue was, before it was fixed, that on some systems Puma would by default bind only to the IPv6 resolution of localhost, i.e. ::1. Since vcap.me does not provide a IPv6 resolution, you could not reach Puma by calling http://vcap.me:3000/.
Your observation that rails server -p 3000 -b vcap.me solved the issue is because that is equivalent to rails server -p 3000 -b 127.0.0.1. After that, the server's address matched the IPv4-only name resolution of vcap.me.
Anyway, it's an issue of the past. Now by default, Puma binds to both the IPv4 and IPv6 resolutions of localhost.
I'm migrating my rails app (still in development) from Thin to Unicorn. My app uses config.force_ssl = true.
When using Thin I could just write:
thin start --ssl
My question is: What is the equivalent way to start Unicorn with ssl in development?
If I correctly understood your question, you're trying to run unicorn on port 443.
This is a bad idea.
Instead, to achieve the same goal, I would suggest, run unicorn on an unprivileged port (above 1024), or better on a unix socket, and switch Nginx before, passing all static stuff directly trough nginx, and the rails stuff, trough unicorn.
I know this doesn't answer your question, but for the user, it will work exactly the same, with some benefits when your app server (unicorn) crashes, for example a nice rendered 502 error page served via nginx instead of a plain network error message seen in the browser of your users.
You can with this solution run X different applications on the same port, with different subdomains. a must have for a development machine with many projects.
I've been playing with HTTPS in a local rails application using the 'answer' from the following question: Configuring WEBrick to use SSL in Rails 4
So i was running rails via a 'second /bin/rails_temp file and the https was working
now going back to my original rails file and just running 'rails s' I get redirected to https for everything
I've tried clearing cache in browsers, loading new browsers, removing /tmp/ contents
Totally lost!
Are you forcing SSL anywhere in your application?
For example config.force_ssl = true
Perhaps changing the port would affect the behaviour?
:Port => 3000, # Specify the port here
Perhaps your laptop is caching the state of the server so changing it's address (using /etc/hosts) would help?
127.0.0.1 sslapplication.local
I am successfully using the websocket-rails gem in my development environment, but I am not able to use it when it is deployed to my production machine. I am using the standalone server mode with the JavaScript client:
var dispatcher = new WebSocketRails("localhost:3001/websocket");
But following the same technique in production either results in dispatcher (with no prefix) being undefined, or it being defined successfully but the browser not being able to establish a connection to the server (when using a wss:// prefix).
I wonder if this has anything to do with interference from the SSL cert.
Any ideas?
EDIT
I use the production server's address in production and not 'localhost'.
Yo should not use localhost it should point to the production server IP address like:
new WebSocketRails("IP_ADDRESS:3001/websocket");
Localhost points to the same machine.
Also look the chrome console to see if you are not getting Cross Domain exceptions
My application isn't quite ready to go full SSL so in the meantime, I am allowing SSL but not requiring it. I do require SSL for certain controllers and force redirects to HTTPS for actions in such controllers.
I can start a thin server using SSL via thin start --ssl. This works great for SSL testing. However, I cannot have HTTP and HTTPS running simultaneously on the same port. Obviously, this makes testing redirects from HTTP to HTTPS quite frustrating. I can run an Apache or nginx server on top but I don't really want to go through the trouble of doing that in my development environment.
To start two servers, one for SSL and without, I use foreman like so:
web: rails server
ssl: thin start --ssl -p 3001
This starts the HTTP server on port 3000 and HTTPS server on 3001. Now my question is. How do I create a "smart redirect" policy only on my development environment only such that redirects from HTTP to HTTPS intelligently changes the port from 3000 to 3001? Thanks!