rainbows 0.96.0 → 0.97.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/FAQ +12 -7
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +1 -1
- data/README +3 -3
- data/lib/rainbows.rb +8 -16
- data/lib/rainbows/acceptor.rb +26 -0
- data/lib/rainbows/base.rb +1 -1
- data/lib/rainbows/const.rb +1 -1
- data/lib/rainbows/dev_fd_response.rb +4 -2
- data/lib/rainbows/event_machine.rb +4 -2
- data/lib/rainbows/fiber/rev.rb +2 -1
- data/lib/rainbows/fiber_pool.rb +2 -1
- data/lib/rainbows/fiber_spawn.rb +2 -1
- data/lib/rainbows/response.rb +1 -0
- data/lib/rainbows/response/body.rb +7 -1
- data/lib/rainbows/rev/client.rb +2 -1
- data/lib/rainbows/rev/core.rb +2 -2
- data/lib/rainbows/rev/heartbeat.rb +1 -1
- data/lib/rainbows/rev/master.rb +12 -17
- data/lib/rainbows/revactor.rb +1 -1
- data/lib/rainbows/thread_pool.rb +3 -2
- data/lib/rainbows/thread_spawn.rb +2 -1
- data/lib/rainbows/thread_timeout.rb +94 -0
- data/rainbows.gemspec +1 -1
- data/t/close-pipe-to_path-response.ru +30 -0
- data/t/t0016-onenine-encoding-is-tricky.sh +1 -1
- data/t/t0017-keepalive-timeout-zero.sh +43 -0
- data/t/t0032-close-pipe-to_path-response.sh +101 -0
- data/t/t9100-thread-timeout.sh +36 -0
- data/t/t9100.ru +9 -0
- data/t/t9101-thread-timeout-threshold.sh +62 -0
- data/t/t9101.ru +9 -0
- data/t/test_isolate.rb +1 -1
- metadata +18 -7
    
        data/FAQ
    CHANGED
    
    | @@ -2,7 +2,7 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            === Why is \Rainbows! a separate project from Unicorn?
         | 
| 4 4 |  | 
| 5 | 
            -
            \Rainbows is for the odd, corner-case requests that Unicorn is poorly
         | 
| 5 | 
            +
            \Rainbows! is for the odd, corner-case requests that Unicorn is poorly
         | 
| 6 6 | 
             
            suited for.  More scalable concurrency models introduce additional
         | 
| 7 7 | 
             
            complexity that Unicorn users and developers are uncomfortable with for
         | 
| 8 8 | 
             
            the common cases.
         | 
| @@ -52,9 +52,14 @@ solution even if nginx will always outperform it in raw throughput. | |
| 52 52 |  | 
| 53 53 | 
             
            === How do I support SSL?
         | 
| 54 54 |  | 
| 55 | 
            -
            If you need  | 
| 56 | 
            -
            Rack application, then  | 
| 57 | 
            -
             | 
| 55 | 
            +
            If you need streaming "rack.input" to do on-the-fly upload processing
         | 
| 56 | 
            +
            within your Rack application, then using an SSL proxy such as
         | 
