I am trying to create a simple WebSocket connection in JavaScript against my Rails app. I get the following:
WebSocket connection to 'ws://localhost:4000/' failed: Error during WebSocket handshake: 'Sec-WebSocket-Accept' header is missing
What am I doing wrong? Here is my code:
JavaScript:
var socket = new WebSocket('ws://localhost:4000');
socket.onopen = function() {
var handshake =
"GET / HTTP/1.1\n" +
"Host: localhost\n" +
"Upgrade: websocket\n" +
"Connection: Upgrade\n" +
"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\n" +
"Sec-WebSocket-Protocol: quote\n" +
"Sec-WebSocket-Version: 13\n" +
"Origin: http://localhost\n";
socket.send(handshake);
};
socket.onmessage = function(data) {
console.log(data);
};
Ruby:
require 'rubygems'
require 'em-websocket-server'
module QuoteService
class WebSocket < EventMachine::WebSocket::Server
def on_connect
handshake_response = "HTTP/1.1 101 Switching Protocols\n"
handshake_response << "Upgrade: websocket\n"
handshake_response << "Connection: Upgrade\n"
handshake_response << "Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=\n"
handshake_response << "Sec-WebSocket-Protocol: quote\n"
send_message(handshake_response)
end
def on_receive(data)
puts 'RECEIVED: ' + data
end
end
end
EventMachine.run do
print 'Starting WebSocket server...'
EventMachine.start_server '0.0.0.0', 4000, QuoteService::WebSocket
puts 'running'
end
The handshake headers are per Wikipedia.
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:
html file:
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...
I notice that in both Firebug and Safari Developer Tools, the server does not send a Sec-WebSocket-Accept header:
Nothing I tried would make the code work in Firefox 21.0. To check whether Firefox 21.0 even supports websockets, I went to:
and it said my browser does support websockets.
3) 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:Same client side code. Now the response headers include Sec-WebSocket-Accept:
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:
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):
http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
Here is the current websockets protocol(which also contains an example of a request and response):
http://tools.ietf.org/html/rfc6455
Conclusion: em-websockets-server is obsolete.