Websocket Handshake with Ruby and Em::Websocket::Server

WebSocket handshake with Ruby and EM::WebSocket::Server

  1. I think that once the connection is open the request and response have already occurred, so sending headers at that point is too late. In addition, headers have to end with a blank line, which you omitted.

  2. According to the demos, you don't even have to set headers in the client or the server--the ruby module automatically takes care of the headers on the server side, and html5 automatically takes care of the headers on the client side. I think this should work:

    require "em-websocket-server"

    class EchoServer < EM::WebSocket::Server

    def on_connect
    EM::WebSocket::Log.debug "Connected"
    puts "I felt a connection."
    end

    def on_receive msg
    puts "RECEIVED: #{msg}"
    send_message msg
    end

    end

    EM.run do
    myhost = "0.0.0.0"
    myport = 8000
    puts "Starting WebSocket server. Listening on port #{myport}..."
    EM.start_server myhost, myport, EchoServer
    end

html file:

<!DOCTYPE html> <html> <head><title>Test</title>

<script type="text/javascript">

var myWebSocket = new WebSocket("ws://localhost:8000");

myWebSocket.onopen = function(evt) {
console.log("Connection open. Sending message...");
myWebSocket.send("Hello WebSockets!"); };

myWebSocket.onmessage = function(evt) {
console.log(evt.data);
myWebSocket.close(); };

myWebSocket.onclose = function(evt) {
console.log("Connection closed."); };

myWebSocket.onerror = function(err) {
alert(err.name + " => " + err.message); } </script>

</head> <body> <div>Hello</div> </body> </html>

And it does work in Safari 5.1.9 (which is an older browser): I see the expected output on both the server and the client. However, the code does not work in Firefox 21: I get the error message...

Firefox can't establish a connection to the server at ws://localhost:8000/.
var myWebSocket = new WebSocket("ws://localhost:8000");

I notice that in both Firebug and Safari Developer Tools, the server does not send a Sec-WebSocket-Accept header:

Response Headers

Connection Upgrade
Upgrade WebSocket
WebSocket-Location ws://localhost:8000/
WebSocket-Origin null

Request Headers

Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding gzip, deflate
Accept-Language en-US,en;q=0.5
Cache-Control no-cache
Connection keep-alive, Upgrade
DNT 1
Host localhost:8000
Origin null
Pragma no-cache
Sec-WebSocket-Key r9xT+ywe533EHF09wxelkg==
Sec-WebSocket-Version 13
Upgrade websocket
User-Agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:21.0) Gecko/20100101 Firefox/21.0

Nothing I tried would make the code work in Firefox 21.0. To check whether Firefox 21.0 even supports websockets, I went to:

http://www.websocket.org/echo.html  

and it said my browser does support websockets.


  1. Is there any reason you have to use the em-websocket-server module? The last modification for that module on github was three years ago. And whenever you see require rubygems in ruby code, that should alert you that the code is old. I tried the newer em-websocket module, and I was able to successfully transfer data back and forth using websockets on both Firefox 21.0 and Safari 5.1.9:

    require 'em-websocket'

    myhost = "0.0.0.0"
    myport = 8000

    EM.run {
    puts "Listening on port #{myport}..."

    EM::WebSocket.run(:host => myhost, :port => myport, :debug => false) do |ws|

     ws.onopen do |handshake|
    path = handshake.path
    query_str = handshake.query
    origin = handshake.origin

    puts "WebSocket opened:"
    puts "\t path \t\t -> #{path}"
    puts "\t query_str \t -> #{query_str}"
    puts "\t origin \t -> #{origin}"
    end

    ws.onmessage { |msg|
    ws.send "Pong: #{msg}"
    }
    ws.onclose {
    puts "WebSocket closed"
    }
    ws.onerror { |e|
    puts "Error: #{e.message}"
    }

    end
    }

Same client side code. Now the response headers include Sec-WebSocket-Accept:

Response Headers

