Coder Social home page Coder Social logo

nullvoxpopuli / action_cable_client Goto Github PK

View Code? Open in Web Editor NEW
257.0 11.0 28.0 98 KB

A ruby client for interacting with Rails' ActionCable. -- Maintainers Wanted.

License: MIT License

Ruby 100.00%
action-cable action-cable-protocol rails ruby em-websocket hacktoberfest

action_cable_client's Introduction

Action Cable Client

Gem Version Build Status Code Climate Test Coverage

This gem is a wrapper around websocket-eventmachine-client, and supports the Rails Action Cable protocol.

Usage

require 'action_cable_client'

EventMachine.run do

  uri = "ws://localhost:3000/cable/"
  client = ActionCableClient.new(uri, 'RoomChannel')
  # called whenever a welcome message is received from the server
  client.connected { puts 'successfully connected.' }

  # called whenever a message is received from the server
  client.received do | message |
    puts message
  end

  # Sends a message to the sever, with the 'action', 'speak'
  client.perform('speak', { message: 'hello from amc' })
end

This example is compatible with this version of a small Rails app with Action Cable

The available hooks to tie in to are:

  • disconnected {}
  • connected {}
  • subscribed {}
  • rejected {}
  • errored { |msg| }
  • received { |msg }
  • pinged { |msg| }

Connecting on initialization is also configurable.

client = ActionCableClient.new(uri, 'RoomChannel', false)
client.connect!(headers = {})
client.pinged do |_data|
  # you could track the time since you last received a ping, if you haven't
  # received one in a while, it could be that your client is disconnected.
end

To reconnect,

client.reconnect!

Sending additional params

params = { channel: 'RoomChannel', favorite_color: 'blue' }
client = ActionCableClient.new(uri, params)

then on the server end, in your Channel, params will give you:

{
       "channel" => "RoomChannel",
"favorite_color" => "blue"
}

Using Headers

params = { channel: 'RoomChannel', favorite_color: 'blue' }
client = ActionCableClient.new(uri, params, true, {
  'Authorization' => 'Bearer token'
})

Using TLS

Example given for client certificate authentication. See EventMachine::Connection#start_tls documentation for other options.

params = { channel: 'RoomChannel', favorite_color: 'blue' }
tls = {cert_chain_file: 'user.crt', private_key_file: 'user.key'}
client = ActionCableClient.new(uri, params, true, nil, tls)

Demo

Live Demo

Action Cable Client Demo on YouTube (1:41)

Here is a set of files in a gist that demonstrate how different action_cable_clients can communicate with eachother.

The Action Cable Protocol

There really isn't that much to this gem. :-)

  1. Connect to the Action Cable URL
  2. After the connection succeeds, send a subscribe message
  • The subscribe message JSON should look like this
    • {"command":"subscribe","identifier":"{\"channel\":\"MeshRelayChannel\"}"}
  • You should receive a message like this:
    • {"identifier"=>"{\"channel\":\"MeshRelayChannel\"}", "type"=>"confirm_subscription"}
  1. Once subscribed, you can send messages.
  • Make sure that the action string matches the data-handling method name on your ActionCable server.
  • Your message JSON should look like this:
    • {"command":"message","identifier":"{\"channel\":\"MeshRelayChannel\"}","data":"{\"to\":\"user1\",\"message\":\"hello from user2\",\"action\":\"chat\"}"}
    • Received messages should look about the same
  1. Notes:
  • Every message sent to the server has a command and identifier key.
  • The channel value must match the name of the channel class on the ActionCable server.
  • identifier and data are redundantly jsonified. So, for example (in ruby):
payload = {
  command: 'command text',
  identifier: { channel: 'MeshRelayChannel' }.to_json,
  data: { to: 'user', message: 'hi', action: 'chat' }.to_json
}.to_json

