Connect Rails backend to remote socket.io service?

78 Views Asked by At

We have a remote service that we want to connect to that (unfortunately) uses socket.io as the websocket implementation. It is 'easy' to connect to this from a javascript client (or python). But I can find nothing for Rails.

To clarify - we want to be a socket.io CLIENT, not run a socket.io server.

Any code examples, gists, snippets, etc that show how to connect to socket.io - even if it means writing a bridging service - would be appreciated.

2

There are 2 best solutions below

0
kwerle On

Is there a reason you don't want to use/crib

https://github.com/shokai/ruby-socket.io-client-simple

0
Alex On

Instructions: https://socket.io/docs/v4/engine-io-protocol/

Set up:

gem install websocket-client-simple
git clone https://github.com/socketio/chat-example.git
cd chat-example
touch sio.rb
npm install
open http://localhost:3000/

# start chat server
DEBUG=* node index.js

A "few" lines of code and you're good to go:

# sio.rb

require "json"
require "uri"
require "net/http"
require "websocket-client-simple"

class Sio
  attr_reader :url, :websocket
  attr_accessor :state, :last_ping_at, :last_pong_at

  def initialize(url = "http://localhost:3000", **opts)
    # some socketio specific dance
    params = {EIO: 4, transport: :polling}
    uri = URI.parse(url)
    @uri = uri

    uri.query = URI.encode_www_form(params)
    uri.path = "/socket.io/"
    puts uri
    response = Net::HTTP.get(uri)
    _code, json = parse(response)

    uri.query = URI.encode_www_form(params.merge!(json.slice(:sid)))
    response = Net::HTTP.post(uri, "40")
    puts response.body
   
    response = Net::HTTP.get(uri)
    parse(response)

    uri.scheme = "ws"
    uri.query = URI.encode_www_form(params.merge!(transport: :websocket))
    puts uri

    Thread.new do
      loop do
        if @websocket
          if @state == :connect
            emit("chat message", Time.now.to_s)
          end
        end
        sleep 1
      end
    end
  end

  def connect
    @websocket = WebSocket::Client::Simple.connect(@uri.to_s)
    client = self
    socket = @websocket

    @websocket.on :open do |msg|
      client.state = :connect
      socket.send "2probe"                     # say 2probe to get 3probe
    end
    @websocket.on :message do |msg|
      code, data = client.parse(msg.data)
      case code
      when 0  # connect
        client.state = :connect
      when 2  # ping
        socket.send 3
      when 3  # pong
        socket.send 5 if data.to_s == "probe"  # say 5 to upgrade when you get 3probe
      when 41  # disconnect
        socket.close if socket.open?
        client.state = :disconnect
      when 42  # data
        data
      end
    end
  end

  def emit(event_name, *data)
    data.unshift event_name
    @websocket.send "42#{data.to_json}"
  end

  def parse data
    code, body = data.split(/^\d+\K/)
    parsed = begin
      JSON.parse(body, {symbolize_names: true})
    rescue
      body
    end
    p [code.to_i, parsed]
  end
end
load "./sio.rb"
sio = Sio.new
sio.connect

#=>
# http://localhost:3000/socket.io/?EIO=4&transport=polling
# [0, {:sid=>"9Z8g_jE7TL2GjZ1gAAAC", :upgrades=>["websocket"], :pingInterval=>25000, :pingTimeout=>20000}]
# ok
# [40, {:sid=>"BvLtpgr0bZ9U4TzTAAAD"}]
# ws://localhost:3000/socket.io/?EIO=4&transport=websocket&sid=9Z8g_jE7TL2GjZ1gAAAC

# [3, "probe"]
# [42, ["chat message", "2023-08-26 10:22:57 -0400"]]
# [42, ["chat message", "2023-08-26 10:22:58 -0400"]]
# [42, ["chat message", "message from the browser"]]   # from the chat input
# [42, ["chat message", "2023-08-26 10:22:59 -0400"]]
# [42, ["chat message", "2023-08-26 10:23:00 -0400"]]  # yes i can see these in the browser

and now i'm never gonna use it :)