Connection Upgrade
Sec-WebSocket-Accept LyIm6d+kAAqkcTR744tVK9HMepY=
Upgrade websocket

Request Headers

Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding gzip, deflate
Accept-Language en-US,en;q=0.5
Cache-Control no-cache
Connection keep-alive, Upgrade
DNT 1
Host localhost:8000
Origin null
Pragma no-cache
Sec-WebSocket-Key pbK8lFHQAF+arl9tFvHn/Q==
Sec-WebSocket-Version 13
Upgrade websocket
User-Agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:21.0) Gecko/20100101 Firefox/21.0

In your code, I don't think you are setting any headers. Instead, you are just sending messages back and forth that happen to contain characters that look like headers. Apparently, your browser requires the Sec-WebSocket-Accept header in the response before it will allow the connection, and when the em-websocket-server module fails to set that header in the response, your browser refuses the connection.

The relevant source code for em-websockets-server looks like this:

module EM
module WebSocket
module Protocol
module Version76

# generate protocol 76 compatible response headers
def response
response = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
response << "Upgrade: WebSocket\r\n"
response << "Connection: Upgrade\r\n"
response << "Sec-WebSocket-Origin: #{origin}\r\n"
response << "Sec-WebSocket-Location: #{scheme}://#{host}#{path}\r\n"

if protocol
response << "Sec-WebSocket-Protocol: #{protocol}\r\n"
end

response << "\r\n"
response << Digest::MD5.digest(keyset)

response
end

As you can see, it doesn't set the Sec-WebSocket-Accept header. That code is in a module called Version76, and searching google for websockets version 76 yields an obsolete protocol(which contains an example of a request and response):

https://datatracker.ietf.org/doc/html/draft-hixie-thewebsocketprotocol-76

Here is the current websockets protocol(which also contains an example of a request and response):

https://www.rfc-editor.org/rfc/rfc6455

Conclusion: em-websockets-server is obsolete.

websocket-rails, websocket handshake error

Thanks to the members of the Websocket Rails IRC, I found out that in my route, I was appending the locale before every path, so it coudln't find it.

Check your routes.rb if someone hit that issue too !

Websockets with Rails(Puma) - Error during WebSocket handshake: Unexpected response code: 200

Try this in your console:

window.dispatcher = new WebSocketRails window.document.location.host + '/websocket'

You configure the rest of what you want to do in the config/events.rb file and whatever controllers you use to handle the events

Any success with Sinatra working together with EventMachine WebSockets?

Did not try it, but should not be too hard:

require 'em-websocket'
require 'sinatra/base'
require 'thin'

EM.run do
class App < Sinatra::Base
# Sinatra code here
end

EM::WebSocket.start(:host => '0.0.0.0', :port => 3001) do
# Websocket code here
end

# You could also use Rainbows! instead of Thin.
# Any EM based Rack handler should do.
Thin::Server.start App, '0.0.0.0', 3000
end

Also, Cramp has a websocket implementation that works directly with Thin/Rainbows! you might be able to extract, so you won't even need to run the server on another port.

Using em-websocket gem can't change connection to 'ws://' RUBY

EventMachine is listening on port 8000:
EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8000) but you are trying to connect to port 3000: ws = new WebSocket("ws://localhost:3000/home/webSocket");

Change it to connect to port 8000:

ws = new WebSocket("ws://localhost:8000/home/webSocket");

Although the additional path is not needed unless you specifically want to pass /home/webSocket to EventMachine.

broadcasting data with event machine websocket

You asking for a basic chat server?

Just store the connections in a list (or hash).
People tend to include in a hash to make it easier to remove them
If this is in a class, use @connections instead of $connections

GL

$connections = {}
EventMachine::WebSocket.run(:host => "0.0.0.0", :port => 8080) do |ws|
ws.onopen
$connections[ws] = true
end
ws.onclose do
$connections.delete(ws)
end
ws.onmessage do |msg|
$connections.each { |c, b| c.send msg }
end
end


Related Topics



Leave a reply



Submit