I'm trying to configure a Rails 7.1.3/Turbo 2.0.5 app locally to try out broadcasts_refreshes
for a simple model. On the view template that tries to turbo_stream_from
this model, I'm getting websocket errors, indicating that it is trying to connect.
Here are the errors I am getting:
# in the browser, with or without the above config.action_cable.url
WebSocket connection to 'ws://localhost:3000/cable' failed: There was a bad response from the server.
# in the terminal window, while rails server is running
Started GET "/cable" for ::1 at 2024-04-18 01:14:20 -0700
Started GET "/cable" [WebSocket] for ::1 at 2024-04-18 01:14:20 -0700
Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
WebSocket error occurred: undefined method `write_nonblock' for nil
::1 - - [18/Apr/2024:01:14:20 PDT] "GET /cable HTTP/1.1" -1 0
WebSocket error occurred: undefined method `write_nonblock' for nil
Do the above error messages mean the authentication didn't pass?
If Turbo creates its own channels, does it also create its own websocket connections and handle its own auth logic separately? Or does it use my app's ActionCable connection authentication logic in the app/channels/application_cable/connection.rb
file? (My connection.rb
file is currently empty, because we don't use ActionCable for anything else.)
Does Rails start the redis server for ActionCable automatically with rails s
?
routes.rb
:mount ActionCable.server => "/cable"
config/environments/development.rb
:config.action_cable.url = "ws://localhost:3000/cable"
config.action_cable.allowed_request_origins = [/http:\/\/*/,/https:\/\/*/]
turbo-rails
version 2.0.5redis
installed. I have run ./bin/rails turbo:install:redis
cable.yml
configured for redis in development, thanks to this answer.development:
adapter: redis
url: redis://localhost:6379/1
broadcasts_refreshes
class FieldSlip < ApplicationRecord
broadcasts_refreshes
turbo_stream_from
:<%= turbo_stream_from(:field_slip, dom_id(field_slip)) %>
<%= render(partial: "field_slips/row",
locals: { field_slip: field_slip }) %>
Everything on the template side seems to be working, Turbo is generating turbo-cable-stream-source
tags, but I notice there is no connected
attribute:
<turbo-cable-stream-source channel="Turbo::StreamsChannel" signed-stream-name="ImZpZWxkX3NsaXBfam9iX3RyYWNrZXI6ZmllbGRfc2xpcF9qb2JfdHJhY2tlcl8xIg==--bcf536df9f893f646adb2ce946d4d8ffa166eb297cca1a5c7fa093c77b0da878"></turbo-cable-stream-source>
Clearly, the websocket is not working. What am I missing?
In my case it was the authentication issue. It is true that Turbo can connect to the websocket without authentication, but if your app requires authentication at that point, the websocket request needs authentication too. In our case, the whole view where the live updates should occur requires login.
Adjusting app/channels/application_cable/connection.rb
to use the app's authentication logic (from the cookie) made it so the Action Cable request was authenticated the same way.
With this working, the turbo-cable-stream-source
tag in the rendered page shows a connected
attribute.