I have set up my Ruby on Rails 5 ActionCable to receive websocket messages from external devcies (like websocket client plugins for browser etc.). The connection works fine but when i send messages i get an JSON::ParserError. Rails log level is alread :debug but I’m not able to identify the route or how i can catch the error or read the message parameters.
All i get in the console log is this:
There was an exception - JSON::ParserError(783: unexpected token at 'ping')
/usr/local/lib/ruby/2.7.0/json/common.rb:156:in `parse'
/usr/local/lib/ruby/2.7.0/json/common.rb:156:in `parse'
/usr/local/bundle/gems/activesupport-5.2.3/lib/active_support/json/decoding.rb:23:in `decode'
/usr/local/bundle/gems/actioncable-5.2.3/lib/action_cable/connection/base.rb:164:in `decode'
/usr/local/bundle/gems/actioncable-5.2.3/lib/action_cable/connection/base.rb:87:in `dispatch_websocket_message'
/usr/local/bundle/gems/actioncable-5.2.3/lib/action_cable/server/worker.rb:60:in `block in invoke'
/usr/local/bundle/gems/actioncable-5.2.3/lib/action_cable/server/worker.rb:41:in `block in work'
/usr/local/bundle/gems/activesupport-5.2.3/lib/active_support/callbacks.rb:109:in `block in run_callbacks'
/usr/local/bundle/gems/actioncable-5.2.3/lib/action_cable/server/worker/active_record_connection_management.rb:16:in `block in with_database_connections'
/usr/local/bundle/gems/actioncable-5.2.3/lib/action_cable/connection/tagged_logger_proxy.rb:24:in `block in tag'
/usr/local/bundle/gems/activesupport-5.2.3/lib/active_support/tagged_logging.rb:71:in `block in tagged'
/usr/local/bundle/gems/activesupport-5.2.3/lib/active_support/tagged_logging.rb:28:in `tagged'
/usr/local/bundle/gems/activesupport-5.2.3/lib/active_support/tagged_logging.rb:71:in `tagged'
/usr/local/bundle/gems/actioncable-5.2.3/lib/action_cable/connection/tagged_logger_proxy.rb:24:in `tag'
/usr/local/bundle/gems/actioncable-5.2.3/lib/action_cable/server/worker/active_record_connection_management.rb:16:in `with_database_connections'
/usr/local/bundle/gems/activesupport-5.2.3/lib/active_support/callbacks.rb:118:in `block in run_callbacks'
/usr/local/bundle/gems/actioncable-5.2.3/lib/action_cable/engine.rb:62:in `block (4 levels) in <class:Engine>'
/usr/local/bundle/gems/activesupport-5.2.3/lib/active_support/execution_wrapper.rb:87:in `wrap'
/usr/local/bundle/gems/actioncable-5.2.3/lib/action_cable/engine.rb:57:in `block (3 levels) in <class:Engine>'
/usr/local/bundle/gems/activesupport-5.2.3/lib/active_support/callbacks.rb:118:in `instance_exec'
/usr/local/bundle/gems/activesupport-5.2.3/lib/active_support/callbacks.rb:118:in `block in run_callbacks'
/usr/local/bundle/gems/activesupport-5.2.3/lib/active_support/callbacks.rb:136:in `run_callbacks'
/usr/local/bundle/gems/actioncable-5.2.3/lib/action_cable/server/worker.rb:40:in `work'
/usr/local/bundle/gems/actioncable-5.2.3/lib/action_cable/server/worker.rb:58:in `invoke'
/usr/local/bundle/gems/actioncable-5.2.3/lib/action_cable/server/worker.rb:53:in `block in async_invoke'
/usr/local/bundle/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:352:in `run_task'
/usr/local/bundle/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:343:in `block (3 levels) in create_worker'
/usr/local/bundle/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:334:in `loop'
/usr/local/bundle/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:334:in `block (2 levels) in create_worker'
/usr/local/bundle/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:333:in `catch'
/usr/local/bundle/gems/concurrent-ruby-1.1.10/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:333:in `block in create_worker'
It looks like the incoming message is not being parsed correctly. Unfortunately I can't make any changes to the incoming messages, I have to work with what arrives.
Update: I have updated my config/application.rb file like this:
require_relative '../lib/middleware/catch_json_parse_errors'
...
module my_rails5_app
class Application < Rails::Application
...
config.middleware.insert_before Rack::Head, CatchJsonParseErrors
end
end
And lib/middleware/catch_json_parse_errors.rb looks like this:
class CatchJsonParseErrors
def initialize(app)
@app = app
end
def call(env)
begin
@app.call(env)
rescue JSON::ParserError => e
if env['CONTENT_TYPE'] =~ /application\/json/
return [
400, # Status code
{ 'Content-Type' => 'application/json' }, # Response headers
[{ status: 400, error: 'Bad Request', message: 'The browser (or proxy) sent a request that this server could not understand.' }.to_json] # Response body
]
else
raise e
end
end
end
end
When i send a websocket message like "test" the error still occurs. Just to mention it, when i send a websocket message like "{anything: 'test'}" the message can be parsed and no error occurs.
I have also tried the error classes ActionDispatch::Http::Parameters::ParseError and StandardError.