skip to Main Content

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

Main questions:

  1. Do the above error messages mean the authentication didn’t pass?

  2. 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.)

  3. Does Rails start the redis server for ActionCable automatically with rails s?

What I tried that doesn’t seem to work:

  • Add to routes.rb:
mount ActionCable.server => "/cable"
  • Add to config/environments/development.rb:
config.action_cable.url = "ws://localhost:3000/cable"
config.action_cable.allowed_request_origins = [/http://*/,/https://*/]

App configuration:

  • gem turbo-rails version 2.0.5
  • gem redis 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
  • model declares broadcasts_refreshes
class FieldSlip < ApplicationRecord
  broadcasts_refreshes
  • view template has 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?

2

Answers


  1. Chosen as BEST ANSWER

    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.


  2. I also had the same issue. I noticed that not only did it affect the streams, but any Hotwire feature. I also tried all the things you outlined.

    What solved it for me was adding the following tag to the head in my application.html.erb:

    <%= turbo_include_tags %>

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search