diff --git a/Gemfile b/Gemfile
index 98123f722d..e5ecd9b8c5 100644
--- a/Gemfile
+++ b/Gemfile
@@ -41,7 +41,8 @@ if RUBY_ENGINE == "ruby"
gem 'puma'
gem 'yajl-ruby'
gem 'nokogiri'
- gem 'puma'
+ gem 'rainbows'
+ gem 'eventmachine'
gem 'slim', '~> 2.0'
gem 'coffee-script', '>= 2.0'
gem 'rdoc'
diff --git a/README.md b/README.md
index 0ef30a4bc9..2db8ba5efc 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,7 @@ The code you changed will not take effect until you restart the server.
Please restart the server every time you change or use
[sinatra/reloader](http://www.sinatrarb.com/contrib/reloader).
-It is recommended to also run `gem install thin`, which Sinatra will
+It is recommended to also run `gem install puma`, which Sinatra will
pick up if available.
## Table of Contents
@@ -1685,36 +1685,53 @@ to `stream` finishes executing. Streaming does not work at all with Shotgun.
If the optional parameter is set to `keep_open`, it will not call `close` on
the stream object, allowing you to close it at any later point in the
-execution flow. This only works on evented servers, like Thin and Rainbows.
+execution flow. This only works on evented servers, like Rainbows.
Other servers will still close the stream:
```ruby
-# long polling
-
-set :server, :thin
-connections = []
+# config.ru
+require 'sinatra/base'
-get '/subscribe' do
- # register a client's interest in server events
- stream(:keep_open) do |out|
- connections << out
- # purge dead connections
- connections.reject!(&:closed?)
+class App < Sinatra::Base
+ connections = []
+
+ get '/subscribe' do
+ # register a client's interest in server events
+ stream(:keep_open) do |out|
+ connections << out
+ # purge dead connections
+ connections.reject!(&:closed?)
+ end
end
-end
-post '/:message' do
- connections.each do |out|
- # notify client that a new message has arrived
- out << params['message'] << "\n"
+ post '/:message' do
+ connections.each do |out|
+ # notify client that a new message has arrived
+ out << params['message'] << "\n"
- # indicate client to connect again
- out.close
+ # indicate client to connect again
+ out.close
+ end
+
+ # acknowledge
+ "message received"
end
+end
+
+run App
+```
- # acknowledge
- "message received"
+```ruby
+# rainbows.conf
+Rainbows! do
+ use :EventMachine
end
+````
+
+Run:
+
+```shell
+rainbows -c rainbows.conf
```
It's also possible for the client to close the connection when trying to
@@ -2377,7 +2394,7 @@ set :protection, :session => true
If you are using a WEBrick web server, presumably for your development
environment, you can pass a hash of options to server_settings,
such as SSLEnable or SSLVerifyClient. However, web
- servers such as Puma and Thin do not support this, so you can set
+ servers such as Puma do not support this, so you can set
server_settings by defining it as a method when you call
configure.
@@ -2428,7 +2445,7 @@ set :protection, :session => true
threaded
- If set to true, will tell Thin to use
+ If set to true, will tell server to use
EventMachine.defer for processing the request.
@@ -3017,7 +3034,7 @@ Options are:
-p # set the port (default is 4567)
-o # set the host (default is 0.0.0.0)
-e # set the environment (default is development)
--s # specify rack server/handler (default is thin)
+-s # specify rack server/handler (default is puma)
-q # turn on quiet mode for server (default is off)
-x # turn on the mutex lock (default is off)
```
@@ -3029,15 +3046,15 @@ _Paraphrasing from
by Konstantin_
Sinatra doesn't impose any concurrency model, but leaves that to the
-underlying Rack handler (server) like Thin, Puma or WEBrick. Sinatra
+underlying Rack handler (server) like Puma or WEBrick. Sinatra
itself is thread-safe, so there won't be any problem if the Rack handler
uses a threaded model of concurrency. This would mean that when starting
the server, you'd have to specify the correct invocation method for the
specific Rack handler. The following example is a demonstration of how
-to start a multi-threaded Thin server:
+to start a multi-threaded Rainbows server:
```ruby
-# app.rb
+# config.ru
require 'sinatra/base'
@@ -3047,14 +3064,22 @@ class App < Sinatra::Base
end
end
-App.run!
+run App
+```
+```ruby
+# rainbows.conf
+
+# Rainbows configurator is based on Unicorn.
+Rainbows! do
+ use :ThreadSpawn
+end
```
To start the server, the command would be:
```shell
-thin --threaded start
+rainbows -c rainbows.conf
```
## Requirement
diff --git a/examples/chat.rb b/examples/chat.rb
index 8e47b7d1b3..a3f1ab6668 100755
--- a/examples/chat.rb
+++ b/examples/chat.rb
@@ -1,7 +1,7 @@
#!/usr/bin/env ruby -I ../lib -I lib
# coding: utf-8
require 'sinatra'
-set :server, 'thin'
+set :server, 'puma'
connections = []
get '/' do
diff --git a/examples/rainbows.conf b/examples/rainbows.conf
new file mode 100644
index 0000000000..31742e961b
--- /dev/null
+++ b/examples/rainbows.conf
@@ -0,0 +1,3 @@
+Rainbows! do
+ use :EventMachine
+end
diff --git a/examples/stream.ru b/examples/stream.ru
index 074c66b99c..74af0a6148 100644
--- a/examples/stream.ru
+++ b/examples/stream.ru
@@ -2,10 +2,10 @@
#
# run *one* of these:
#
-# rackup -s mongrel stream.ru # gem install mongrel
-# thin -R stream.ru start # gem install thin
-# unicorn stream.ru # gem install unicorn
-# puma stream.ru # gem install puma
+# rackup -s mongrel stream.ru # gem install mongrel
+# unicorn stream.ru # gem install unicorn
+# puma stream.ru # gem install puma
+# rainbows -c rainbows.conf stream.ru # gem install rainbows eventmachine
require 'sinatra/base'
diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb
index 28be7b424a..18665885fc 100644
--- a/lib/sinatra/base.rb
+++ b/lib/sinatra/base.rb
@@ -206,7 +206,7 @@ def drop_body?
end
end
- # Some Rack handlers (Thin, Rainbows!) implement an extended body object protocol, however,
+ # Some Rack handlers (Rainbows!) implement an extended body object protocol, however,
# some middleware (namely Rack::Lint) will break it by not mirroring the methods in question.
# This middleware will detect an extended body object and will make sure it reaches the
# handler directly. We do this here, so our middleware and middleware set up by the app will
@@ -473,7 +473,7 @@ def closed?
#
# The close parameter specifies whether Stream#close should be called
# after the block has been executed. This is only relevant for evented
- # servers like Thin or Rainbows.
+ # servers like Rainbows.
def stream(keep_open = false)
scheduler = env['async.callback'] ? EventMachine : Stream
current = @params.dup
@@ -1475,8 +1475,7 @@ def use(middleware, *args, &block)
# Stop the self-hosted server if running.
def quit!
return unless running?
- # Use Thin's hard #stop! if available, otherwise just #stop.
- running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop
+ running_server.stop
$stderr.puts "== Sinatra has ended his set (crowd applauds)" unless suppress_messages?
set :running_server, nil
set :handler_name, nil
@@ -1485,7 +1484,7 @@ def quit!
alias_method :stop!, :quit!
# Run the Sinatra app as a self-hosted server using
- # Thin, Puma, Mongrel, or WEBrick (in that order). If given a block, will call
+ # Puma, Mongrel, or WEBrick (in that order). If given a block, will call
# with the constructed handler once we have taken the stage.
def run!(options = {}, &block)
return if running?
@@ -1849,7 +1848,6 @@ class << self
server.unshift 'reel'
server.unshift 'puma'
server.unshift 'mongrel' if ruby_engine.nil?
- server.unshift 'thin' if ruby_engine != 'jruby'
server.unshift 'trinidad' if ruby_engine == 'jruby'
end
diff --git a/lib/sinatra/main.rb b/lib/sinatra/main.rb
index d6f9f9a524..411af380b4 100644
--- a/lib/sinatra/main.rb
+++ b/lib/sinatra/main.rb
@@ -5,7 +5,7 @@ module Sinatra
require 'optparse'
parser = OptionParser.new { |op|
op.on('-p port', 'set the port (default is 4567)') { |val| ParamsConfig[:port] = Integer(val) }
- op.on('-s server', 'specify rack server/handler (default is thin)') { |val| ParamsConfig[:server] = val }
+ op.on('-s server', 'specify rack server/handler (default is puma)') { |val| ParamsConfig[:server] = val }
op.on('-q', 'turn on quiet mode (default is off)') { ParamsConfig[:quiet] = true }
op.on('-x', 'turn on the mutex lock (default is off)') { ParamsConfig[:lock] = true }
op.on('-e env', 'set the environment (default is development)') do |val|
diff --git a/test/integration/app.rb b/test/integration/app.rb
index 3e87a00aaf..97884f852d 100644
--- a/test/integration/app.rb
+++ b/test/integration/app.rb
@@ -1,6 +1,8 @@
$stderr.puts "loading"
require 'sinatra'
+require_relative 'rainbows' if RUBY_ENGINE == 'ruby'
+
configure do
set :foo, :bar
end
diff --git a/test/integration/rainbows.conf b/test/integration/rainbows.conf
new file mode 100644
index 0000000000..31742e961b
--- /dev/null
+++ b/test/integration/rainbows.conf
@@ -0,0 +1,3 @@
+Rainbows! do
+ use :EventMachine
+end
diff --git a/test/integration/rainbows.rb b/test/integration/rainbows.rb
new file mode 100644
index 0000000000..7a93275d78
--- /dev/null
+++ b/test/integration/rainbows.rb
@@ -0,0 +1,20 @@
+require 'rainbows'
+
+module Rack
+ module Handler
+ class Rainbows
+ def self.run(app, **options)
+ rainbows_options = {
+ listeners: ["#{options[:Host]}:#{options[:Port]}"],
+ worker_processes: 1,
+ timeout: 30,
+ config_file: ::File.expand_path('../rainbows.conf', __FILE__),
+ }
+
+ ::Rainbows::HttpServer.new(app, rainbows_options).start.join
+ end
+ end
+
+ register "rainbows", "Rack::Handler::Rainbows"
+ end
+end
diff --git a/test/integration_async_helper.rb b/test/integration_async_helper.rb
new file mode 100644
index 0000000000..155a627c11
--- /dev/null
+++ b/test/integration_async_helper.rb
@@ -0,0 +1,14 @@
+require File.expand_path('../integration_helper', __FILE__)
+
+module IntegrationAsyncHelper
+ def it(message, &block)
+ base_port = 5100 + Process.pid % 100
+
+ %w(rainbows).each_with_index do |server_name, index|
+ server = IntegrationHelper::BaseServer.new(server_name, base_port + index)
+ next unless server.installed?
+
+ super("with #{server.name}: #{message}") { server.run_test(self, &block) }
+ end
+ end
+end
diff --git a/test/integration_async_test.rb b/test/integration_async_test.rb
new file mode 100644
index 0000000000..79397baab3
--- /dev/null
+++ b/test/integration_async_test.rb
@@ -0,0 +1,40 @@
+require File.expand_path('../helper', __FILE__)
+require File.expand_path('../integration_async_helper', __FILE__)
+
+# These tests are like integration_test, but they test asynchronous streaming.
+class IntegrationAsyncTest < Minitest::Test
+ extend IntegrationAsyncHelper
+ attr_accessor :server
+
+ it 'streams async' do
+ Timeout.timeout(3) do
+ chunks = []
+ server.get_stream '/async' do |chunk|
+ next if chunk.empty?
+ chunks << chunk
+ case chunk
+ when "hi!" then server.get "/send?msg=hello"
+ when "hello" then server.get "/send?close=1"
+ end
+ end
+
+ assert_equal ['hi!', 'hello'], chunks
+ end
+ end
+
+ it 'streams async from subclass' do
+ Timeout.timeout(3) do
+ chunks = []
+ server.get_stream '/subclass/async' do |chunk|
+ next if chunk.empty?
+ chunks << chunk
+ case chunk
+ when "hi!" then server.get "/subclass/send?msg=hello"
+ when "hello" then server.get "/subclass/send?close=1"
+ end
+ end
+
+ assert_equal ['hi!', 'hello'], chunks
+ end
+ end
+end
diff --git a/test/integration_helper.rb b/test/integration_helper.rb
index aa2770072b..b3b2c7c5be 100644
--- a/test/integration_helper.rb
+++ b/test/integration_helper.rb
@@ -61,7 +61,7 @@ def command
file, dir = RbConfig::CONFIG.values_at('ruby_install_name', 'bindir')
cmd << File.expand_path(file, dir).inspect
end
- cmd << "-w" unless thin? || net_http_server?
+ cmd << "-w" unless net_http_server?
cmd << "-I" << File.expand_path('../../lib', __FILE__).inspect
cmd << app_file.inspect << '-s' << server << '-o' << '127.0.0.1' << '-p' << port
cmd << "-e" << environment.to_s << '2>&1'
@@ -73,8 +73,8 @@ def webrick?
name.to_s == "webrick"
end
- def thin?
- name.to_s == "thin"
+ def rainbows?
+ name.to_s == "rainbows"
end
def puma?
diff --git a/test/integration_test.rb b/test/integration_test.rb
index 1e9f41b5a4..9c6177c69f 100644
--- a/test/integration_test.rb
+++ b/test/integration_test.rb
@@ -13,7 +13,7 @@ class IntegrationTest < Minitest::Test
it('only extends main') { assert_equal "true", server.get("/mainonly") }
it 'logs once in development mode' do
- next if server.puma? or RUBY_ENGINE == 'jruby'
+ next if server.puma? or server.rainbows? or RUBY_ENGINE == 'jruby'
random = "%064x" % Kernel.rand(2**256-1)
server.get "/ping?x=#{random}"
count = server.log.scan("GET /ping?x=#{random}").count
@@ -39,42 +39,6 @@ class IntegrationTest < Minitest::Test
assert times[2] - times[1] > 1
end
- it 'streams async' do
- next unless server.thin?
-
- Timeout.timeout(3) do
- chunks = []
- server.get_stream '/async' do |chunk|
- next if chunk.empty?
- chunks << chunk
- case chunk
- when "hi!" then server.get "/send?msg=hello"
- when "hello" then server.get "/send?close=1"
- end
- end
-
- assert_equal ['hi!', 'hello'], chunks
- end
- end
-
- it 'streams async from subclass' do
- next unless server.thin?
-
- Timeout.timeout(3) do
- chunks = []
- server.get_stream '/subclass/async' do |chunk|
- next if chunk.empty?
- chunks << chunk
- case chunk
- when "hi!" then server.get "/subclass/send?msg=hello"
- when "hello" then server.get "/subclass/send?close=1"
- end
- end
-
- assert_equal ['hi!', 'hello'], chunks
- end
- end
-
it 'starts the correct server' do
exp = %r{
==\sSinatra\s\(v#{Sinatra::VERSION}\)\s
@@ -83,7 +47,7 @@ class IntegrationTest < Minitest::Test
}ix
# because Net HTTP Server logs to $stderr by default
- assert_match exp, server.log unless server.net_http_server? || server.reel?
+ assert_match exp, server.log unless server.net_http_server? || server.reel? || server.rainbows?
end
it 'does not generate warnings' do
diff --git a/test/settings_test.rb b/test/settings_test.rb
index 6ba8d2bfaa..bd4e9e1c16 100644
--- a/test/settings_test.rb
+++ b/test/settings_test.rb
@@ -488,12 +488,6 @@ def pub; end
assert @base.server.include?('puma')
assert @application.server.include?('puma')
end
-
- it 'includes thin' do
- next if RUBY_ENGINE == 'jruby'
- assert @base.server.include?('thin')
- assert @application.server.include?('thin')
- end
end
describe 'app_file' do