Как обслуживать Cramp::Websocket и обычное приложение Rack на одном порту?

Я пытаюсь использовать тот же порт для обслуживания обычного HTTP-трафика, а также веб-сокет HTML5 через Cramp (который построен поверх EventMachine), используя Ruby 1.9.3 и Thin 1.3.1. Вот минимальный, автономный пример:

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

Если вы хотите попробовать это сами, вставьте этот код в файл с именем config.ru и беги thin start, Вам нужны драгоценные камни thin, cramp а также http_router быть установленным.

Идея состоит в том, что код JavaScript устанавливает соединение WebSocket с ws://localhost:3000/socket, который повторяет сообщения, отправленные на него, но это не работает, как предполагалось. open событие отправляется, ошибки при отправке сообщения нет, но мы никогда не получаем ответ.

С точки зрения сервера, соединение не было установлено, так как Client connected сообщение не печатается.

С помощью thin start -DЯ вижу, что происходит HTTP 101, и происходит обмен некоторыми двоичными данными.

Что я делаю неправильно?

Обновление: если я разделю файл на две части, HttpRouterи запустить два thin экземпляры на разных портах, это все еще не работает. Так что проблема в коде сокета, а не в HttpRouter или WebApp,

1 ответ

Решение

Что ж, это обман, но я наконец решил это, переключившись на другую библиотеку: websocket-rack. Для любопытных исправленный код выглядит следующим образом:

require 'thin'
require 'http_router'
require 'rack/websocket'

class SocketApp < Rack::WebSocket::Application
  def on_open(env)
    puts 'Client connected'
  end
  def on_close(env)
    puts 'Client disconnected'
  end
  def on_message(env, message)
    puts "Got message: #{message}"
    send_data '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 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.new)
  add('/').to(WebApp.new)
end

run app
Другие вопросы по тегам