I'm trying to use the same port to serve normal HTTP traffic as well as an HTML5 websocket via Cramp (which is built on top of EventMachine), using Ruby 1.9.3 and Thin 1.3.1. Here is a minimal, self-contained example:
require 'thin'
require 'cramp'
require 'http_router'
Cramp::Websocket.backend = :thin
class SocketApp < Cramp::Action
self.transport = :websocket
on_start = :connected
on_finish = :disconnected
on_data = :message
def connected
puts 'Client connected'
end
def disconnected
puts 'Client disconnected'
end
def message(msg)
puts "Got message: #{msg}"
render 'Here is your reply'
end
end
class WebApp
def call(env)
[ 200, { 'Content-Type' => 'text/html' }, <<EOF
<html><head>
<script>
function init() {
function log(msg) { document.getElementById('log').innerHTML += msg + '<br>'; }
var socketUri = 'ws://' + document.location.host + '/socket';
log('Socket URI: ' + socketUri);
var socket = new WebSocket(socketUri);
socket.onopen = function(e) {
log('onopen');
socket.send('Is there anybody out there?');
log('sent message');
};
socket.onclose = function(e) {
log('onclose; code = ' + e.code + ', reason = ' + e.reason);
};
socket.onerror = function(e) {
log('onerror');
};
socket.onmessage = function(e) {
log('onmessage; data = ' + e.data);
};
}
</script>
</head><body onload='init();'>
<h1>Serving Cramp::Websocket and normal Rack app on the same port</h1>
<p id='log'></p>
</body></html>
EOF
]
end
end
app = HttpRouter.new do
add('/socket').to SocketApp
add('/').to WebApp.new
end
run app
If you want to try this for yourself, stick this code into a file named config.ru
and run thin start
. You need the gems thin
, cramp
and http_router
to be installed.
The idea is that the JavaScript code makes a WebSocket connection to ws://localhost:3000/socket
, which echoes messages sent to it, but this does not work as intended. The open
event fires, there is no error upon sending the message, but we never get a response.
From the server's point of view, no connection has been made, as the Client connected
message does not get printed.
Using thin start -D
, I can see the HTTP 101 happening, and some binary data is being exchanged.
What am I doing wrong?
Update: If I split the file in two parts, rip out the HttpRouter
, and run two thin
instances on different ports, it still doesn't work. So the problem is in the socket code, not in the HttpRouter
or the WebApp
.
Well, this is cheating, but I finally solved it by switching to a different library: websocket-rack. For the curious, the corrected code follows: