From b989bdaa455c61ab7d7c18330824111069251883 Mon Sep 17 00:00:00 2001 From: Ryuichi KAWAMATA Date: Sun, 26 Jul 2020 18:07:17 +0900 Subject: [PATCH 1/7] Use puma instead of thin in examples. --- examples/chat.rb | 2 +- examples/stream.ru | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) 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/stream.ru b/examples/stream.ru index 074c66b99c..8cec60cacb 100644 --- a/examples/stream.ru +++ b/examples/stream.ru @@ -3,7 +3,6 @@ # 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 From 559f01a295814f3de6847960705a3b24d5600aa1 Mon Sep 17 00:00:00 2001 From: Ryuichi KAWAMATA Date: Sun, 26 Jul 2020 18:10:00 +0900 Subject: [PATCH 2/7] Remove thin? and add rainbows? in integration_test. --- test/integration_helper.rb | 6 +++--- test/integration_test.rb | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) 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..befe6960b7 100644 --- a/test/integration_test.rb +++ b/test/integration_test.rb @@ -40,7 +40,7 @@ class IntegrationTest < Minitest::Test end it 'streams async' do - next unless server.thin? + next unless server.rainbows? Timeout.timeout(3) do chunks = [] @@ -58,7 +58,7 @@ class IntegrationTest < Minitest::Test end it 'streams async from subclass' do - next unless server.thin? + next unless server.rainbows? Timeout.timeout(3) do chunks = [] From 7cec4e9eb66254b58ff28454f8a1d4af7eb27f5b Mon Sep 17 00:00:00 2001 From: Ryuichi KAWAMATA Date: Sun, 26 Jul 2020 18:27:44 +0900 Subject: [PATCH 3/7] Remove thin in settings_test. --- test/settings_test.rb | 6 ------ 1 file changed, 6 deletions(-) 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 From 607346295924c75a86f12ecbad9430cfc430c032 Mon Sep 17 00:00:00 2001 From: Ryuichi KAWAMATA Date: Sun, 26 Jul 2020 18:12:18 +0900 Subject: [PATCH 4/7] Remove thin in lib. --- lib/sinatra/base.rb | 10 ++++------ lib/sinatra/main.rb | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) 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| From c0d4dd74183dd57d56e317def5d0b7433ee617bc Mon Sep 17 00:00:00 2001 From: Ryuichi KAWAMATA Date: Sun, 26 Jul 2020 20:00:48 +0900 Subject: [PATCH 5/7] Use puma or rainbows instead of thin in readme. --- README.md | 83 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 29 deletions(-) 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 From 965c8b9c39da2163592415f0056a196f31049b95 Mon Sep 17 00:00:00 2001 From: Ryuichi KAWAMATA Date: Tue, 4 Aug 2020 16:35:46 +0900 Subject: [PATCH 6/7] Create integration_async_test for to be enable test with rainbows --- Gemfile | 3 ++- test/integration/app.rb | 2 ++ test/integration/rainbows.conf | 3 +++ test/integration/rainbows.rb | 20 ++++++++++++++++ test/integration_async_helper.rb | 14 +++++++++++ test/integration_async_test.rb | 40 ++++++++++++++++++++++++++++++++ test/integration_test.rb | 40 ++------------------------------ 7 files changed, 83 insertions(+), 39 deletions(-) create mode 100644 test/integration/rainbows.conf create mode 100644 test/integration/rainbows.rb create mode 100644 test/integration_async_helper.rb create mode 100644 test/integration_async_test.rb 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/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_test.rb b/test/integration_test.rb index befe6960b7..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.rainbows? - - 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.rainbows? - - 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 From 7a964728bf86718b0a128e386797113a644c6c6e Mon Sep 17 00:00:00 2001 From: Ryuichi KAWAMATA Date: Sat, 8 Aug 2020 19:12:59 +0900 Subject: [PATCH 7/7] Run examples/stream.ru with rainbows. --- examples/rainbows.conf | 3 +++ examples/stream.ru | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 examples/rainbows.conf 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 8cec60cacb..74af0a6148 100644 --- a/examples/stream.ru +++ b/examples/stream.ru @@ -2,9 +2,10 @@ # # run *one* of these: # -# rackup -s mongrel stream.ru # gem install mongrel -# 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'