anycable / anycable-rails

AnyCable for Ruby on Rails applications
https://anycable.io
MIT License
494 stars 35 forks source link

ActionController::RoutingError (No route matches [GET] "/cable"): #194

Closed phiele closed 6 months ago

phiele commented 6 months ago

EDIT: Locally everything seems to be working now, besides getting this error:

09:02:58 ws.1      | 2024/02/21 09:02:58 ERROR: [transport] Client received GoAway with error code ENHANCE_YOUR_CALM and debug data equal to ASCII "too_many_pings".

However, in production the connection still keeps failing.


Environment:


I followed every step of the Exploring Rails 7, Hotwire and AnyCable speedy streams tutorial and already went through the documented troubleshooting, but somehow the Client connection still keeps failing in production.

1) config.action_cable.url points to the AnyCable server and not to the Rails one.

irb(main):001:0> Rails.application.config.action_cable.url
=> "wss://dev.tech/cable"
irb(main):001:0> ActionCable.server.config.url
=> "wss://dev.tech/cable"

2) Make sure that action_cable_meta_tag is called before JS script is loaded.

<head>
    <title>AnyCable app</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <%= action_cable_with_jwt_meta_tag %> <%# Required for AnyCable %>
    <link href="https://unpkg.com/dropzone@6.0.0-beta.1/dist/dropzone.css" rel="stylesheet" type="text/css" />
    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
    <%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
    <%= favicon_link_tag asset_path('favicon.png') %>
  </head>

here's the actual DOM element:

<meta name="action-cable-url" content="wss://dev.tech/cable?jid=eyJhbGciOiJIUzI1NiJ9.eyJleHQiOiJ7fSIsImV4cCI6MTcwODUwNjA5Mn0.8C4W0akHvPjiOhh67hOg_fQSNtkMATi_boAzKo2tEpo">

3) Make sure you do not pass incorrect URL to JS createConsumer question.

4) In case of using a reverse proxy (e.g. Nginx), check that it points to the correct server as well.


For the sake of completeness: anycable.yml

default: &default
  # Turn on/off access logs ("Started..." and "Finished...")
  access_logs_disabled: false
  # Whether to enable gRPC level logging or not
  log_grpc: false
  # Use Redis to broadcast messages to AnyCable server
  broadcast_adapter: redis
  # Use the same channel name for WebSocket server, e.g.:
  #   $ anycable-go --redis_channel="__anycable__"
  redis_channel: "__anycable__"
  # You can use REDIS_URL env var to configure Redis URL.
  # Localhost is used by default.
  # redis_url: "redis://localhost:6379/1"
  jwt_id_key: "secret"

development:
  <<: *default

test:
  <<: *default

production:
  <<: *default
  redis_url: <%= ENV.fetch("REDIS_URL", "redis://localhost:6379/1") %>

cable.yml

development:
  adapter: <%= ENV.fetch("ACTION_CABLE_ADAPTER", "any_cable") %>

test:
  adapter: test

production:
  adapter: <%= ENV.fetch("ACTION_CABLE_ADAPTER", "any_cable") %>

production.rb

Rails.application.configure do
  # Specify AnyCable WebSocket server URL to use by JS client
  config.after_initialize do
    config.action_cable.url = ActionCable.server.config.url = ENV.fetch("CABLE_URL", "/cable") if AnyCable::Rails.enabled?
  end

  # Specify AnyCable Turbo Streams verifier key
  # config.turbo.signed_stream_verifier_key = ENV.fetch("ANYCABLE_TURBO_RAILS_KEY")
  config.turbo.signed_stream_verifier_key = "secret"
  ...

Environment variables:

REDIS_URL=rediss://default:secret@dev-app-redis-do-user-123456789-0.b.db.ondigitalocean.com:25061
CABLE_URL=wss://dev.tech/cable
ANYCABLE_HOST=0.0.0.0
ANYCABLE_JWT_ID_KEY=secret
ANYCABLE_TURBO_RAILS_KEY=secret
ACTION_CABLE_ADAPTER=any_cable

