Building WebSocket server in Ruby
Vti’s post about building WebSocket server inspired me to write another one, but this time about both server and client in Ruby.
Currently we have couple of great implementations of WebSocket in Ruby - em-websocket, web-socket-ruby or sunshowers to name a few. And looking at each one of them I can see two things - they all are great, and they all are alone. What I mean?
Github alone says that there are 40 projects in Ruby connected to WebSocket. Some of them are basing one on another, but there are more than 15 server implementations from scratch. Only two support both Draft 75 and Draft 76. And only one support newer drafts. Am I only one who see the problem?
That’s why I built LibWebSocket library whose main purpose is to eliminate the problem of aging server implementations - wrapper around multiple drafts of WebSocket with common API that any server implementation can build against. Thank’s to that whole community will be able to gain instead of working independently. So how to get started?
Building server
Currently most popular Ruby WebSocket server is em-websocket (I’m also using it in my Socky project) and I believe that currently it’s the best implementation. So why don’t start from building similar server which just couple of lines instead of huge library?
#!/usr/bin/env ruby
require 'rubygems'
require 'eventmachine'
require 'libwebsocket'
module EchoServer
def receive_data(data)
@hs ||= LibWebSocket::OpeningHandshake::Server.new
@frame ||= LibWebSocket::Frame.new
if !@hs.done?
@hs.parse(data)
if @hs.done?
send_data(@hs.to_s)
end
return
end
@frame.append(data)
while message = @frame.next
send_data @frame.new(message).to_s
end
end
end
EventMachine::run do
EventMachine::start_server '0.0.0.0', 8080, EchoServer
puts "Started EchoServer on #{host}:#{port}..."
end
And that’s all! Of course it’s just simplified implementation, but I think that shows what I’m talking about. No more handling events, checking version and implementing multiple drafts - it’s all inside! And together with that we can also build client:
Building client
This one is more tricky but still most work is handled by LibWebsocket:
require "socket"
require 'libwebsocket'
class WebSocket
def initialize(url, params = {})
@hs ||= LibWebSocket::OpeningHandshake::Client.new(:url => url,
:version => params[:version])
@frame ||= LibWebSocket::Frame.new
@socket = TCPSocket.new(@hs.url.host, @hs.url.port || 80)
@socket.write(@hs.to_s)
@socket.flush
loop do
data = @socket.getc
next if data.nil?
result = @hs.parse(data.chr)
raise @hs.error unless result
if @hs.done?
@handshaked = true
break
end
end
end
def send(data)
raise "no handshake!" unless @handshaked
data = @frame.new(data).to_s
@socket.write data
@socket.flush
end
def receive
raise "no handshake!" unless @handshaked
data = @socket.gets("\xff")
@frame.append(data)
messages = []
while message = @frame.next
messages << message
end
messages
end
end
This will allow to connecting, sending and receiving messages from WebSocket server in drafts #75 and #76(more vesions comming)
State and future
LibWebSocket is currently in very early state of development but I hope that it will move fast to stable version. You can now install it as gem and start using:
gem install libwebsocket
See project at github: https://github.com/imanel/libwebsocket.