| 57 | 
            +
            {Pound}[http://www.apsis.ch/pound/] or {Stunnel}[http://stunnel.org/] is
         | 
| 58 | 
            +
            required.  Pound has built-in X-Forwarded-For support while Stunnel
         | 
| 59 | 
            +
            requires a extra {patch}[http://haproxy.1wt.eu/download/patches/].
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            If you don't need streaming "rack.input", then nginx is a great HTTPS
         | 
| 62 | 
            +
            reverse proxy.
         | 
| 58 63 |  | 
| 59 64 | 
             
            Refer to the {Unicorn FAQ}[http://unicorn.bogomips.org/FAQ.html] on how
         | 
| 60 65 | 
             
            to ensure redirects go to "https://" URLs.
         | 
| @@ -77,15 +82,15 @@ to set RAILS_ENV. | |
| 77 82 | 
             
            For Rails 2.3.x, the following config.ru will work for you:
         | 
| 78 83 |  | 
| 79 84 | 
             
              ENV["RAILS_ENV"] ||= ENV["RACK_ENV"]
         | 
| 80 | 
            -
              require "config/environment"
         | 
| 85 | 
            +
              require "#{::File.expand_path('config/environment')}"
         | 
| 81 86 | 
             
              use Rails::Rack::Static
         | 
| 82 87 | 
             
              run ActionController::Dispatcher.new
         | 
| 83 88 |  | 
| 84 89 | 
             
            For older versions of Rails, the following config.ru will work:
         | 
| 85 90 |  | 
| 86 91 | 
             
              ENV["RAILS_ENV"] ||= ENV["RACK_ENV"]
         | 
| 87 | 
            -
              require 'config/boot'
         | 
| 88 | 
            -
              require 'config/environment'
         | 
| 92 | 
            +
              require "#{::File.expand_path('config/boot')}"
         | 
| 93 | 
            +
              require "#{::File.expand_path('config/environment')}"
         | 
| 89 94 | 
             
              require 'unicorn/app/old_rails'
         | 
| 90 95 | 
             
              require 'unicorn/app/old_rails/static' # not needed with Unicorn 0.95+
         | 
| 91 96 | 
             
              use Unicorn::App::OldRails::Static
         | 
    
        data/GIT-VERSION-GEN
    CHANGED
    
    
    
        data/GNUmakefile
    CHANGED
    
    
    
        data/README
    CHANGED
    
    | @@ -67,7 +67,7 @@ network concurrency. | |
| 67 67 |  | 
| 68 68 | 
             
            == Applications
         | 
| 69 69 |  | 
| 70 | 
            -
            \Rainbows is mainly designed for the odd things Unicorn sucks at:
         | 
| 70 | 
            +
            \Rainbows! is mainly designed for the odd things Unicorn sucks at:
         | 
| 71 71 |  | 
| 72 72 | 
             
            * Web Sockets (via {Sunshowers}[http://rainbows.rubyforge.org/sunshowers/])
         | 
| 73 73 | 
             
            * 3rd-party APIs (to services outside your control/LAN)
         | 
| @@ -81,7 +81,7 @@ network concurrency. | |
| 81 81 | 
             
            * Reverse AJAX
         | 
| 82 82 | 
             
            * real-time upload processing (via {upr}[http://upr.bogomips.org/])
         | 
| 83 83 |  | 
| 84 | 
            -
            \Rainbows can also be used to service slow clients directly even with
         | 
| 84 | 
            +
            \Rainbows! can also be used to service slow clients directly even with
         | 
| 85 85 | 
             
            fast applications.
         | 
| 86 86 |  | 
| 87 87 | 
             
            == License
         | 
| @@ -97,7 +97,7 @@ details. | |
| 97 97 |  | 
| 98 98 | 
             
            == Install
         | 
| 99 99 |  | 
| 100 | 
            -
            You may download the tarball from the Rainbows project page on Rubyforge
         | 
| 100 | 
            +
            You may download the tarball from the \Rainbows! project page on Rubyforge
         | 
| 101 101 | 
             
            and run setup.rb after unpacking it:
         | 
| 102 102 |  | 
| 103 103 | 
             
            http://rubyforge.org/frs/?group_id=8977
         | 
    
        data/lib/rainbows.rb
    CHANGED
    
    | @@ -30,6 +30,12 @@ module Rainbows | |
| 30 30 | 
             
              G = State.new(true, 0, 0, 5)
         | 
| 31 31 | 
             
              O = {}
         | 
| 32 32 | 
             
              class Response416 < RangeError; end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              # map of numeric file descriptors to IO objects to avoid using IO.new
         | 
| 35 | 
            +
              # and potentially causing race conditions when using /dev/fd/
         | 
| 36 | 
            +
              FD_MAP = {}
         | 
| 37 | 
            +
              FD_MAP.compare_by_identity if FD_MAP.respond_to?(:compare_by_identity)
         | 
| 38 | 
            +
             | 
| 33 39 | 
             
              # :startdoc:
         | 
| 34 40 |  | 
| 35 41 | 
             
              require 'rainbows/const'
         | 
| @@ -69,22 +75,6 @@ module Rainbows | |
| 69 75 | 
             
                  HttpServer.new(app, options).start.join
         | 
| 70 76 | 
             
                end
         | 
| 71 77 |  | 
| 72 | 
            -
                # returns nil if accept fails
         | 
| 73 | 
            -
                def sync_accept(sock) # :nodoc:
         | 
| 74 | 
            -
                  rv = sock.accept
         | 
| 75 | 
            -
                  rv.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
         | 
| 76 | 
            -
                  rv
         | 
| 77 | 
            -
                rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EINTR
         | 
| 78 | 
            -
                end
         | 
| 79 | 
            -
             | 
| 80 | 
            -
                # returns nil if accept fails
         | 
| 81 | 
            -
                def accept(sock) # :nodoc:
         | 
| 82 | 
            -
                  rv = sock.accept_nonblock
         | 
| 83 | 
            -
                  rv.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
         | 
| 84 | 
            -
                  rv
         | 
| 85 | 
            -
                rescue Errno::EAGAIN, Errno::ECONNABORTED
         | 
| 86 | 
            -
                end
         | 
| 87 | 
            -
             | 
| 88 78 | 
             
                # returns a string representing the address of the given client +io+
         | 
| 89 79 | 
             
                # For local UNIX domain sockets, this will return a string referred
         | 
| 90 80 | 
             
                # to by the (non-frozen) Unicorn::HttpRequest::LOCALHOST constant.
         | 
| @@ -131,4 +121,6 @@ module Rainbows | |
| 131 121 | 
             
              autoload :ByteSlice, 'rainbows/byte_slice'
         | 
| 132 122 | 
             
              autoload :StreamFile, 'rainbows/stream_file'
         | 
| 133 123 | 
             
              autoload :HttpResponse, 'rainbows/http_response' # deprecated
         | 
| 124 | 
            +
              autoload :ThreadTimeout, 'rainbows/thread_timeout'
         | 
| 134 125 | 
             
            end
         | 
| 126 | 
            +
            require 'rainbows/acceptor'
         | 
| @@ -0,0 +1,26 @@ | |
| 1 | 
            +
            # -*- encoding: binary -*-
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # :enddoc:
         | 
| 4 | 
            +
            require 'fcntl'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            # this should make life easier for Zbatery if compatibility with
         | 
| 7 | 
            +
            # fcntl-crippled platforms is required (or if FD_CLOEXEC is inherited)
         | 
| 8 | 
            +
            # and we want to microptimize away fcntl(2) syscalls.
         | 
| 9 | 
            +
            module Rainbows::Acceptor
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              # returns nil if accept fails
         | 
| 12 | 
            +
              def sync_accept(sock)
         | 
| 13 | 
            +
                rv = sock.accept
         | 
| 14 | 
            +
                rv.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
         | 
| 15 | 
            +
                rv
         | 
| 16 | 
            +
              rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EINTR
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              # returns nil if accept fails
         | 
| 20 | 
            +
              def accept(sock)
         | 
| 21 | 
            +
                rv = sock.accept_nonblock
         | 
| 22 | 
            +
                rv.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
         | 
| 23 | 
            +
                rv
         | 
| 24 | 
            +
              rescue Errno::EAGAIN, Errno::ECONNABORTED
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
            end
         | 
    
        data/lib/rainbows/base.rb
    CHANGED
    
    | @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            # -*- encoding: binary -*-
         | 
| 2 2 |  | 
| 3 | 
            -
            # base class for Rainbows concurrency models, this is currently used by
         | 
| 3 | 
            +
            # base class for \Rainbows! concurrency models, this is currently used by
         | 
| 4 4 | 
             
            # ThreadSpawn and ThreadPool models.  Base is also its own
         | 
| 5 5 | 
             
            # (non-)concurrency model which is basically Unicorn-with-keepalive, and
         | 
| 6 6 | 
             
            # not intended for production use, as keepalive with a pure prefork
         | 
    
        data/lib/rainbows/const.rb
    CHANGED
    
    
| @@ -14,7 +14,8 @@ | |
| 14 14 | 
             
            class Rainbows::DevFdResponse < Struct.new(:app)
         | 
| 15 15 |  | 
| 16 16 | 
             
              # :stopdoc:
         | 
| 17 | 
            -
               | 
| 17 | 
            +
              FD_MAP = Rainbows::FD_MAP
         | 
| 18 | 
            +
             | 
| 18 19 | 
             
              # make this a no-op under Rubinius, it's pointless anyways
         | 
| 19 20 | 
             
              # since Rubinius doesn't have IO.copy_stream
         | 
| 20 21 | 
             
              def self.new(app)
         | 
| @@ -37,6 +38,7 @@ class Rainbows::DevFdResponse < Struct.new(:app) | |
| 37 38 | 
             
                headers = HeaderHash.new(headers)
         | 
| 38 39 | 
             
                st = io.stat
         | 
| 39 40 | 
             
                fileno = io.fileno
         | 
| 41 | 
            +
                FD_MAP[fileno] = io
         | 
| 40 42 | 
             
                if st.file?
         | 
| 41 43 | 
             
                  headers['Content-Length'] ||= st.size.to_s
         | 
| 42 44 | 
             
                  headers.delete('Transfer-Encoding')
         | 
| @@ -70,7 +72,7 @@ class Rainbows::DevFdResponse < Struct.new(:app) | |
| 70 72 | 
             
                end
         | 
| 71 73 |  | 
| 72 74 | 
             
                # remain Rack::Lint-compatible for people with wonky systems :P
         | 
| 73 | 
            -
                unless File. | 
| 75 | 
            +
                unless File.directory?("/dev/fd")
         | 
| 74 76 | 
             
                  alias to_path_orig to_path
         | 
| 75 77 | 
             
                  undef_method :to_path
         | 
| 76 78 | 
             
                end
         | 
| @@ -95,7 +95,8 @@ module Rainbows | |
| 95 95 | 
             
                    # long-running async response
         | 
| 96 96 | 
             
                    (response.nil? || -1 == response[0]) and return @state = :close
         | 
| 97 97 |  | 
| 98 | 
            -
                     | 
| 98 | 
            +
                    alive = @hp.keepalive? && G.alive && G.kato > 0
         | 
| 99 | 
            +
                    em_write_response(response, alive)
         | 
| 99 100 | 
             
                    if alive
         | 
| 100 101 | 
             
                      @env.clear
         | 
| 101 102 | 
             
                      @hp.reset
         | 
| @@ -169,6 +170,7 @@ module Rainbows | |
| 169 170 | 
             
                end
         | 
| 170 171 |  | 
| 171 172 | 
             
                module Server # :nodoc: all
         | 
| 173 | 
            +
                  include Rainbows::Acceptor
         | 
| 172 174 |  | 
| 173 175 | 
             
                  def close
         | 
| 174 176 | 
             
                    detach
         | 
| @@ -177,7 +179,7 @@ module Rainbows | |
| 177 179 |  | 
| 178 180 | 
             
                  def notify_readable
         | 
| 179 181 | 
             
                    return if CUR.size >= MAX
         | 
| 180 | 
            -
                    io =  | 
| 182 | 
            +
                    io = accept(@io) or return
         | 
| 181 183 | 
             
                    sig = EM.attach_fd(io.fileno, false)
         | 
| 182 184 | 
             
                    CUR[sig] = CL.new(sig, io)
         | 
| 183 185 | 
             
                  end
         | 
    
        data/lib/rainbows/fiber/rev.rb
    CHANGED
    
    | @@ -54,6 +54,7 @@ module Rainbows::Fiber | |
| 54 54 | 
             
                  include Rainbows
         | 
| 55 55 | 
             
                  include Rainbows::Const
         | 
| 56 56 | 
             
                  include Rainbows::Response
         | 
| 57 | 
            +
                  include Rainbows::Acceptor
         | 
| 57 58 | 
             
                  FIO = Rainbows::Fiber::IO
         | 
| 58 59 |  | 
| 59 60 | 
             
                  def to_io
         | 
| @@ -72,7 +73,7 @@ module Rainbows::Fiber | |
| 72 73 |  | 
| 73 74 | 
             
                  def on_readable
         | 
| 74 75 | 
             
                    return if G.cur >= MAX
         | 
| 75 | 
            -
                    c =  | 
| 76 | 
            +
                    c = accept(@io) and ::Fiber.new { process(c) }.resume
         | 
| 76 77 | 
             
                  end
         | 
| 77 78 |  | 
| 78 79 | 
             
                  def process(io)
         | 
    
        data/lib/rainbows/fiber_pool.rb
    CHANGED
    
    | @@ -15,6 +15,7 @@ module Rainbows | |
| 15 15 |  | 
| 16 16 | 
             
              module FiberPool
         | 
| 17 17 | 
             
                include Fiber::Base
         | 
| 18 | 
            +
                include Rainbows::Acceptor
         | 
| 18 19 |  | 
| 19 20 | 
             
                def worker_loop(worker) # :nodoc:
         | 
| 20 21 | 
             
                  init_worker_process(worker)
         | 
| @@ -29,7 +30,7 @@ module Rainbows | |
| 29 30 | 
             
                  begin
         | 
| 30 31 | 
             
                    schedule do |l|
         | 
| 31 32 | 
             
                      fib = pool.shift or break # let another worker process take it
         | 
| 32 | 
            -
                      if io =  | 
| 33 | 
            +
                      if io = accept(l)
         | 
| 33 34 | 
             
                        fib.resume(Fiber::IO.new(io, fib))
         | 
| 34 35 | 
             
                      else
         | 
| 35 36 | 
             
                        pool << fib
         | 
    
        data/lib/rainbows/fiber_spawn.rb
    CHANGED
    
    | @@ -12,6 +12,7 @@ module Rainbows | |
| 12 12 |  | 
| 13 13 | 
             
              module FiberSpawn
         | 
| 14 14 | 
             
                include Fiber::Base
         | 
| 15 | 
            +
                include Rainbows::Acceptor
         | 
| 15 16 |  | 
| 16 17 | 
             
                def worker_loop(worker) # :nodoc:
         | 
| 17 18 | 
             
                  init_worker_process(worker)
         | 
| @@ -22,7 +23,7 @@ module Rainbows | |
| 22 23 | 
             
                  begin
         | 
| 23 24 | 
             
                    schedule do |l|
         | 
| 24 25 | 
             
                      break if G.cur >= limit
         | 
| 25 | 
            -
                      io =  | 
| 26 | 
            +
                      io = accept(l) or next
         | 
| 26 27 | 
             
                      ::Fiber.new { process_client(fio.new(io, ::Fiber.current)) }.resume
         | 
| 27 28 | 
             
                    end
         | 
| 28 29 | 
             
                  rescue => e
         | 
    
        data/lib/rainbows/response.rb
    CHANGED
    
    
| @@ -30,6 +30,12 @@ | |
| 30 30 | 
             
            module Rainbows::Response::Body # :nodoc:
         | 
| 31 31 | 
             
              ALIASES = {}
         | 
| 32 32 |  | 
| 33 | 
            +
              FD_MAP = Rainbows::FD_MAP
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              def io_for_fd(fd)
         | 
| 36 | 
            +
                FD_MAP.delete(fd) || IO.new(fd)
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 33 39 | 
             
              # to_io is not part of the Rack spec, but make an exception here
         | 
| 34 40 | 
             
              # since we can conserve path lookups and file descriptors.
         | 
| 35 41 | 
             
              # \Rainbows! will never get here without checking for the existence
         | 
| @@ -41,7 +47,7 @@ module Rainbows::Response::Body # :nodoc: | |
| 41 47 | 
             
                  # try to take advantage of Rainbows::DevFdResponse, calling File.open
         | 
| 42 48 | 
             
                  # is a last resort
         | 
| 43 49 | 
             
                  path = body.to_path
         | 
| 44 | 
            -
                  path =~ %r{\A/dev/fd/(\d+)\z} ?  | 
| 50 | 
            +
                  path =~ %r{\A/dev/fd/(\d+)\z} ? io_for_fd($1.to_i) : File.open(path)
         | 
| 45 51 | 
             
                end
         | 
| 46 52 | 
             
              end
         | 
| 47 53 |  | 
    
        data/lib/rainbows/rev/client.rb
    CHANGED
    
    | @@ -58,7 +58,7 @@ module Rainbows | |
| 58 58 |  | 
| 59 59 | 
             
                  def next!
         | 
| 60 60 | 
             
                    @deferred = nil
         | 
| 61 | 
            -
                     | 
| 61 | 
            +
                    enable_write_watcher
         | 
| 62 62 | 
             
                  end
         | 
| 63 63 |  | 
| 64 64 | 
             
                  def timeout?
         | 
| @@ -170,6 +170,7 @@ module Rainbows | |
| 170 170 | 
             
                  def on_close
         | 
| 171 171 | 
             
                    close_deferred
         | 
| 172 172 | 
             
                    CONN.delete(self)
         | 
| 173 | 
            +
                    KATO.delete(self)
         | 
| 173 174 | 
             
                  end
         | 
| 174 175 |  | 
| 175 176 | 
             
                end # module Client
         | 
    
        data/lib/rainbows/rev/core.rb
    CHANGED
    
    | @@ -7,12 +7,12 @@ require 'rainbows/rev/heartbeat' | |
| 7 7 | 
             
            module Rainbows
         | 
| 8 8 | 
             
              module Rev
         | 
| 9 9 | 
             
                class Server < ::Rev::IO
         | 
| 10 | 
            -
                   | 
| 10 | 
            +
                  include Rainbows::Acceptor
         | 
| 11 11 | 
             
                  # CL and MAX will be defined in the corresponding worker loop
         | 
| 12 12 |  | 
| 13 13 | 
             
                  def on_readable
         | 
| 14 14 | 
             
                    return if CONN.size >= MAX
         | 
| 15 | 
            -
                    io =  | 
| 15 | 
            +
                    io = accept(@_io) and CL.new(io).attach(LOOP)
         | 
| 16 16 | 
             
                  end
         | 
| 17 17 | 
             
                end # class Server
         | 
| 18 18 |  | 
    
        data/lib/rainbows/rev/master.rb
    CHANGED
    
    | @@ -2,25 +2,20 @@ | |
| 2 2 | 
             
            # :enddoc:
         | 
| 3 3 | 
             
            require 'rainbows/rev'
         | 
| 4 4 |  | 
| 5 | 
            -
             | 
| 5 | 
            +
            class Rainbows::Rev::Master < Rev::AsyncWatcher
         | 
| 6 6 |  | 
| 7 | 
            -
               | 
| 8 | 
            -
                 | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
                    super()
         | 
| 12 | 
            -
                    @queue = queue
         | 
| 13 | 
            -
                  end
         | 
| 7 | 
            +
              def initialize(queue)
         | 
| 8 | 
            +
                super()
         | 
| 9 | 
            +
                @queue = queue
         | 
| 10 | 
            +
              end
         | 
| 14 11 |  | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 12 | 
            +
              def <<(output)
         | 
| 13 | 
            +
                @queue << output
         | 
| 14 | 
            +
                signal
         | 
| 15 | 
            +
              end
         | 
| 19 16 |  | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
                  end
         | 
| 24 | 
            -
                end
         | 
| 17 | 
            +
              def on_signal
         | 
| 18 | 
            +
                client, response = @queue.pop
         | 
| 19 | 
            +
                client.response_write(response)
         | 
| 25 20 | 
             
              end
         | 
| 26 21 | 
             
            end
         | 
    
        data/lib/rainbows/revactor.rb
    CHANGED
    
    | @@ -62,7 +62,7 @@ module Rainbows::Revactor | |
| 62 62 | 
             
                  if hp.headers?
         | 
| 63 63 | 
             
                    headers = HH.new(headers)
         | 
| 64 64 | 
             
                    range = make_range!(env, status, headers) and status = range.shift
         | 
| 65 | 
            -
                    env = false unless hp.keepalive? && G.alive
         | 
| 65 | 
            +
                    env = false unless hp.keepalive? && G.alive && G.kato > 0
         | 
| 66 66 | 
             
                    headers[CONNECTION] = env ? KEEP_ALIVE : CLOSE
         | 
| 67 67 | 
             
                    client.write(response_header(status, headers))
         | 
| 68 68 | 
             
                  end
         | 
    
        data/lib/rainbows/thread_pool.rb
    CHANGED
    
    | @@ -24,6 +24,7 @@ module Rainbows | |
| 24 24 | 
             
              module ThreadPool
         | 
| 25 25 |  | 
| 26 26 | 
             
                include Base
         | 
| 27 | 
            +
                include Rainbows::Acceptor
         | 
| 27 28 |  | 
| 28 29 | 
             
                def worker_loop(worker) # :nodoc:
         | 
| 29 30 | 
             
                  init_worker_process(worker)
         | 
| @@ -44,7 +45,7 @@ module Rainbows | |
| 44 45 | 
             
                def sync_worker # :nodoc:
         | 
| 45 46 | 
             
                  s = LISTENERS[0]
         | 
| 46 47 | 
             
                  begin
         | 
| 47 | 
            -
                    c =  | 
| 48 | 
            +
                    c = sync_accept(s) and process_client(c)
         | 
| 48 49 | 
             
                  rescue => e
         | 
| 49 50 | 
             
                    Error.listen_loop(e)
         | 
| 50 51 | 
             
                  end while G.alive
         | 
| @@ -58,7 +59,7 @@ module Rainbows | |
| 58 59 | 
             
                    # problem.  On the other hand, a thundering herd may not
         | 
| 59 60 | 
             
                    # even incur as much overhead as an extra Mutex#synchronize
         | 
| 60 61 | 
             
                    ret = IO.select(LISTENERS, nil, nil, 1) and ret[0].each do |s|
         | 
| 61 | 
            -
                      s =  | 
| 62 | 
            +
                      s = accept(s) and process_client(s)
         | 
| 62 63 | 
             
                    end
         | 
| 63 64 | 
             
                  rescue Errno::EINTR
         | 
| 64 65 | 
             
                  rescue => e
         | 
| @@ -18,6 +18,7 @@ module Rainbows | |
| 18 18 |  | 
| 19 19 | 
             
              module ThreadSpawn
         | 
| 20 20 | 
             
                include Base
         | 
| 21 | 
            +
                include Rainbows::Acceptor
         | 
| 21 22 |  | 
| 22 23 | 
             
                def accept_loop(klass) #:nodoc:
         | 
| 23 24 | 
             
                  lock = Mutex.new
         | 
| @@ -36,7 +37,7 @@ module Rainbows | |
| 36 37 | 
             
                          # CPU during I/O wait, CPU cycles that can be better used
         | 
| 37 38 | 
             
                          # by other worker _processes_.
         | 
| 38 39 | 
             
                          sleep(0.01)
         | 
| 39 | 
            -
                        elsif c =  | 
| 40 | 
            +
                        elsif c = sync_accept(l)
         | 
| 40 41 | 
             
                          klass.new(c) do |c|
         | 
| 41 42 | 
             
                            begin
         | 
| 42 43 | 
             
                              lock.synchronize { G.cur += 1 }
         | 
| @@ -0,0 +1,94 @@ | |
| 1 | 
            +
            # -*- encoding: binary -*-
         | 
| 2 | 
            +
            require 'thread'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            # Soft timeout middleware for thread-based concurrency models in \Rainbows!
         | 
| 5 | 
            +
            # This timeout only includes application dispatch, and will not take into
         | 
| 6 | 
            +
            # account the (rare) response bodies that are dynamically generated while
         | 
| 7 | 
            +
            # they are being written out to the client.
         | 
| 8 | 
            +
            #
         | 
| 9 | 
            +
            # In your rackup config file (config.ru), the following line will
         | 
| 10 | 
            +
            # cause execution to timeout in 1.5 seconds.
         | 
| 11 | 
            +
            #
         | 
| 12 | 
            +
            #    use Rainbows::ThreadTimeout, :timeout => 1.5
         | 
| 13 | 
            +
            #    run MyApplication.new
         | 
| 14 | 
            +
            #
         | 
| 15 | 
            +
            # You may also specify a threshold, so the timeout does not take
         | 
| 16 | 
            +
            # effect until there are enough active clients.  It does not make
         | 
| 17 | 
            +
            # sense to set a +:threshold+ higher or equal to the
         | 
| 18 | 
            +
            # +worker_connections+ \Rainbows! configuration parameter.
         | 
| 19 | 
            +
            # You may specify a negative threshold to be an absolute
         | 
| 20 | 
            +
            # value relative to the +worker_connections+ parameter, thus
         | 
| 21 | 
            +
            # if you specify a threshold of -1, and have 100 worker_connections,
         | 
| 22 | 
            +
            # ThreadTimeout will only activate when there are 99 active requests.
         | 
| 23 | 
            +
            #
         | 
| 24 | 
            +
            #    use Rainbows::ThreadTimeout, :timeout => 1.5, :threshold => -1
         | 
| 25 | 
            +
            #    run MyApplication.new
         | 
| 26 | 
            +
            #
         | 
| 27 | 
            +
            # This middleware only affects elements below it in the stack, so
         | 
| 28 | 
            +
            # it can be configured to ignore certain endpoints or middlewares.
         | 
| 29 | 
            +
            #
         | 
| 30 | 
            +
            # Timed-out requests will cause this middleware to return with a
         | 
| 31 | 
            +
            # "408 Request Timeout" response.
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            class Rainbows::ThreadTimeout < Struct.new(:app, :timeout,
         | 
| 34 | 
            +
                                                       :threshold, :watchdog,
         | 
| 35 | 
            +
                                                       :active, :lock)
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              # :stopdoc:
         | 
| 38 | 
            +
              class ExecutionExpired < ::Exception
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
              def initialize(app, opts)
         | 
| 42 | 
            +
                timeout = opts[:timeout]
         | 
| 43 | 
            +
                Numeric === timeout or
         | 
| 44 | 
            +
                  raise TypeError, "timeout=#{timeout.inspect} is not numeric"
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                if threshold = opts[:threshold]
         | 
| 47 | 
            +
                  Integer === threshold or
         | 
| 48 | 
            +
                    raise TypeError, "threshold=#{threshold.inspect} is not an integer"
         | 
| 49 | 
            +
                  threshold == 0 and
         | 
| 50 | 
            +
                    raise ArgumentError, "threshold=0 does not make sense"
         | 
| 51 | 
            +
                  threshold < 0 and
         | 
| 52 | 
            +
                    threshold += Rainbows::G.server.worker_connections
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
                super(app, timeout, threshold, nil, {}, Mutex.new)
         | 
| 55 | 
            +
              end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
              def call(env)
         | 
| 58 | 
            +
                lock.synchronize do
         | 
| 59 | 
            +
                  start_watchdog unless watchdog
         | 
| 60 | 
            +
                  active[Thread.current] = Time.now + timeout
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
                begin
         | 
| 63 | 
            +
                  app.call(env)
         | 
| 64 | 
            +
                ensure
         | 
| 65 | 
            +
                  lock.synchronize { active.delete(Thread.current) }
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
                rescue ExecutionExpired
         | 
| 68 | 
            +
                  [ 408, { 'Content-Type' => 'text/plain', 'Content-Length' => '0' }, [] ]
         | 
| 69 | 
            +
              end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
              def start_watchdog
         | 
| 72 | 
            +
                self.watchdog = Thread.new do
         | 
| 73 | 
            +
                  begin
         | 
| 74 | 
            +
                    if next_wake = lock.synchronize { active.values }.min
         | 
| 75 | 
            +
                      next_wake -= Time.now
         | 
| 76 | 
            +
                      sleep(next_wake) if next_wake > 0
         | 
| 77 | 
            +
                    else
         | 
| 78 | 
            +
                      sleep(timeout)
         | 
| 79 | 
            +
                    end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                    # "active.size" is atomic in MRI 1.8 and 1.9
         | 
| 82 | 
            +
                    next if threshold && active.size < threshold
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                    now = Time.now
         | 
| 85 | 
            +
                    lock.synchronize do
         | 
| 86 | 
            +
                      active.delete_if do |thread, time|
         | 
| 87 | 
            +
                        time >= now and thread.raise(ExecutionExpired).nil?
         | 
| 88 | 
            +
                      end
         | 
| 89 | 
            +
                    end
         | 
| 90 | 
            +
                  end while true
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
              end
         | 
| 93 | 
            +
              # :startdoc:
         | 
| 94 | 
            +
            end
         | 
    
        data/rainbows.gemspec
    CHANGED
    
    | @@ -46,7 +46,7 @@ Gem::Specification.new do |s| | |
| 46 46 | 
             
              # we need Unicorn for the HTTP parser and process management
         | 
| 47 47 | 
             
              # Unicorn 0.991.0 handles config.ru when started outside of
         | 
| 48 48 | 
             
              # the prespecified working_directory
         | 
| 49 | 
            -
              s.add_dependency(%q<unicorn>, [">= 1.1. | 
| 49 | 
            +
              s.add_dependency(%q<unicorn>, [">= 1.1.3", "< 2.0.0"])
         | 
| 50 50 | 
             
              s.add_development_dependency(%q<isolate>, "~> 2.1.0")
         | 
| 51 51 |  | 
| 52 52 | 
             
              # optional runtime dependencies depending on configuration
         | 
| @@ -0,0 +1,30 @@ | |
| 1 | 
            +
            # must be run without Rack::Lint since that clobbers to_path
         | 
| 2 | 
            +
            class MyMiddleware < Struct.new(:app)
         | 
| 3 | 
            +
              class Body < Struct.new(:body, :to_path)
         | 
| 4 | 
            +
                def each(&block); body.each(&block); end
         | 
| 5 | 
            +
                def close
         | 
| 6 | 
            +
                  c = body.respond_to?(:close)
         | 
| 7 | 
            +
                  ::File.open(ENV['fifo'], 'wb') do |fp|
         | 
| 8 | 
            +
                    fp.syswrite("CLOSING #{body.inspect} #{to_path} (#{c})\n")
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
                  body.close if c
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              def call(env)
         | 
| 15 | 
            +
                status, headers, body = app.call(env)
         | 
| 16 | 
            +
                body.respond_to?(:to_path) and body = Body.new(body, body.to_path)
         | 
| 17 | 
            +
                [ status, headers, body ]
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
            end
         | 
| 20 | 
            +
            use MyMiddleware
         | 
| 21 | 
            +
            use Rainbows::DevFdResponse
         | 
| 22 | 
            +
            run(lambda { |env|
         | 
| 23 | 
            +
              io = IO.popen('cat random_blob', 'rb')
         | 
| 24 | 
            +
              [ 200,
         | 
| 25 | 
            +
                {
         | 
| 26 | 
            +
                  'Content-Length' => ::File.stat('random_blob').size.to_s,
         | 
| 27 | 
            +
                  'Content-Type' => 'application/octet-stream',
         | 
| 28 | 
            +
                },
         | 
| 29 | 
            +
                io ]
         | 
| 30 | 
            +
            })
         | 
| @@ -4,7 +4,7 @@ t_plan 4 "proper handling of onenine encoding for $model" | |
| 4 4 |  | 
| 5 5 | 
             
            t_begin "setup and startup" && {
         | 
| 6 6 | 
             
            	rainbows_setup $model
         | 
| 7 | 
            -
            	rainbows -D ./t0016.rb -c $unicorn_config
         | 
| 7 | 
            +
            	rainbows -E none -D ./t0016.rb -c $unicorn_config
         | 
| 8 8 | 
             
            	rainbows_wait_start
         | 
| 9 9 | 
             
            	expect_sha1=8ff79d8115f9fe38d18be858c66aa08a1cc27a66
         | 
| 10 10 | 
             
            }
         | 
| @@ -0,0 +1,43 @@ | |
| 1 | 
            +
            #!/bin/sh
         | 
| 2 | 
            +
            . ./test-lib.sh
         | 
| 3 | 
            +
            t_plan 6 "keepalive_timeout 0 tests for $model"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            t_begin "setup and start" && {
         | 
| 6 | 
            +
            	rainbows_setup $model 2 0
         | 
| 7 | 
            +
                    grep 'keepalive_timeout 0' $unicorn_config
         | 
| 8 | 
            +
            	rainbows -D env.ru -c $unicorn_config
         | 
| 9 | 
            +
            	rainbows_wait_start
         | 
| 10 | 
            +
            }
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            t_begin 'check server responds with Connection: close' && {
         | 
| 13 | 
            +
            	curl -sSfi http://$listen/ | grep 'Connection: close'
         | 
| 14 | 
            +
            }
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            t_begin "send keepalive response that does not expect close" && {
         | 
| 17 | 
            +
            	req='GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'
         | 
| 18 | 
            +
            	t0=$(date +%s)
         | 
| 19 | 
            +
            	(
         | 
| 20 | 
            +
            		cat $fifo > $tmp &
         | 
| 21 | 
            +
            		printf "$req"
         | 
| 22 | 
            +
            		wait
         | 
| 23 | 
            +
            		date +%s > $ok
         | 
| 24 | 
            +
            	) | socat - TCP:$listen > $fifo
         | 
| 25 | 
            +
            	now="$(cat $ok)"
         | 
| 26 | 
            +
            	elapsed=$(( $now - $t0 ))
         | 
| 27 | 
            +
            	t_info "elapsed=$elapsed (expecting <=3)"
         | 
| 28 | 
            +
            	test $elapsed -le 3
         | 
| 29 | 
            +
            }
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            t_begin "'Connection: close' header set" && {
         | 
| 32 | 
            +
            	grep 'Connection: close' $tmp
         | 
| 33 | 
            +
            }
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            t_begin "killing succeeds" && {
         | 
| 36 | 
            +
            	kill $rainbows_pid
         | 
| 37 | 
            +
            }
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            t_begin "check stderr" && {
         | 
| 40 | 
            +
            	check_stderr
         | 
| 41 | 
            +
            }
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            t_done
         | 
| @@ -0,0 +1,101 @@ | |
| 1 | 
            +
            #!/bin/sh
         | 
| 2 | 
            +
            . ./test-lib.sh
         | 
| 3 | 
            +
            if ! test -d /dev/fd
         | 
| 4 | 
            +
            then
         | 
| 5 | 
            +
            	t_info "skipping $T since /dev/fd is required"
         | 
| 6 | 
            +
            	exit 0
         | 
| 7 | 
            +
            fi
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            t_plan 16 "close pipe to_path response for $model"
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            t_begin "setup and startup" && {
         | 
| 12 | 
            +
            	rtmpfiles err out http_fifo sub_ok
         | 
| 13 | 
            +
            	rainbows_setup $model
         | 
| 14 | 
            +
            	export fifo
         | 
| 15 | 
            +
            	rainbows -E none -D close-pipe-to_path-response.ru -c $unicorn_config
         | 
| 16 | 
            +
            	rainbows_wait_start
         | 
| 17 | 
            +
            }
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            t_begin "read random blob sha1" && {
         | 
| 20 | 
            +
            	random_blob_sha1=$(rsha1 < random_blob)
         | 
| 21 | 
            +
            }
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            t_begin "start FIFO reader" && {
         | 
| 24 | 
            +
            	cat $fifo > $out &
         | 
| 25 | 
            +
            }
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            t_begin "single request matches" && {
         | 
| 28 | 
            +
            	sha1=$(curl -sSfv 2> $err http://$listen/ | rsha1)
         | 
| 29 | 
            +
            	test -n "$sha1"
         | 
| 30 | 
            +
            	test x"$sha1" = x"$random_blob_sha1"
         | 
| 31 | 
            +
            }
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            t_begin "body.close called" && {
         | 
| 34 | 
            +
            	wait # for cat $fifo
         | 
| 35 | 
            +
            	grep CLOSING $out || die "body.close not logged"
         | 
| 36 | 
            +
            }
         | 
| 37 | 
            +
             | 
| 38 | 
            +
            t_begin "start FIFO reader for abortive HTTP/1.1 request" && {
         | 
| 39 | 
            +
            	cat $fifo > $out &
         | 
| 40 | 
            +
            }
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            t_begin "send abortive HTTP/1.1 request" && {
         | 
| 43 | 
            +
            	rm -f $ok
         | 
| 44 | 
            +
            	(
         | 
| 45 | 
            +
            		printf 'GET /random_blob HTTP/1.1\r\nHost: example.com\r\n\r\n'
         | 
| 46 | 
            +
            		dd bs=4096 count=1 < $http_fifo >/dev/null
         | 
| 47 | 
            +
            		echo ok > $ok
         | 
| 48 | 
            +
            	) | socat - TCP:$listen > $http_fifo || :
         | 
| 49 | 
            +
            	test xok = x$(cat $ok)
         | 
| 50 | 
            +
            }
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            t_begin "body.close called for aborted HTTP/1.1 request" && {
         | 
| 53 | 
            +
            	wait # for cat $fifo
         | 
| 54 | 
            +
            	grep CLOSING $out || die "body.close not logged"
         | 
| 55 | 
            +
            }
         | 
| 56 | 
            +
             | 
| 57 | 
            +
            t_begin "start FIFO reader for abortive HTTP/1.0 request" && {
         | 
| 58 | 
            +
            	cat $fifo > $out &
         | 
| 59 | 
            +
            }
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            t_begin "send abortive HTTP/1.0 request" && {
         | 
| 62 | 
            +
            	rm -f $ok
         | 
| 63 | 
            +
            	(
         | 
| 64 | 
            +
            		printf 'GET /random_blob HTTP/1.0\r\n\r\n'
         | 
| 65 | 
            +
            		dd bs=4096 count=1 < $http_fifo >/dev/null
         | 
| 66 | 
            +
            		echo ok > $ok
         | 
| 67 | 
            +
            	) | socat - TCP:$listen > $http_fifo || :
         | 
| 68 | 
            +
            	test xok = x$(cat $ok)
         | 
| 69 | 
            +
            }
         | 
| 70 | 
            +
             | 
| 71 | 
            +
            t_begin "body.close called for aborted HTTP/1.0 request" && {
         | 
| 72 | 
            +
            	wait # for cat $fifo
         | 
| 73 | 
            +
            	grep CLOSING $out || die "body.close not logged"
         | 
| 74 | 
            +
            }
         | 
| 75 | 
            +
             | 
| 76 | 
            +
            t_begin "start FIFO reader for abortive HTTP/0.9 request" && {
         | 
| 77 | 
            +
            	cat $fifo > $out &
         | 
| 78 | 
            +
            }
         | 
| 79 | 
            +
             | 
| 80 | 
            +
            t_begin "send abortive HTTP/0.9 request" && {
         | 
| 81 | 
            +
            	rm -f $ok
         | 
| 82 | 
            +
            	(
         | 
| 83 | 
            +
            		printf 'GET /random_blob\r\n'
         | 
| 84 | 
            +
            		dd bs=4096 count=1 < $http_fifo >/dev/null
         | 
| 85 | 
            +
            		echo ok > $ok
         | 
| 86 | 
            +
            	) | socat - TCP:$listen > $http_fifo || :
         | 
| 87 | 
            +
            	test xok = x$(cat $ok)
         | 
| 88 | 
            +
            }
         | 
| 89 | 
            +
             | 
| 90 | 
            +
            t_begin "body.close called for aborted HTTP/0.9 request" && {
         | 
| 91 | 
            +
            	wait # for cat $fifo
         | 
| 92 | 
            +
            	grep CLOSING $out || die "body.close not logged"
         | 
| 93 | 
            +
            }
         | 
| 94 | 
            +
             | 
| 95 | 
            +
            t_begin "shutdown server" && {
         | 
| 96 | 
            +
            	kill -QUIT $rainbows_pid
         | 
| 97 | 
            +
            }
         | 
| 98 | 
            +
             | 
| 99 | 
            +
            t_begin "check stderr" && check_stderr
         | 
| 100 | 
            +
             | 
| 101 | 
            +
            t_done
         | 
| @@ -0,0 +1,36 @@ | |
| 1 | 
            +
            #!/bin/sh
         | 
| 2 | 
            +
            . ./test-lib.sh
         | 
| 3 | 
            +
            case $model in
         | 
| 4 | 
            +
            ThreadSpawn|ThreadPool|RevThreadSpawn|RevThreadPool) ;;
         | 
| 5 | 
            +
            *) t_info "$0 is only compatible with Thread*"; exit 0 ;;
         | 
| 6 | 
            +
            esac
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            t_plan 5 "ThreadTimeout Rack middleware test for $model"
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            t_begin "configure and start" && {
         | 
| 11 | 
            +
            	rtmpfiles curl_err
         | 
| 12 | 
            +
            	rainbows_setup
         | 
| 13 | 
            +
            	rainbows -D t9100.ru -c $unicorn_config
         | 
| 14 | 
            +
            	rainbows_wait_start
         | 
| 15 | 
            +
            }
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            t_begin "normal request should not timeout" && {
         | 
| 18 | 
            +
            	test x"HI" = x"$(curl -sSf http://$listen/ 2>> $curl_err)"
         | 
| 19 | 
            +
            }
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            t_begin "sleepy request times out with 408" && {
         | 
| 22 | 
            +
            	rm -f $ok
         | 
| 23 | 
            +
            	curl -sSf http://$listen/2 2>> $curl_err || > $ok
         | 
| 24 | 
            +
            	test -e $ok
         | 
| 25 | 
            +
            	grep 408 $curl_err
         | 
| 26 | 
            +
            }
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            t_begin "kill server" && {
         | 
| 29 | 
            +
            	kill $rainbows_pid
         | 
| 30 | 
            +
            }
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            t_begin "no errors in Rainbows! stderr" && {
         | 
| 33 | 
            +
            	check_stderr
         | 
| 34 | 
            +
            }
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            t_done
         | 
    
        data/t/t9100.ru
    ADDED
    
    
| @@ -0,0 +1,62 @@ | |
| 1 | 
            +
            #!/bin/sh
         | 
| 2 | 
            +
            . ./test-lib.sh
         | 
| 3 | 
            +
            case $model in
         | 
| 4 | 
            +
            ThreadSpawn|ThreadPool|RevThreadSpawn|RevThreadPool) ;;
         | 
| 5 | 
            +
            *) t_info "$0 is only compatible with Thread*"; exit 0 ;;
         | 
| 6 | 
            +
            esac
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            t_plan 6 "ThreadTimeout Rack middleware test for $model"
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            t_begin "configure and start" && {
         | 
| 11 | 
            +
            	rtmpfiles curl_err curl_out
         | 
| 12 | 
            +
            	rainbows_setup $model 10
         | 
| 13 | 
            +
            	rainbows -D t9101.ru -c $unicorn_config
         | 
| 14 | 
            +
            	rainbows_wait_start
         | 
| 15 | 
            +
            }
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            t_begin "normal request should not timeout" && {
         | 
| 18 | 
            +
            	test x"HI" = x"$(curl -sSf http://$listen/ 2>> $curl_err)"
         | 
| 19 | 
            +
            }
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            t_begin "8 sleepy requests do not time out" && {
         | 
| 22 | 
            +
            	> $curl_err
         | 
| 23 | 
            +
            	for i in 1 2 3 4 5 6 7 8
         | 
| 24 | 
            +
            	do
         | 
| 25 | 
            +
            		curl --no-buffer -sSf http://$listen/3 \
         | 
| 26 | 
            +
            		  2>> $curl_err >> $curl_out &
         | 
| 27 | 
            +
            	done
         | 
| 28 | 
            +
            	wait
         | 
| 29 | 
            +
            	test 8 -eq "$(wc -l < $curl_out)"
         | 
| 30 | 
            +
            	test xHI = x"$(sort < $curl_out | uniq)"
         | 
| 31 | 
            +
            }
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            t_begin "9 sleepy requests do time out" && {
         | 
| 34 | 
            +
            	> $curl_err
         | 
| 35 | 
            +
            	> $curl_out
         | 
| 36 | 
            +
            	for i in 1 2 3 4 5 6 7 8 9
         | 
| 37 | 
            +
            	do
         | 
| 38 | 
            +
            		rtmpfiles curl_err_$i
         | 
| 39 | 
            +
            		curl -sSf --no-buffer \
         | 
| 40 | 
            +
            		  http://$listen/3 2>> ${curl_err}_${i} >> $curl_out &
         | 
| 41 | 
            +
            	done
         | 
| 42 | 
            +
            	wait
         | 
| 43 | 
            +
            	if test -s $curl_out
         | 
| 44 | 
            +
            	then
         | 
| 45 | 
            +
            		dbgcat curl_out
         | 
| 46 | 
            +
            		die "$curl_out should be empty"
         | 
| 47 | 
            +
            	fi
         | 
| 48 | 
            +
            	for i in 1 2 3 4 5 6 7 8 9
         | 
| 49 | 
            +
            	do
         | 
| 50 | 
            +
            		grep 408 ${curl_err}_${i}
         | 
| 51 | 
            +
            	done
         | 
| 52 | 
            +
            }
         | 
| 53 | 
            +
             | 
| 54 | 
            +
            t_begin "kill server" && {
         | 
| 55 | 
            +
            	kill $rainbows_pid
         | 
| 56 | 
            +
            }
         | 
| 57 | 
            +
             | 
| 58 | 
            +
            t_begin "no errors in Rainbows! stderr" && {
         | 
| 59 | 
            +
            	check_stderr
         | 
| 60 | 
            +
            }
         | 
| 61 | 
            +
             | 
| 62 | 
            +
            t_done
         | 
    
        data/t/t9101.ru
    ADDED
    
    
    
        data/t/test_isolate.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,13 +1,13 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification 
         | 
| 2 2 | 
             
            name: rainbows
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            -
              hash:  | 
| 4 | 
            +
              hash: 411
         | 
| 5 5 | 
             
              prerelease: false
         | 
| 6 6 | 
             
              segments: 
         | 
| 7 7 | 
             
              - 0
         | 
| 8 | 
            -
              -  | 
| 8 | 
            +
              - 97
         | 
| 9 9 | 
             
              - 0
         | 
| 10 | 
            -
              version: 0. | 
| 10 | 
            +
              version: 0.97.0
         | 
| 11 11 | 
             
            platform: ruby
         | 
| 12 12 | 
             
            authors: 
         | 
| 13 13 | 
             
            - Rainbows! hackers
         | 
| @@ -15,7 +15,7 @@ autorequire: | |
| 15 15 | 
             
            bindir: bin
         | 
| 16 16 | 
             
            cert_chain: []
         | 
| 17 17 |  | 
| 18 | 
            -
            date: 2010-08- | 
| 18 | 
            +
            date: 2010-08-28 00:00:00 +00:00
         | 
| 19 19 | 
             
            default_executable: 
         | 
| 20 20 | 
             
            dependencies: 
         | 
| 21 21 | 
             
            - !ruby/object:Gem::Dependency 
         | 
| @@ -41,12 +41,12 @@ dependencies: | |
| 41 41 | 
             
                requirements: 
         | 
| 42 42 | 
             
                - - ">="
         | 
| 43 43 | 
             
                  - !ruby/object:Gem::Version 
         | 
| 44 | 
            -
                    hash:  | 
| 44 | 
            +
                    hash: 21
         | 
| 45 45 | 
             
                    segments: 
         | 
| 46 46 | 
             
                    - 1
         | 
| 47 47 | 
             
                    - 1
         | 
| 48 | 
            -
                    -  | 
| 49 | 
            -
                    version: 1.1. | 
| 48 | 
            +
                    - 3
         | 
| 49 | 
            +
                    version: 1.1.3
         | 
| 50 50 | 
             
                - - <
         | 
| 51 51 | 
             
                  - !ruby/object:Gem::Version 
         | 
| 52 52 | 
             
                    hash: 15
         | 
| @@ -87,6 +87,7 @@ extra_rdoc_files: | |
| 87 87 | 
             
            - DEPLOY
         | 
| 88 88 | 
             
            - FAQ
         | 
| 89 89 | 
             
            - lib/rainbows.rb
         | 
| 90 | 
            +
            - lib/rainbows/acceptor.rb
         | 
| 90 91 | 
             
            - lib/rainbows/actor_spawn.rb
         | 
| 91 92 | 
             
            - lib/rainbows/app_pool.rb
         | 
| 92 93 | 
             
            - lib/rainbows/base.rb
         | 
| @@ -138,6 +139,7 @@ extra_rdoc_files: | |
| 138 139 | 
             
            - lib/rainbows/tee_input.rb
         | 
| 139 140 | 
             
            - lib/rainbows/thread_pool.rb
         | 
| 140 141 | 
             
            - lib/rainbows/thread_spawn.rb
         | 
| 142 | 
            +
            - lib/rainbows/thread_timeout.rb
         | 
| 141 143 | 
             
            - lib/rainbows/writer_thread_pool.rb
         | 
| 142 144 | 
             
            - lib/rainbows/writer_thread_spawn.rb
         | 
| 143 145 | 
             
            - LICENSE
         | 
| @@ -178,6 +180,7 @@ files: | |
| 178 180 | 
             
            - Test_Suite
         | 
| 179 181 | 
             
            - bin/rainbows
         | 
| 180 182 | 
             
            - lib/rainbows.rb
         | 
| 183 | 
            +
            - lib/rainbows/acceptor.rb
         | 
| 181 184 | 
             
            - lib/rainbows/actor_spawn.rb
         | 
| 182 185 | 
             
            - lib/rainbows/app_pool.rb
         | 
| 183 186 | 
             
            - lib/rainbows/base.rb
         | 
| @@ -229,6 +232,7 @@ files: | |
| 229 232 | 
             
            - lib/rainbows/tee_input.rb
         | 
| 230 233 | 
             
            - lib/rainbows/thread_pool.rb
         | 
| 231 234 | 
             
            - lib/rainbows/thread_spawn.rb
         | 
| 235 | 
            +
            - lib/rainbows/thread_timeout.rb
         | 
| 232 236 | 
             
            - lib/rainbows/writer_thread_pool.rb
         | 
| 233 237 | 
             
            - lib/rainbows/writer_thread_spawn.rb
         | 
| 234 238 | 
             
            - local.mk.sample
         | 
| @@ -250,6 +254,7 @@ files: | |
| 250 254 | 
             
            - t/bin/unused_listen
         | 
| 251 255 | 
             
            - t/bin/utee
         | 
| 252 256 | 
             
            - t/close-pipe-response.ru
         | 
| 257 | 
            +
            - t/close-pipe-to_path-response.ru
         | 
| 253 258 | 
             
            - t/content-md5.ru
         | 
| 254 259 | 
             
            - t/cramp/README
         | 
| 255 260 | 
             
            - t/cramp/rainsocket.ru
         | 
| @@ -302,6 +307,7 @@ files: | |
| 302 307 | 
             
            - t/t0015-working_directory.sh
         | 
| 303 308 | 
             
            - t/t0016-onenine-encoding-is-tricky.sh
         | 
| 304 309 | 
             
            - t/t0016.rb
         | 
| 310 | 
            +
            - t/t0017-keepalive-timeout-zero.sh
         | 
| 305 311 | 
             
            - t/t0020-large-sendfile-response.sh
         | 
| 306 312 | 
             
            - t/t0021-sendfile-wrap-to_path.sh
         | 
| 307 313 | 
             
            - t/t0022-copy_stream-byte-range.sh
         | 
| @@ -309,6 +315,7 @@ files: | |
| 309 315 | 
             
            - t/t0024-pipelined-sendfile-response.sh
         | 
| 310 316 | 
             
            - t/t0030-fast-pipe-response.sh
         | 
| 311 317 | 
             
            - t/t0031-close-pipe-response.sh
         | 
| 318 | 
            +
            - t/t0032-close-pipe-to_path-response.sh
         | 
| 312 319 | 
             
            - t/t0034-pipelined-pipe-response.sh
         | 
| 313 320 | 
             
            - t/t0100-rack-input-hammer-chunked.sh
         | 
| 314 321 | 
             
            - t/t0100-rack-input-hammer-content-length.sh
         | 
| @@ -332,6 +339,10 @@ files: | |
| 332 339 | 
             
            - t/t9001.ru
         | 
| 333 340 | 
             
            - t/t9002-server-token.sh
         | 
| 334 341 | 
             
            - t/t9002.ru
         | 
| 342 | 
            +
            - t/t9100-thread-timeout.sh
         | 
| 343 | 
            +
            - t/t9100.ru
         | 
| 344 | 
            +
            - t/t9101-thread-timeout-threshold.sh
         | 
| 345 | 
            +
            - t/t9101.ru
         | 
| 335 346 | 
             
            - t/test-lib.sh
         | 
| 336 347 | 
             
            - t/test_isolate.rb
         | 
| 337 348 | 
             
            - t/worker-follows-master-to-death.ru
         |