Contributing

  1. Fork it ( https://github.com/NullVoxPopuli/action_cable_client/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

action_cable_client's People

Contributors

jan-vitek avatar jer-k avatar neomilium avatar nullvoxpopuli avatar srabuini avatar wpp avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

action_cable_client's Issues

Create wrapper gem or a persistent connection, based off of the relay_pool logic from meshchat

https://github.com/NullVoxPopuli/meshchat-core/blob/master/lib/meshchat/network/remote/relay_pool.rb

Basically, this should just function like the ConnectionPool that rails uses.
The behavior should be:

  • have at least a minimum number of connections
  • if a connection dies, start another one to reach minimum number of connections
  • if there is consistently no way to reach the minimum number of connections, log to the console, and try again in a minute, 2 minutes, 5 minutes, 10 minutes, and then stop trying.

Add a way to actually test the action cable connection (with dummy app?)

Not sure how best to do this, or how far along action cable testing has come since this project was started, but there needs to be a way to actually test the communication with an action cable server. I think this might involve the use of a dummy app in the test folder, but am unsure if there is an elegant way to isolate action cable configurations between tests.

See the coverage report for things need testing.
This should be the last thing to get this project to 100% test coverage.

How to use this gem in rails application with sustainability

Hi,
I have a rails app and another rails app with actioncable channel. I need to communicate between these two rails app using actioncable. Is there an example using this gem. And how to ensure the socket will reconnect if let say disconnected for many hours.

thanks in advance!

document use of connected?

in my meshchat project, if I make a call to connected? I get a really funky error:

/home/webdev/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/json/common.rb:156:in `parse': 784: unexpected token at 'One or more reserved bits are on: reserved1 = 1, reserved2 = 0, reserved3 = 0' (JSON::ParserError)
    from /home/webdev/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/json/common.rb:156:in `parse'
    from /home/webdev/.rvm/gems/ruby-2.3.0@meshchat/gems/action_cable_client-1.2.4/lib/action_cable_client.rb:132:in `handle_received_message'
    from /home/webdev/.rvm/gems/ruby-2.3.0@meshchat/gems/action_cable_client-1.2.4/lib/action_cable_client.rb:78:in `block in received'
    from /home/webdev/.rvm/gems/ruby-2.3.0@meshchat/gems/em-websocket-client-0.1.2/lib/em-websocket-client.rb:43:in `receive_data'
    from /home/webdev/.rvm/gems/ruby-2.3.0@meshchat/gems/eventmachine-1.2.0.1/lib/eventmachine.rb:194:in `run_machine'
    from /home/webdev/.rvm/gems/ruby-2.3.0@meshchat/gems/eventmachine-1.2.0.1/lib/eventmachine.rb:194:in `run'
    from /home/webdev/Development/meshchat/lib/meshchat.rb:53:in `start'
    from ./run:90:in `<main>'

Subscription params

In the JS ActionCable client an object passed as the first argument to subscription factory becomes the params hash in the channel:

App.cable.subscriptions.create({
  channel: 'ChatChannel',
  room: 'super-duper-room'
}, /* ... */);

Is there any way to pass params using action_cable_client?

How to close connection from client?

Hi,

I have written a small JRuby app and I'm using this gem to interact with rails web app Action Cable channel. I want to close the connection when I logout from the app.
Is it possible and what is the correct way to close the connection using action_cable_client gem?

Thanks

No message Json received when ruby client connect to rails server

the ruby client connect successfully to server rails but no respond in terminal
when i stop client i have this problem :/usr/local/rvm/gems/ruby-2.2.2@global/gems/eventmachine-1.2.3/lib/eventmachine.rb:194:in run_machine': Interrupt from /usr/local/rvm/gems/ruby-2.2.2@global/gems/eventmachine-1.2.3/lib/eventmachine.rb:194:in run'
from local_test.rb:7:in `

'

at terminal rails server when client ruby connect to the server this what happens :
Started GET "/cable/" for ::1 at 2017-04-20 21:09:52 +0100
Started GET "/cable/" [WebSocket] for ::1 at 2017-04-20 21:09:52 +0100
Request origin not allowed:
Failed to upgrade to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
Finished "/cable/" [WebSocket] for ::1 at 2017-04-20 21:09:52 +0100
2017-04-20 21:09:52 +0100: HTTP parse error, malformed request (): #<Puma::HttpParserError: Invalid HTTP format, parsing fails.>
2017-04-20 21:09:52 +0100: ENV: {"rack.version"=>[1, 3], "rack.errors"=>#<IO:>, "rack.multithread"=>true, "rack.multiprocess"=>false, "rack.run_once"=>false, "SCRIPT_NAME"=>"", "QUERY_STRING"=>"", "SERVER_PROTOCOL"=>"HTTP/1.1", "SERVER_SOFTWARE"=>"puma 3.8.2 Sassy Salamander", "GATEWAY_INTERFACE"=>"CGI/1.2"}

Started GET "/rooms/show" for ::1 at 2017-04-20 21:10:41 +0100
Processing by RoomsController#show as HTML
Rendering rooms/show.html.erb within layouts/application
Rendered rooms/show.html.erb within layouts/application (1.3ms)
Completed 200 OK in 608ms (Views: 568.7ms | ActiveRecord: 0.0ms)

Cannot connect with Rails 5.0.0.1 actioncable-example

I am trying to connect this client to https://github.com/rails/actioncable-examples but the client didn't response anything.

image
it just forever silence.

below is my code, i'm using authetication token instead of session which i tested fine from the web browser.

require 'action_cable_client'

EventMachine.run do
  uri = "ws://localhost:28080/?auth_token=aaa"
  client = ActionCableClient.new(uri, 'CommentsChannel')
  client.connected { puts 'successfully connected.' }
  client.received do | message |
    puts message
  end
  client.perform('follow', { message_id: 1 })
end

noticed that client.perform('follow', { message_id: 1 }) is what i'm trying to simulate from the actioncable-examples app @perform 'follow', message_id: messageId in comments.coffee. I don't think it's the right code. :)

Update :
I notice that server quickly disconnect me when I implement this code client.disconnected { puts 'DISCONNECTED' } it disconnects right away after I started this client.

reconnect! method not calling the connected callback?

Hi,
Just saw the PR that implemented the reconnect! method. I am actually using it but i noticed it doesn't calls back the connected callback. What i am doing to trigger the disconnect the callback is just shutting down the ActionCable server in a different process, then turning it back on immediately.

Here is my sample code:

require 'action_cable_client'

EventMachine.run do

  @connected = false
  device_id = 1
  uri = 'ws://localhost:3001/cable'
  params = {
    channel: 'DevicesChannel'
  }
  headers = {
    :Origin => 'ws://localhost:3001/cable',
    :device_id => device_id
  }

  client = ActionCableClient.new(uri, params, true, headers)

  client.connected do
    @connected = true
    puts 'successfully connected.'
  end

  client.received do |message|
    p message
  end

  client.subscribed do
    puts "-> Subscribed!"
  end

  client.disconnected do
    @connected = false
    while !@connected
      puts "-> Disconnected, trying to reconnect in 5 seconds"
      sleep 5
      client.reconnect!
      puts "-> Client inspect: #{client.inspect}"
    end
  end

  client.errored do |e|
    @connected = false
    puts "-> Errored #{e}."
  end

end

I expected the reconnect method to start over all callbacks again. Maybe i need to subscribe all callbacks again? I think that would cause infinite loops.

Here is my console output:

Davids-MacBook-Air:keypad-firmware damuz91$ ruby app/keypad_socket.rb 
successfully connected.
-> Subscribed!
-> Disconnected, trying to reconnect in 15 seconds
-> Client inspect: #<ActionCableClient:0x00007fd6be9c8a28 @_uri="ws://localhost:3001/cable", @message_queue=[], @_subscribed=false, @_message_factory=#<ActionCableClient::MessageFactory:0x00007fd6be9c8988 @channel={:channel=>"KeypadsChannel"}, @identifier={:channel=>"KeypadsChannel"}>, @_websocket_client=#<WebSocket::EventMachine::Client:0x00007fd6be9c85a0 @signature=3, @args={:uri=>"ws://localhost:3001/cable", :headers=>{:Origin=>"ws://localhost:3001/cable", :keypad_id=>1}, :tls=>{}}, @state=:connecting, @handshake=<WebSocket::Handshake::Client:0x3feb5f83fcb8 @uri="ws://localhost:3001/cable", @headers={:Origin=>"ws://localhost:3001/cable", :device_id=>1}, @tls={}, @state=:new, @handler=#<WebSocket::Handshake::Handler::Client11:0x00007fd6bf07ef48 @handshake=<WebSocket::Handshake::Client:0x3feb5f83fcb8 @uri="ws://localhost:3001/cable", @headers={:Origin=>"ws://localhost:3001/cable", :device_id=>1}, @tls={}, @state=:new, @handler=#<WebSocket::Handshake::Handler::Client11:0x00007fd6bf07ef48 ...>, @data="", @protocols=[], @secure=false, @host="localhost", @port=3001, @path="/cable", @query=nil, @version=13>>, @data="", @protocols=[], @secure=false, @host="localhost", @port=3001, @path="/cable", @query=nil, @version=13>, @onclose=#<Proc:0x00007fd6bf840498@/Users/damuz91/.rvm/gems/ruby-2.6.0/gems/action_cable_client-3.1.0/lib/action_cable_client.rb:64>, @onmessage=#<Proc:0x00007fd6bf84b140@/Users/damuz91/.rvm/gems/ruby-2.6.0/gems/action_cable_client-3.1.0/lib/action_cable_client.rb:92>, @onerror=#<Proc:0x00007fd6bf84a600@app/keypad_socket.rb:52>, @frame=<WebSocket::Frame::Incoming::Client:0x3feb6040d6a8 @decoded=false, @code=nil, @data="", @version=13, @handler=#<WebSocket::Frame::Handler::Handler07:0x00007fd6bf064b70 @frame=<WebSocket::Frame::Incoming::Client:0x3feb6040d6a8 @decoded=false, @code=nil, @data="", @version=13, @handler=#<WebSocket::Frame::Handler::Handler07:0x00007fd6bf064b70 ...>>>>>, @_connected_callback=#<Proc:0x00007fd6bf84b5a0@/Users/damuz91/.rvm/gems/ruby-2.6.0/gems/action_cable_client-3.1.0/lib/action_cable_client.rb:107>, @_subscribed_callback=#<Proc:0x00007fd6bf84afb0@app/keypad_socket.rb:37>, @_disconnected_callback=#<Proc:0x00007fd6bf84aad8@/Users/damuz91/.rvm/gems/ruby-2.6.0/gems/action_cable_client-3.1.0/lib/action_cable_client.rb:143>>
-> Disconnected, trying to reconnect in 5 seconds

TLS on server configuration

Hello! โ€” I had an error trying to connect to a Rails server using certbot as HTTPS provider.

In order to connect from the client, I had to use:

uri = "wss://SERVERURL/cable/"
tls = {cert_chain_file: 'fullchain.pem', private_key_file: 'privkey.pem'}
client = ActionCableClient.new(uri, 'YourChannel', true, nil, tls)

The PEM files live here:
/etc/letsencrypt/live/YOURDOMAIN

Hope this helps someone.

Cheers!

Request origin not allowed

While this gem provides a nice simple way to test out actioncable, I could not get past the initial connection. The actioncable server is not allowing any request from this client. I added the following to the application.rb file:
config.action_cable.allowed_request_origins = [/http:\/\/*/, /https:\/\/*/]
But still the issue persists.
Any way to get pas this issue?

How to use client.connect! to reconnect

I have code as below:

  client.disconnected do
    puts "Disconnecteddddd"
    
    alive = false
    while alive == false
      puts "RECONNECCCT"
      sleep 5
      client.connect!
      sleep 5
      alive = true
    end
  end

I've tried several methods other than this. I'm unable to find a way how to reconnect my client if my server restarted.

Underscore prefix of attributes

What's the rationale of prefixing some attributes with _? Are you trying to indicate that these attributes are private? If so, why do they have attribute reader methods?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.