Logs:

[dev] [2024-02-21 08:00:55] => Booting Puma
[dev] [2024-02-21 08:00:55] => Rails 7.0.6 application starting in production 
[dev] [2024-02-21 08:00:55] => Run `bin/rails server --help` for more startup options
[dev] [2024-02-21 08:00:59] 
[dev] [2024-02-21 08:00:59] ⚡ Motor::Admin is starting under /motor_admin
[dev] [2024-02-21 08:00:59] 
[dev] [2024-02-21 08:00:59] Puma starting in single mode...
[dev] [2024-02-21 08:00:59] * Puma version: 5.6.6 (ruby 3.2.2-p53) ("Birdie's Version")
[dev] [2024-02-21 08:00:59] *  Min threads: 5
[dev] [2024-02-21 08:00:59] *  Max threads: 5
[dev] [2024-02-21 08:00:59] *  Environment: production
[dev] [2024-02-21 08:00:59] *          PID: 1
[dev] [2024-02-21 08:00:59] * Listening on http://0.0.0.0:3000
[dev] [2024-02-21 08:00:59] Use Ctrl-C to stop
[dev] [2024-02-21 08:01:26] I, [2024-02-21T09:01:26.929148 #1]  INFO -- : [2d9528bf-eac6-4c61-91eb-96bc7919f93e] Started GET "/cable?jid=eyJhbGciOiJIUzI1NiJ9.eyJleHQiOiJ7fSIsImV4cCI6MTcwODQ3MjkyM30.yJPBccjVnlFRUXwYJ_WyRWnNynCfJoOhPUZDeNKot9I" for 162.158.112.130 at 2024-02-21 09:01:26 +0100
[dev] [2024-02-21 08:01:26] F, [2024-02-21T09:01:26.941229 #1] FATAL -- : [2d9528bf-eac6-4c61-91eb-96bc7919f93e]   
[dev] [2024-02-21 08:01:26] [2d9528bf-eac6-4c61-91eb-96bc7919f93e] ActionController::RoutingError (No route matches [GET] "/cable"):
[dev] [2024-02-21 08:01:26] [2d9528bf-eac6-4c61-91eb-96bc7919f93e]   
[dev] [2024-02-21 08:01:26] I, [2024-02-21T09:01:26.943861 #1]  INFO -- : [2d9528bf-eac6-4c61-91eb-96bc7919f93e] Processing by ErrorsController#not_found as */*
[dev] [2024-02-21 08:01:26] I, [2024-02-21T09:01:26.943978 #1]  INFO -- : [2d9528bf-eac6-4c61-91eb-96bc7919f93e]   Parameters: {"jid"=>"eyJhbGciOiJIUzI1NiJ9.eyJleHQiOiJ7fSIsImV4cCI6MTcwODQ3MjkyM30.yJPBccjVnlFRUXwYJ_WyRWnNynCfJoOhPUZDeNKot9I"}
[dev] [2024-02-21 08:01:27] I, [2024-02-21T09:01:27.311280 #1]  INFO -- : [17045664-5ff9-4da8-acc6-e3498f2c580a] Started GET "/cable?jid=eyJhbGciOiJIUzI1NiJ9.eyJleHQiOiJ7fSIsImV4cCI6MTcwODUwNjAxN30.6-c-0L9lDIsedgCEei1c96xKygcJkL_TeFDp3oT7b5E" for 162.158.112.130 at 2024-02-21 09:01:27 +0100
[dev] [2024-02-21 08:01:27] F, [2024-02-21T09:01:27.413998 #1] FATAL -- : [17045664-5ff9-4da8-acc6-e3498f2c580a

Logs websocket:

[2024-02-21 08:00:26] I 2024-02-21T08:00:26.069Z context=main Starting AnyCable 1.4.8-f9dd29c (pid: 1, open file limit: 1048576, gomaxprocs: 8)
[2024-02-21 08:00:26] I 2024-02-21T08:00:26.069Z context=main JWT identification is enabled (param: jid, enforced: false)
[2024-02-21 08:00:26] I 2024-02-21T08:00:26.069Z context=main Using channels router: Turbo::StreamsChannel
[2024-02-21 08:00:26] I 2024-02-21T08:00:26.070Z context=main Anonymized telemetry is on. Learn more: https://docs.anycable.io/anycable-go/telemetry
[2024-02-21 08:00:26] I 2024-02-21T08:00:26.070Z context=main Using no-op (legacy) broker
[2024-02-21 08:00:26] I 2024-02-21T08:00:26.073Z context=rpc RPC controller initialized: localhost:50051 (concurrency: 28, impl: grpc, enable_tls: false, proto_versions: v1)
[2024-02-21 08:00:26] I 2024-02-21T08:00:26.073Z context=main Handle WebSocket connections at http://0.0.0.0:8080/cable
[2024-02-21 08:00:26] I 2024-02-21T08:00:26.073Z context=main Handle health requests at http://0.0.0.0:8080/health
[2024-02-21 08:00:26] I 2024-02-21T08:00:26.149Z context=broadcast provider=redis Subscribed to Redis channel: __anycable__
[2024-02-21 08:00:26] 


Unfortunately, the other closed issues relating to the same problem haven't helped either.

Any clue what else I could try to debug?

palkan commented 6 months ago

Thanks for the details! Let's think what could go wrong..

config.action_cable.url points to the AnyCable server and not to the Rails one

createConsumer is not imported

Are you using the @hotwired/turbo-rails package then?

Let's check the browser dev tools and find the WS connection information; more precisely, we need to check the URL and who initiated the connection (in Chrome, open Network tab, filter by WS, probably, reload the page, find the request and check its "Initiator" tab for the source code, see below):

image
phiele commented 6 months ago

Thanks for your help @palkan!

Are you using the @hotwired/turbo-rails package then?

Yes.

Here's a screenshot of the request initiator chain:

Screenshot 2024-02-23 at 21 56 44

Note that the domain has changed from https://dev.tech/ to https://dev.glazed.tech/ in the meantime. However, the configuration of my initial post is still the same.

palkan commented 6 months ago

So, the URL used by the browser is the same as you configured in config.action_cable.url?

If so, the next question is how do you run anycable-go? Are you running Rails and AnyCable on the same machine or a separate one?

From the screenshot, I can see that the WS server hostname as the same as your Rails app hostname (your page is https://dev.glazed.tech/design_files/16). If you're running them on the same server, you should probably add a port to the cable url, i.e., wss://dev.glazed.tech:8080/cable.

phiele commented 6 months ago

So, the URL used by the browser is the same as you configured in config.action_cable.url?

Yes, it's wss://dev.glazed.tech/cable

Changing the CABLE_URL to wss://dev.glazed.tech:8080/cable didn't fix it:

Screenshot 2024-02-24 at 00 08 31

I'm running anycable-go on a dedicated WS server with ANYCABLE_HOST set to 0.0.0.0:

image

When checking the anycable-go server health, it looks alright:

image
palkan commented 6 months ago

When checking the anycable-go server health, it looks alright:

Interesting, it has /anycable-go namespace; so, probably, the correct WS url must be: wss://dev.glazed.tech/anycable-go/cable ?

phiele commented 6 months ago

True, I've no idea why I haven't tried adding the namespace before.

Changing the CABLE_URL to wss://dev.glazed.tech/anycable-go/cable fixed it.

Thank you @palkan !

phiele commented 6 months ago

When setting up an anycable-go WS server in Digital Ocean, make sure to include its required route path in the config.action_cable.url, e.g. wss://[your-domain]/[anycable-go-route]/cable