Sort our server/ruby compatibility. Now running on Puma, Thin, old and new Rainbows, and Goliath, on MRI/JRuby/RBX.

This commit is contained in:
James Coglan
2013-05-03 09:06:48 +01:00
parent 92fd9dd5a9
commit 7d4d153702
11 changed files with 118 additions and 64 deletions
+5 -1
View File
@@ -1,4 +1,5 @@
language: ruby
rvm:
- 1.8.7
- 1.9.2
@@ -6,10 +7,13 @@ rvm:
- 2.0.0
- jruby-18mode
- jruby-19mode
- rbx-18mode
- rbx-19mode
before_script:
- git submodule update --init --recursive
- bundle install
- cd vendor/parser && rake compile
script: bundle exec rspec spec/
script: bundle exec rspec -c spec/
+46 -16
View File
@@ -19,9 +19,12 @@ access via proxies than WebSockets.
Currently, the following web servers are supported, and can be accessed directly
or via HAProxy:
* {Thin}[http://code.macournoyer.com/thin/]
* {Rainbows}[http://rainbows.rubyforge.org/] using EventMachine
* {Goliath}[http://postrank-labs.github.com/goliath/]
* {Puma}[http://puma.io/]
* {Rainbows}[http://rainbows.rubyforge.org/] using EventMachine
* {Thin}[http://code.macournoyer.com/thin/]
Any web server that supports the <tt>rack.hijack</tt> API should also work.
The server-side socket can process {draft-75}[http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75],
{draft-76}[http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76],
@@ -31,6 +34,11 @@ supports both +text+ and +binary+ messages, and transparently handles +ping+,
+pong+, +close+ and fragmented messages.
== Installation
$ gem install faye-websocket
== Handling WebSocket connections in Rack
You can handle WebSockets on the server side by listening for requests using the
@@ -65,10 +73,14 @@ and sending messages. For example this is how you'd implement an echo server:
This is a standard Rack app, so it can be run using a <tt>config.ru</tt> file.
However, so that incoming requests can be properly prepared to process WebSocket
connections, you need to tell <tt>Faye::WebSocket</tt> which adapter to load;
this can be either +thin+, +rainbows+ or +goliath+. If one of these servers is
already loaded before <tt>faye/websocket</tt> is loaded, it will load
appropriate adapters automatically.
connections, <tt>Faye::WebSocket</tt> needs to monkey-patch certain web
servers. If you're using one of these servers, you need to load an adapter:
* Goliath
* Rainbows! version < 4.5.0
* Thin
You load the adapter like so, passing either +goliath+, +rainbows+ or +thin+.
# config.ru
require './app'
@@ -261,6 +273,8 @@ further in a block passed to +run+:
require 'thin'
require './app'
Faye::WebSocket.load_adapter('thin')
EM.run {
thin = Rack::Handler.get('thin')
@@ -275,18 +289,19 @@ further in a block passed to +run+:
}
=== Running the app with Puma
Puma has a command line interface for starting your application:
puma config.ru -p 9292
You do not need to call <tt>Faye::WebSocket.load_adapter</tt> to work with Puma
but you must use at least version 2.0.0 of the +puma+ gem.
=== Running the app with Rainbows
<tt>Faye::WebSocket</tt> can only be run using EventMachine. To begin with,
you'll need a Rainbows config file that tells it to use EventMachine, along with
whatever Rainbows/Unicorn configuration you require.
# rainbows.conf
Rainbows! do
use :EventMachine
end
You can then run your <tt>config.ru</tt> file from the command line. Again,
You can run your <tt>config.ru</tt> file from the command line. Again,
<tt>Rack::Lint</tt> will complain unless you put the application in production
mode.
@@ -308,6 +323,19 @@ Rainbows also has a Ruby API for starting a server:
# This is non-blocking; use server.start.join to block
server.start
If you're using version 4.4 or lower of Rainbows, you need to run it with the
EventMachine backend and enable the adapter. Put this in your
<tt>rainbows.conf</tt> file:
# rainbows.conf
Rainbows! do
use :EventMachine
end
And make sure you load the adapter in your application:
Faye::WebSocket.load_adapter('rainbows')
=== Running the app with Goliath
@@ -316,6 +344,7 @@ Goliath can be made to run arbitrary Rack apps by delegating to them from a
require 'goliath'
require './app'
Faye::WebSocket.load_adapter('goliath')
class EchoServer < Goliath::API
def response(env)
@@ -327,6 +356,7 @@ Goliath can be made to run arbitrary Rack apps by delegating to them from a
require 'goliath'
require 'faye/websocket'
Faye::WebSocket.load_adapter('goliath')
class EchoServer < Goliath::API
def response(env)
+4 -8
View File
@@ -1,17 +1,13 @@
# Run using your favourite server:
#
# thin start -R examples/config.ru -p 7000
# rainbows -E production examples/config.ru -p 7000
#
# If you run using one of these commands, the webserver is loaded before this
# file, so Faye::WebSocket can figure out which adapter to load. If instead you
# run using `rackup`, you need the `load_adapter` line below.
#
# rackup -E production -s thin examples/config.ru -p 7000
# rainbows -c examples/rainbows.conf -E production examples/config.ru -p 7000
require 'rubygems'
require File.expand_path('../app', __FILE__)
# Faye::WebSocket.load_adapter('thin')
Faye::WebSocket.load_adapter('thin')
Faye::WebSocket.load_adapter('rainbows')
run App
+2 -5
View File
@@ -8,11 +8,7 @@ engine = ARGV[2] || 'thin'
spec = File.expand_path('../../spec', __FILE__)
require File.expand_path('../app', __FILE__)
if %w[goliath thin].include?(engine)
Faye::WebSocket.load_adapter(engine)
else
require engine
end
Faye::WebSocket.load_adapter(engine)
case engine
@@ -36,6 +32,7 @@ when 'rainbows'
rackup[:port] = port
rackup[:set_listener] = true
options = rackup[:options]
options[:config_file] = File.expand_path('../rainbows.conf', __FILE__)
Rainbows::HttpServer.new(App, options).start.join
when 'thin'
+20 -16
View File
@@ -1,30 +1,34 @@
Gem::Specification.new do |s|
s.name = "faye-websocket"
s.version = "0.4.7"
s.summary = "Standards-compliant WebSocket server and client"
s.author = "James Coglan"
s.email = "jcoglan@gmail.com"
s.homepage = "http://github.com/faye/faye-websocket-ruby"
s.name = 'faye-websocket'
s.version = '0.4.7'
s.summary = 'Standards-compliant WebSocket server and client'
s.author = 'James Coglan'
s.email = 'jcoglan@gmail.com'
s.homepage = 'http://github.com/faye/faye-websocket-ruby'
s.extra_rdoc_files = %w[README.rdoc]
s.rdoc_options = %w[--main README.rdoc]
s.require_paths = %w[lib]
s.files = %w[README.rdoc CHANGELOG.txt] +
Dir.glob("lib/**/*.rb") +
Dir.glob("{examples,spec}/**/*")
Dir.glob('lib/**/*.rb') +
Dir.glob('{examples,spec}/**/*')
s.add_dependency "eventmachine", ">= 0.12.0"
s.add_dependency "websocket-protocol"
s.add_dependency 'eventmachine', '>= 0.12.0'
s.add_dependency 'websocket-protocol'
s.add_development_dependency "progressbar"
s.add_development_dependency "rack"
s.add_development_dependency "rspec"
s.add_development_dependency 'progressbar'
s.add_development_dependency 'puma', '>= 2.0.0'
s.add_development_dependency 'rack'
s.add_development_dependency 'rspec'
unless RUBY_PLATFORM =~ /java/
s.add_development_dependency "puma", ">= 2.0.0"
s.add_development_dependency "rainbows", ">= 4.5.0"
s.add_development_dependency "thin", ">= 1.2.0"
s.add_development_dependency 'rainbows', '~> 4.4.0'
s.add_development_dependency 'thin', '>= 1.2.0'
end
unless (defined?(RUBY_ENGINE) and RUBY_ENGINE =~ /rbx/) or RUBY_VERSION < '1.9'
s.add_development_dependency 'goliath'
end
end
+1 -1
View File
@@ -20,7 +20,7 @@ module Faye
@connection = socket_object.env['em.connection']
@stream_send = socket_object.env['stream.send']
if socket_object.env['rack.hijack?']
if socket_object.env['rack.hijack']
socket_object.env['rack.hijack'].call
@rack_hijack_io = socket_object.env['rack.hijack_io']
EventMachine.attach(@rack_hijack_io, Reader) do |reader|
+5 -3
View File
@@ -22,9 +22,10 @@ module Faye
autoload :Client, root + '/client'
ADAPTERS = {
'thin' => :Thin,
'goliath' => :Goliath,
'puma' => :Puma,
'rainbows' => :Rainbows,
'goliath' => :Goliath
'thin' => :Thin
}
def self.determine_url(env)
@@ -41,7 +42,8 @@ module Faye
def self.load_adapter(backend)
const = Kernel.const_get(ADAPTERS[backend]) rescue nil
require(backend) unless const
require File.expand_path("../adapters/#{backend}", __FILE__)
path = File.expand_path("../adapters/#{backend}.rb", __FILE__)
require(path) if File.file?(path)
end
def self.websocket?(env)
+17 -3
View File
@@ -3,6 +3,8 @@
require "spec_helper"
require "socket"
IS_JRUBY = (defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby')
WebSocketSteps = EM::RSpec.async_steps do
def server(port, backend, secure, &callback)
@server = EchoServer.new
@@ -77,13 +79,13 @@ WebSocketSteps = EM::RSpec.async_steps do
end
describe Faye::WebSocket::Client do
next if WebSocket::Protocol.jruby?
include WebSocketSteps
let(:port) { 4180 }
let(:protocols) { ["foo", "echo"] }
let(:protocols) { ["foo", "echo"] }
let(:plain_text_url) { "ws://0.0.0.0:#{port}/" }
let(:wrong_url) { "ws://0.0.0.0:9999/" }
let(:secure_url) { "wss://0.0.0.0:#{port}/" }
shared_examples_for "socket client" do
@@ -134,7 +136,19 @@ describe Faye::WebSocket::Client do
end
end
describe "with a Puma server" do
let(:socket_url) { plain_text_url }
let(:blocked_url) { wrong_url }
before { server port, :puma, false }
after { stop }
it_should_behave_like "socket client"
end
describe "with a plain-text Thin server" do
next if IS_JRUBY
let(:socket_url) { plain_text_url }
let(:blocked_url) { secure_url }
@@ -145,7 +159,7 @@ describe Faye::WebSocket::Client do
end
describe "with a secure Thin server" do
next if WebSocket::Protocol.rbx?
next if IS_JRUBY
let(:socket_url) { secure_url }
let(:blocked_url) { plain_text_url }
+17 -10
View File
@@ -4,11 +4,11 @@ require 'bundler/setup'
require File.expand_path('../../lib/faye/websocket', __FILE__)
require File.expand_path('../../vendor/em-rspec/lib/em-rspec', __FILE__)
require 'puma'
unless RUBY_PLATFORM =~ /java/
Faye::WebSocket.load_adapter('thin')
Thin::Logging.silent = true
require 'rainbows'
Unicorn::Configurator::DEFAULTS[:logger] = Logger.new(StringIO.new)
end
class EchoServer
@@ -20,15 +20,19 @@ class EchoServer
socket.rack_response
end
def log(*args)
end
def listen(port, backend, ssl = false)
case backend
when :rainbows
rackup = Unicorn::Configurator::RACKUP
rackup[:port] = port
rackup[:set_listener] = true
options = rackup[:options]
@server = Rainbows::HttpServer.new(self, options)
@server.start
when :puma
events = Puma::Events.new(StringIO.new, StringIO.new)
binder = Puma::Binder.new(events)
binder.parse(["tcp://0.0.0.0:#{port}"], self)
@server = Puma::Server.new(self, events)
@server.binder = binder
@server.run
when :thin
Rack::Handler.get('thin').run(self, :Port => port) do |s|
if ssl
@@ -44,7 +48,10 @@ class EchoServer
end
def stop
@server.stop
case @server
when Puma::Server then @server.stop(true)
else @server.stop
end
end
end