puma 5.2.2 → 6.3.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.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- checksums.yaml +4 -4
 - data/History.md +483 -4
 - data/README.md +101 -20
 - data/bin/puma-wild +1 -1
 - data/docs/architecture.md +50 -16
 - data/docs/compile_options.md +38 -2
 - data/docs/deployment.md +53 -67
 - data/docs/fork_worker.md +1 -3
 - data/docs/jungle/rc.d/README.md +1 -1
 - data/docs/kubernetes.md +1 -1
 - data/docs/nginx.md +1 -1
 - data/docs/plugins.md +15 -15
 - data/docs/rails_dev_mode.md +2 -3
 - data/docs/restart.md +7 -7
 - data/docs/signals.md +11 -10
 - data/docs/stats.md +8 -8
 - data/docs/systemd.md +65 -69
 - data/docs/testing_benchmarks_local_files.md +150 -0
 - data/docs/testing_test_rackup_ci_files.md +36 -0
 - data/ext/puma_http11/extconf.rb +44 -13
 - data/ext/puma_http11/http11_parser.c +24 -11
 - data/ext/puma_http11/http11_parser.h +2 -2
 - data/ext/puma_http11/http11_parser.java.rl +2 -2
 - data/ext/puma_http11/http11_parser.rl +2 -2
 - data/ext/puma_http11/http11_parser_common.rl +3 -3
 - data/ext/puma_http11/mini_ssl.c +150 -23
 - data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
 - data/ext/puma_http11/org/jruby/puma/Http11Parser.java +50 -48
 - data/ext/puma_http11/org/jruby/puma/MiniSSL.java +188 -102
 - data/ext/puma_http11/puma_http11.c +18 -10
 - data/lib/puma/app/status.rb +10 -7
 - data/lib/puma/binder.rb +112 -62
 - data/lib/puma/cli.rb +24 -20
 - data/lib/puma/client.rb +162 -36
 - data/lib/puma/cluster/worker.rb +31 -27
 - data/lib/puma/cluster/worker_handle.rb +12 -1
 - data/lib/puma/cluster.rb +102 -61
 - data/lib/puma/commonlogger.rb +21 -14
 - data/lib/puma/configuration.rb +78 -54
 - data/lib/puma/const.rb +135 -97
 - data/lib/puma/control_cli.rb +25 -20
 - data/lib/puma/detect.rb +12 -2
 - data/lib/puma/dsl.rb +308 -58
 - data/lib/puma/error_logger.rb +20 -11
 - data/lib/puma/events.rb +6 -126
 - data/lib/puma/io_buffer.rb +39 -4
 - data/lib/puma/jruby_restart.rb +2 -1
 - data/lib/puma/{json.rb → json_serialization.rb} +1 -1
 - data/lib/puma/launcher/bundle_pruner.rb +104 -0
 - data/lib/puma/launcher.rb +114 -173
 - data/lib/puma/log_writer.rb +147 -0
 - data/lib/puma/minissl/context_builder.rb +30 -16
 - data/lib/puma/minissl.rb +132 -38
 - data/lib/puma/null_io.rb +5 -0
 - data/lib/puma/plugin/systemd.rb +90 -0
 - data/lib/puma/plugin/tmp_restart.rb +1 -1
 - data/lib/puma/plugin.rb +2 -2
 - data/lib/puma/rack/builder.rb +7 -7
 - data/lib/puma/rack_default.rb +19 -4
 - data/lib/puma/reactor.rb +19 -10
 - data/lib/puma/request.rb +373 -153
 - data/lib/puma/runner.rb +74 -28
 - data/lib/puma/sd_notify.rb +149 -0
 - data/lib/puma/server.rb +127 -136
 - data/lib/puma/single.rb +13 -11
 - data/lib/puma/state_file.rb +39 -7
 - data/lib/puma/thread_pool.rb +33 -26
 - data/lib/puma/util.rb +20 -15
 - data/lib/puma.rb +28 -11
 - data/lib/rack/handler/puma.rb +113 -86
 - data/tools/Dockerfile +1 -1
 - metadata +15 -10
 - data/lib/puma/queue_close.rb +0 -26
 - data/lib/puma/systemd.rb +0 -46
 
    
        data/lib/puma/minissl.rb
    CHANGED
    
    | 
         @@ -1,11 +1,13 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            begin
         
     | 
| 
       4 
     | 
    
         
            -
              require 'io/wait'
         
     | 
| 
      
 4 
     | 
    
         
            +
              require 'io/wait' unless Puma::HAS_NATIVE_IO_WAIT
         
     | 
| 
       5 
5 
     | 
    
         
             
            rescue LoadError
         
     | 
| 
       6 
6 
     | 
    
         
             
            end
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
      
 8 
     | 
    
         
            +
            require 'open3'
         
     | 
| 
       8 
9 
     | 
    
         
             
            # need for Puma::MiniSSL::OPENSSL constants used in `HAS_TLS1_3`
         
     | 
| 
      
 10 
     | 
    
         
            +
            # use require, see https://github.com/puma/puma/pull/2381
         
     | 
| 
       9 
11 
     | 
    
         
             
            require 'puma/puma_http11'
         
     | 
| 
       10 
12 
     | 
    
         | 
| 
       11 
13 
     | 
    
         
             
            module Puma
         
     | 
| 
         @@ -13,15 +15,16 @@ module Puma 
     | 
|
| 
       13 
15 
     | 
    
         
             
                # Define constant at runtime, as it's easy to determine at built time,
         
     | 
| 
       14 
16 
     | 
    
         
             
                # but Puma could (it shouldn't) be loaded with an older OpenSSL version
         
     | 
| 
       15 
17 
     | 
    
         
             
                # @version 5.0.0
         
     | 
| 
       16 
     | 
    
         
            -
                HAS_TLS1_3 =  
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
      
 18 
     | 
    
         
            +
                HAS_TLS1_3 = IS_JRUBY ||
         
     | 
| 
      
 19 
     | 
    
         
            +
                    ((OPENSSL_VERSION[/ \d+\.\d+\.\d+/].split('.').map(&:to_i) <=> [1,1,1]) != -1 &&
         
     | 
| 
      
 20 
     | 
    
         
            +
                     (OPENSSL_LIBRARY_VERSION[/ \d+\.\d+\.\d+/].split('.').map(&:to_i) <=> [1,1,1]) !=-1)
         
     | 
| 
       19 
21 
     | 
    
         | 
| 
       20 
22 
     | 
    
         
             
                class Socket
         
     | 
| 
       21 
23 
     | 
    
         
             
                  def initialize(socket, engine)
         
     | 
| 
       22 
24 
     | 
    
         
             
                    @socket = socket
         
     | 
| 
       23 
25 
     | 
    
         
             
                    @engine = engine
         
     | 
| 
       24 
26 
     | 
    
         
             
                    @peercert = nil
         
     | 
| 
      
 27 
     | 
    
         
            +
                    @reuse = nil
         
     | 
| 
       25 
28 
     | 
    
         
             
                  end
         
     | 
| 
       26 
29 
     | 
    
         | 
| 
       27 
30 
     | 
    
         
             
                  # @!attribute [r] to_io
         
     | 
| 
         @@ -50,7 +53,7 @@ module Puma 
     | 
|
| 
       50 
53 
     | 
    
         
             
                  # is made with TLSv1.3 as an available protocol
         
     | 
| 
       51 
54 
     | 
    
         
             
                  # @version 5.0.0
         
     | 
| 
       52 
55 
     | 
    
         
             
                  def bad_tlsv1_3?
         
     | 
| 
       53 
     | 
    
         
            -
                    HAS_TLS1_3 &&  
     | 
| 
      
 56 
     | 
    
         
            +
                    HAS_TLS1_3 && ssl_version_state == ['TLSv1.3', 'SSLERR']
         
     | 
| 
       54 
57 
     | 
    
         
             
                  end
         
     | 
| 
       55 
58 
     | 
    
         
             
                  private :bad_tlsv1_3?
         
     | 
| 
       56 
59 
     | 
    
         | 
| 
         @@ -123,7 +126,7 @@ module Puma 
     | 
|
| 
       123 
126 
     | 
    
         
             
                    while true
         
     | 
| 
       124 
127 
     | 
    
         
             
                      wrote = @engine.write data
         
     | 
| 
       125 
128 
     | 
    
         | 
| 
       126 
     | 
    
         
            -
                      enc_wr = '' 
     | 
| 
      
 129 
     | 
    
         
            +
                      enc_wr = +''
         
     | 
| 
       127 
130 
     | 
    
         
             
                      while (enc = @engine.extract)
         
     | 
| 
       128 
131 
     | 
    
         
             
                        enc_wr << enc
         
     | 
| 
       129 
132 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -161,30 +164,15 @@ module Puma 
     | 
|
| 
       161 
164 
     | 
    
         
             
                    @socket.flush
         
     | 
| 
       162 
165 
     | 
    
         
             
                  end
         
     | 
| 
       163 
166 
     | 
    
         | 
| 
       164 
     | 
    
         
            -
                  def read_and_drop(timeout = 1)
         
     | 
| 
       165 
     | 
    
         
            -
                    return :timeout unless IO.select([@socket], nil, nil, timeout)
         
     | 
| 
       166 
     | 
    
         
            -
                    case @socket.read_nonblock(1024, exception: false)
         
     | 
| 
       167 
     | 
    
         
            -
                    when nil
         
     | 
| 
       168 
     | 
    
         
            -
                      :eof
         
     | 
| 
       169 
     | 
    
         
            -
                    when :wait_readable
         
     | 
| 
       170 
     | 
    
         
            -
                      :eagain
         
     | 
| 
       171 
     | 
    
         
            -
                    else
         
     | 
| 
       172 
     | 
    
         
            -
                      :drop
         
     | 
| 
       173 
     | 
    
         
            -
                    end
         
     | 
| 
       174 
     | 
    
         
            -
                  end
         
     | 
| 
       175 
     | 
    
         
            -
             
     | 
| 
       176 
     | 
    
         
            -
                  def should_drop_bytes?
         
     | 
| 
       177 
     | 
    
         
            -
                    @engine.init? || !@engine.shutdown
         
     | 
| 
       178 
     | 
    
         
            -
                  end
         
     | 
| 
       179 
     | 
    
         
            -
             
     | 
| 
       180 
167 
     | 
    
         
             
                  def close
         
     | 
| 
       181 
168 
     | 
    
         
             
                    begin
         
     | 
| 
       182 
     | 
    
         
            -
                       
     | 
| 
       183 
     | 
    
         
            -
             
     | 
| 
       184 
     | 
    
         
            -
             
     | 
| 
       185 
     | 
    
         
            -
             
     | 
| 
      
 169 
     | 
    
         
            +
                      unless @engine.shutdown
         
     | 
| 
      
 170 
     | 
    
         
            +
                        while alert_data = @engine.extract
         
     | 
| 
      
 171 
     | 
    
         
            +
                          @socket.write alert_data
         
     | 
| 
      
 172 
     | 
    
         
            +
                        end
         
     | 
| 
      
 173 
     | 
    
         
            +
                      end
         
     | 
| 
       186 
174 
     | 
    
         
             
                    rescue IOError, SystemCallError
         
     | 
| 
       187 
     | 
    
         
            -
                       
     | 
| 
      
 175 
     | 
    
         
            +
                      Puma::Util.purge_interrupt_queue
         
     | 
| 
       188 
176 
     | 
    
         
             
                      # nothing
         
     | 
| 
       189 
177 
     | 
    
         
             
                    ensure
         
     | 
| 
       190 
178 
     | 
    
         
             
                      @socket.close
         
     | 
| 
         @@ -210,10 +198,6 @@ module Puma 
     | 
|
| 
       210 
198 
     | 
    
         
             
                if IS_JRUBY
         
     | 
| 
       211 
199 
     | 
    
         
             
                  OPENSSL_NO_SSL3 = false
         
     | 
| 
       212 
200 
     | 
    
         
             
                  OPENSSL_NO_TLS1 = false
         
     | 
| 
       213 
     | 
    
         
            -
             
     | 
| 
       214 
     | 
    
         
            -
                  class SSLError < StandardError
         
     | 
| 
       215 
     | 
    
         
            -
                    # Define this for jruby even though it isn't used.
         
     | 
| 
       216 
     | 
    
         
            -
                  end
         
     | 
| 
       217 
201 
     | 
    
         
             
                end
         
     | 
| 
       218 
202 
     | 
    
         | 
| 
       219 
203 
     | 
    
         
             
                class Context
         
     | 
| 
         @@ -223,49 +207,159 @@ module Puma 
     | 
|
| 
       223 
207 
     | 
    
         
             
                  def initialize
         
     | 
| 
       224 
208 
     | 
    
         
             
                    @no_tlsv1   = false
         
     | 
| 
       225 
209 
     | 
    
         
             
                    @no_tlsv1_1 = false
         
     | 
| 
      
 210 
     | 
    
         
            +
                    @key = nil
         
     | 
| 
      
 211 
     | 
    
         
            +
                    @cert = nil
         
     | 
| 
      
 212 
     | 
    
         
            +
                    @key_pem = nil
         
     | 
| 
      
 213 
     | 
    
         
            +
                    @cert_pem = nil
         
     | 
| 
      
 214 
     | 
    
         
            +
                    @reuse = nil
         
     | 
| 
      
 215 
     | 
    
         
            +
                    @reuse_cache_size = nil
         
     | 
| 
      
 216 
     | 
    
         
            +
                    @reuse_timeout = nil
         
     | 
| 
      
 217 
     | 
    
         
            +
                  end
         
     | 
| 
      
 218 
     | 
    
         
            +
             
     | 
| 
      
 219 
     | 
    
         
            +
                  def check_file(file, desc)
         
     | 
| 
      
 220 
     | 
    
         
            +
                    raise ArgumentError, "#{desc} file '#{file}' does not exist" unless File.exist? file
         
     | 
| 
      
 221 
     | 
    
         
            +
                    raise ArgumentError, "#{desc} file '#{file}' is not readable" unless File.readable? file
         
     | 
| 
       226 
222 
     | 
    
         
             
                  end
         
     | 
| 
       227 
223 
     | 
    
         | 
| 
       228 
224 
     | 
    
         
             
                  if IS_JRUBY
         
     | 
| 
       229 
225 
     | 
    
         
             
                    # jruby-specific Context properties: java uses a keystore and password pair rather than a cert/key pair
         
     | 
| 
       230 
226 
     | 
    
         
             
                    attr_reader :keystore
         
     | 
| 
      
 227 
     | 
    
         
            +
                    attr_reader :keystore_type
         
     | 
| 
       231 
228 
     | 
    
         
             
                    attr_accessor :keystore_pass
         
     | 
| 
       232 
     | 
    
         
            -
                     
     | 
| 
      
 229 
     | 
    
         
            +
                    attr_reader :truststore
         
     | 
| 
      
 230 
     | 
    
         
            +
                    attr_reader :truststore_type
         
     | 
| 
      
 231 
     | 
    
         
            +
                    attr_accessor :truststore_pass
         
     | 
| 
      
 232 
     | 
    
         
            +
                    attr_reader :cipher_suites
         
     | 
| 
      
 233 
     | 
    
         
            +
                    attr_reader :protocols
         
     | 
| 
       233 
234 
     | 
    
         | 
| 
       234 
235 
     | 
    
         
             
                    def keystore=(keystore)
         
     | 
| 
       235 
     | 
    
         
            -
                       
     | 
| 
      
 236 
     | 
    
         
            +
                      check_file keystore, 'Keystore'
         
     | 
| 
       236 
237 
     | 
    
         
             
                      @keystore = keystore
         
     | 
| 
       237 
238 
     | 
    
         
             
                    end
         
     | 
| 
       238 
239 
     | 
    
         | 
| 
      
 240 
     | 
    
         
            +
                    def truststore=(truststore)
         
     | 
| 
      
 241 
     | 
    
         
            +
                      # NOTE: historically truststore was assumed the same as keystore, this is kept for backwards
         
     | 
| 
      
 242 
     | 
    
         
            +
                      # compatibility, to rely on JVM's trust defaults we allow setting `truststore = :default`
         
     | 
| 
      
 243 
     | 
    
         
            +
                      unless truststore.eql?(:default)
         
     | 
| 
      
 244 
     | 
    
         
            +
                        raise ArgumentError, "No such truststore file '#{truststore}'" unless File.exist?(truststore)
         
     | 
| 
      
 245 
     | 
    
         
            +
                      end
         
     | 
| 
      
 246 
     | 
    
         
            +
                      @truststore = truststore
         
     | 
| 
      
 247 
     | 
    
         
            +
                    end
         
     | 
| 
      
 248 
     | 
    
         
            +
             
     | 
| 
      
 249 
     | 
    
         
            +
                    def keystore_type=(type)
         
     | 
| 
      
 250 
     | 
    
         
            +
                      raise ArgumentError, "Invalid keystore type: #{type.inspect}" unless ['pkcs12', 'jks', nil].include?(type)
         
     | 
| 
      
 251 
     | 
    
         
            +
                      @keystore_type = type
         
     | 
| 
      
 252 
     | 
    
         
            +
                    end
         
     | 
| 
      
 253 
     | 
    
         
            +
             
     | 
| 
      
 254 
     | 
    
         
            +
                    def truststore_type=(type)
         
     | 
| 
      
 255 
     | 
    
         
            +
                      raise ArgumentError, "Invalid truststore type: #{type.inspect}" unless ['pkcs12', 'jks', nil].include?(type)
         
     | 
| 
      
 256 
     | 
    
         
            +
                      @truststore_type = type
         
     | 
| 
      
 257 
     | 
    
         
            +
                    end
         
     | 
| 
      
 258 
     | 
    
         
            +
             
     | 
| 
      
 259 
     | 
    
         
            +
                    def cipher_suites=(list)
         
     | 
| 
      
 260 
     | 
    
         
            +
                      list = list.split(',').map(&:strip) if list.is_a?(String)
         
     | 
| 
      
 261 
     | 
    
         
            +
                      @cipher_suites = list
         
     | 
| 
      
 262 
     | 
    
         
            +
                    end
         
     | 
| 
      
 263 
     | 
    
         
            +
             
     | 
| 
      
 264 
     | 
    
         
            +
                    # aliases for backwards compatibility
         
     | 
| 
      
 265 
     | 
    
         
            +
                    alias_method :ssl_cipher_list, :cipher_suites
         
     | 
| 
      
 266 
     | 
    
         
            +
                    alias_method :ssl_cipher_list=, :cipher_suites=
         
     | 
| 
      
 267 
     | 
    
         
            +
             
     | 
| 
      
 268 
     | 
    
         
            +
                    def protocols=(list)
         
     | 
| 
      
 269 
     | 
    
         
            +
                      list = list.split(',').map(&:strip) if list.is_a?(String)
         
     | 
| 
      
 270 
     | 
    
         
            +
                      @protocols = list
         
     | 
| 
      
 271 
     | 
    
         
            +
                    end
         
     | 
| 
      
 272 
     | 
    
         
            +
             
     | 
| 
       239 
273 
     | 
    
         
             
                    def check
         
     | 
| 
       240 
274 
     | 
    
         
             
                      raise "Keystore not configured" unless @keystore
         
     | 
| 
      
 275 
     | 
    
         
            +
                      # @truststore defaults to @keystore due backwards compatibility
         
     | 
| 
       241 
276 
     | 
    
         
             
                    end
         
     | 
| 
       242 
277 
     | 
    
         | 
| 
       243 
278 
     | 
    
         
             
                  else
         
     | 
| 
       244 
279 
     | 
    
         
             
                    # non-jruby Context properties
         
     | 
| 
       245 
280 
     | 
    
         
             
                    attr_reader :key
         
     | 
| 
      
 281 
     | 
    
         
            +
                    attr_reader :key_password_command
         
     | 
| 
       246 
282 
     | 
    
         
             
                    attr_reader :cert
         
     | 
| 
       247 
283 
     | 
    
         
             
                    attr_reader :ca
         
     | 
| 
      
 284 
     | 
    
         
            +
                    attr_reader :cert_pem
         
     | 
| 
      
 285 
     | 
    
         
            +
                    attr_reader :key_pem
         
     | 
| 
       248 
286 
     | 
    
         
             
                    attr_accessor :ssl_cipher_filter
         
     | 
| 
       249 
287 
     | 
    
         
             
                    attr_accessor :verification_flags
         
     | 
| 
       250 
288 
     | 
    
         | 
| 
      
 289 
     | 
    
         
            +
                    attr_reader :reuse, :reuse_cache_size, :reuse_timeout
         
     | 
| 
      
 290 
     | 
    
         
            +
             
     | 
| 
       251 
291 
     | 
    
         
             
                    def key=(key)
         
     | 
| 
       252 
     | 
    
         
            -
                       
     | 
| 
      
 292 
     | 
    
         
            +
                      check_file key, 'Key'
         
     | 
| 
       253 
293 
     | 
    
         
             
                      @key = key
         
     | 
| 
       254 
294 
     | 
    
         
             
                    end
         
     | 
| 
       255 
295 
     | 
    
         | 
| 
      
 296 
     | 
    
         
            +
                    def key_password_command=(key_password_command)
         
     | 
| 
      
 297 
     | 
    
         
            +
                      @key_password_command = key_password_command
         
     | 
| 
      
 298 
     | 
    
         
            +
                    end
         
     | 
| 
      
 299 
     | 
    
         
            +
             
     | 
| 
       256 
300 
     | 
    
         
             
                    def cert=(cert)
         
     | 
| 
       257 
     | 
    
         
            -
                       
     | 
| 
      
 301 
     | 
    
         
            +
                      check_file cert, 'Cert'
         
     | 
| 
       258 
302 
     | 
    
         
             
                      @cert = cert
         
     | 
| 
       259 
303 
     | 
    
         
             
                    end
         
     | 
| 
       260 
304 
     | 
    
         | 
| 
       261 
305 
     | 
    
         
             
                    def ca=(ca)
         
     | 
| 
       262 
     | 
    
         
            -
                       
     | 
| 
      
 306 
     | 
    
         
            +
                      check_file ca, 'ca'
         
     | 
| 
       263 
307 
     | 
    
         
             
                      @ca = ca
         
     | 
| 
       264 
308 
     | 
    
         
             
                    end
         
     | 
| 
       265 
309 
     | 
    
         | 
| 
      
 310 
     | 
    
         
            +
                    def cert_pem=(cert_pem)
         
     | 
| 
      
 311 
     | 
    
         
            +
                      raise ArgumentError, "'cert_pem' is not a String" unless cert_pem.is_a? String
         
     | 
| 
      
 312 
     | 
    
         
            +
                      @cert_pem = cert_pem
         
     | 
| 
      
 313 
     | 
    
         
            +
                    end
         
     | 
| 
      
 314 
     | 
    
         
            +
             
     | 
| 
      
 315 
     | 
    
         
            +
                    def key_pem=(key_pem)
         
     | 
| 
      
 316 
     | 
    
         
            +
                      raise ArgumentError, "'key_pem' is not a String" unless key_pem.is_a? String
         
     | 
| 
      
 317 
     | 
    
         
            +
                      @key_pem = key_pem
         
     | 
| 
      
 318 
     | 
    
         
            +
                    end
         
     | 
| 
      
 319 
     | 
    
         
            +
             
     | 
| 
       266 
320 
     | 
    
         
             
                    def check
         
     | 
| 
       267 
     | 
    
         
            -
                      raise "Key not configured"  
     | 
| 
       268 
     | 
    
         
            -
                      raise "Cert not configured"  
     | 
| 
      
 321 
     | 
    
         
            +
                      raise "Key not configured" if @key.nil? && @key_pem.nil?
         
     | 
| 
      
 322 
     | 
    
         
            +
                      raise "Cert not configured" if @cert.nil? && @cert_pem.nil?
         
     | 
| 
      
 323 
     | 
    
         
            +
                    end
         
     | 
| 
      
 324 
     | 
    
         
            +
             
     | 
| 
      
 325 
     | 
    
         
            +
                    # Executes the command to return the password needed to decrypt the key.
         
     | 
| 
      
 326 
     | 
    
         
            +
                    def key_password
         
     | 
| 
      
 327 
     | 
    
         
            +
                      raise "Key password command not configured" if @key_password_command.nil?
         
     | 
| 
      
 328 
     | 
    
         
            +
             
     | 
| 
      
 329 
     | 
    
         
            +
                      stdout_str, stderr_str, status = Open3.capture3(@key_password_command)
         
     | 
| 
      
 330 
     | 
    
         
            +
             
     | 
| 
      
 331 
     | 
    
         
            +
                      return stdout_str.chomp if status.success?
         
     | 
| 
      
 332 
     | 
    
         
            +
             
     | 
| 
      
 333 
     | 
    
         
            +
                      raise "Key password failed with code #{status.exitstatus}: #{stderr_str}"
         
     | 
| 
      
 334 
     | 
    
         
            +
                    end
         
     | 
| 
      
 335 
     | 
    
         
            +
             
     | 
| 
      
 336 
     | 
    
         
            +
                    # Controls session reuse.  Allowed values are as follows:
         
     | 
| 
      
 337 
     | 
    
         
            +
                    # * 'off' - matches the behavior of Puma 5.6 and earlier.  This is included
         
     | 
| 
      
 338 
     | 
    
         
            +
                    #   in case reuse 'on' is made the default in future Puma versions.
         
     | 
| 
      
 339 
     | 
    
         
            +
                    # * 'dflt' - sets session reuse on, with OpenSSL default cache size of
         
     | 
| 
      
 340 
     | 
    
         
            +
                    #   20k and default timeout of 300 seconds.
         
     | 
| 
      
 341 
     | 
    
         
            +
                    # * 's,t' - where s and t are integer strings, for size and timeout.
         
     | 
| 
      
 342 
     | 
    
         
            +
                    # * 's' - where s is an integer strings for size.
         
     | 
| 
      
 343 
     | 
    
         
            +
                    # * ',t' - where t is an integer strings for timeout.
         
     | 
| 
      
 344 
     | 
    
         
            +
                    #
         
     | 
| 
      
 345 
     | 
    
         
            +
                    def reuse=(reuse_str)
         
     | 
| 
      
 346 
     | 
    
         
            +
                      case reuse_str
         
     | 
| 
      
 347 
     | 
    
         
            +
                      when 'off'
         
     | 
| 
      
 348 
     | 
    
         
            +
                        @reuse = nil
         
     | 
| 
      
 349 
     | 
    
         
            +
                      when 'dflt'
         
     | 
| 
      
 350 
     | 
    
         
            +
                        @reuse = true
         
     | 
| 
      
 351 
     | 
    
         
            +
                      when /\A\d+\z/
         
     | 
| 
      
 352 
     | 
    
         
            +
                        @reuse = true
         
     | 
| 
      
 353 
     | 
    
         
            +
                        @reuse_cache_size = reuse_str.to_i
         
     | 
| 
      
 354 
     | 
    
         
            +
                      when /\A\d+,\d+\z/
         
     | 
| 
      
 355 
     | 
    
         
            +
                        @reuse = true
         
     | 
| 
      
 356 
     | 
    
         
            +
                        size, time = reuse_str.split ','
         
     | 
| 
      
 357 
     | 
    
         
            +
                        @reuse_cache_size = size.to_i
         
     | 
| 
      
 358 
     | 
    
         
            +
                        @reuse_timeout = time.to_i
         
     | 
| 
      
 359 
     | 
    
         
            +
                      when /\A,\d+\z/
         
     | 
| 
      
 360 
     | 
    
         
            +
                        @reuse = true
         
     | 
| 
      
 361 
     | 
    
         
            +
                        @reuse_timeout = reuse_str.delete(',').to_i
         
     | 
| 
      
 362 
     | 
    
         
            +
                      end
         
     | 
| 
       269 
363 
     | 
    
         
             
                    end
         
     | 
| 
       270 
364 
     | 
    
         
             
                  end
         
     | 
| 
       271 
365 
     | 
    
         | 
    
        data/lib/puma/null_io.rb
    CHANGED
    
    
| 
         @@ -0,0 +1,90 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative '../plugin'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            # Puma's systemd integration allows Puma to inform systemd:
         
     | 
| 
      
 6 
     | 
    
         
            +
            #  1. when it has successfully started
         
     | 
| 
      
 7 
     | 
    
         
            +
            #  2. when it is starting shutdown
         
     | 
| 
      
 8 
     | 
    
         
            +
            #  3. periodically for a liveness check with a watchdog thread
         
     | 
| 
      
 9 
     | 
    
         
            +
            #  4. periodically set the status
         
     | 
| 
      
 10 
     | 
    
         
            +
            Puma::Plugin.create do
         
     | 
| 
      
 11 
     | 
    
         
            +
              def start(launcher)
         
     | 
| 
      
 12 
     | 
    
         
            +
                require_relative '../sd_notify'
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                launcher.log_writer.log "* Enabling systemd notification integration"
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                # hook_events
         
     | 
| 
      
 17 
     | 
    
         
            +
                launcher.events.on_booted { Puma::SdNotify.ready }
         
     | 
| 
      
 18 
     | 
    
         
            +
                launcher.events.on_stopped { Puma::SdNotify.stopping }
         
     | 
| 
      
 19 
     | 
    
         
            +
                launcher.events.on_restart { Puma::SdNotify.reloading }
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                # start watchdog
         
     | 
| 
      
 22 
     | 
    
         
            +
                if Puma::SdNotify.watchdog?
         
     | 
| 
      
 23 
     | 
    
         
            +
                  ping_f = watchdog_sleep_time
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  in_background do
         
     | 
| 
      
 26 
     | 
    
         
            +
                    launcher.log_writer.log "Pinging systemd watchdog every #{ping_f.round(1)} sec"
         
     | 
| 
      
 27 
     | 
    
         
            +
                    loop do
         
     | 
| 
      
 28 
     | 
    
         
            +
                      sleep ping_f
         
     | 
| 
      
 29 
     | 
    
         
            +
                      Puma::SdNotify.watchdog
         
     | 
| 
      
 30 
     | 
    
         
            +
                    end
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                # start status loop
         
     | 
| 
      
 35 
     | 
    
         
            +
                instance = self
         
     | 
| 
      
 36 
     | 
    
         
            +
                sleep_time = 1.0
         
     | 
| 
      
 37 
     | 
    
         
            +
                in_background do
         
     | 
| 
      
 38 
     | 
    
         
            +
                  launcher.log_writer.log "Sending status to systemd every #{sleep_time.round(1)} sec"
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  loop do
         
     | 
| 
      
 41 
     | 
    
         
            +
                    sleep sleep_time
         
     | 
| 
      
 42 
     | 
    
         
            +
                    # TODO: error handling?
         
     | 
| 
      
 43 
     | 
    
         
            +
                    Puma::SdNotify.status(instance.status)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
              def status
         
     | 
| 
      
 49 
     | 
    
         
            +
                if clustered?
         
     | 
| 
      
 50 
     | 
    
         
            +
                  messages = stats[:worker_status].map do |worker|
         
     | 
| 
      
 51 
     | 
    
         
            +
                    common_message(worker[:last_status])
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end.join(',')
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  "Puma #{Puma::Const::VERSION}: cluster: #{booted_workers}/#{workers}, worker_status: [#{messages}]"
         
     | 
| 
      
 55 
     | 
    
         
            +
                else
         
     | 
| 
      
 56 
     | 
    
         
            +
                  "Puma #{Puma::Const::VERSION}: worker: #{common_message(stats)}"
         
     | 
| 
      
 57 
     | 
    
         
            +
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
              end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
              private
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
              def watchdog_sleep_time
         
     | 
| 
      
 63 
     | 
    
         
            +
                usec = Integer(ENV["WATCHDOG_USEC"])
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                sec_f = usec / 1_000_000.0
         
     | 
| 
      
 66 
     | 
    
         
            +
                # "It is recommended that a daemon sends a keep-alive notification message
         
     | 
| 
      
 67 
     | 
    
         
            +
                # to the service manager every half of the time returned here."
         
     | 
| 
      
 68 
     | 
    
         
            +
                sec_f / 2
         
     | 
| 
      
 69 
     | 
    
         
            +
              end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
              def stats
         
     | 
| 
      
 72 
     | 
    
         
            +
                Puma.stats_hash
         
     | 
| 
      
 73 
     | 
    
         
            +
              end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
              def clustered?
         
     | 
| 
      
 76 
     | 
    
         
            +
                stats.has_key?(:workers)
         
     | 
| 
      
 77 
     | 
    
         
            +
              end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
              def workers
         
     | 
| 
      
 80 
     | 
    
         
            +
                stats.fetch(:workers, 1)
         
     | 
| 
      
 81 
     | 
    
         
            +
              end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
              def booted_workers
         
     | 
| 
      
 84 
     | 
    
         
            +
                stats.fetch(:booted_workers, 1)
         
     | 
| 
      
 85 
     | 
    
         
            +
              end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
              def common_message(stats)
         
     | 
| 
      
 88 
     | 
    
         
            +
                "{ #{stats[:running]}/#{stats[:max_threads]} threads, #{stats[:pool_capacity]} available, #{stats[:backlog]} backlog }"
         
     | 
| 
      
 89 
     | 
    
         
            +
              end
         
     | 
| 
      
 90 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/puma/plugin.rb
    CHANGED
    
    | 
         @@ -64,7 +64,7 @@ module Puma 
     | 
|
| 
       64 
64 
     | 
    
         
             
                def fire_background
         
     | 
| 
       65 
65 
     | 
    
         
             
                  @background.each_with_index do |b, i|
         
     | 
| 
       66 
66 
     | 
    
         
             
                    Thread.new do
         
     | 
| 
       67 
     | 
    
         
            -
                      Puma.set_thread_name " 
     | 
| 
      
 67 
     | 
    
         
            +
                      Puma.set_thread_name "plgn bg #{i}"
         
     | 
| 
       68 
68 
     | 
    
         
             
                      b.call
         
     | 
| 
       69 
69 
     | 
    
         
             
                    end
         
     | 
| 
       70 
70 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -91,7 +91,7 @@ module Puma 
     | 
|
| 
       91 
91 
     | 
    
         
             
                  path = ary.first[CALLER_FILE]
         
     | 
| 
       92 
92 
     | 
    
         | 
| 
       93 
93 
     | 
    
         
             
                  m = %r!puma/plugin/([^/]*)\.rb$!.match(path)
         
     | 
| 
       94 
     | 
    
         
            -
                   
     | 
| 
      
 94 
     | 
    
         
            +
                  m[1]
         
     | 
| 
       95 
95 
     | 
    
         
             
                end
         
     | 
| 
       96 
96 
     | 
    
         | 
| 
       97 
97 
     | 
    
         
             
                def self.create(&blk)
         
     | 
    
        data/lib/puma/rack/builder.rb
    CHANGED
    
    | 
         @@ -102,13 +102,13 @@ module Puma::Rack 
     | 
|
| 
       102 
102 
     | 
    
         
             
                  begin
         
     | 
| 
       103 
103 
     | 
    
         
             
                    info = []
         
     | 
| 
       104 
104 
     | 
    
         
             
                    server = Rack::Handler.get(options[:server]) || Rack::Handler.default(options)
         
     | 
| 
       105 
     | 
    
         
            -
                    if server 
     | 
| 
      
 105 
     | 
    
         
            +
                    if server&.respond_to?(:valid_options)
         
     | 
| 
       106 
106 
     | 
    
         
             
                      info << ""
         
     | 
| 
       107 
107 
     | 
    
         
             
                      info << "Server-specific options for #{server.name}:"
         
     | 
| 
       108 
108 
     | 
    
         | 
| 
       109 
109 
     | 
    
         
             
                      has_options = false
         
     | 
| 
       110 
110 
     | 
    
         
             
                      server.valid_options.each do |name, description|
         
     | 
| 
       111 
     | 
    
         
            -
                        next if  
     | 
| 
      
 111 
     | 
    
         
            +
                        next if /^(Host|Port)[^a-zA-Z]/.match? name.to_s  # ignore handler's host and port options, we do our own.
         
     | 
| 
       112 
112 
     | 
    
         | 
| 
       113 
113 
     | 
    
         
             
                        info << "  -O %-21s %s" % [name, description]
         
     | 
| 
       114 
114 
     | 
    
         
             
                        has_options = true
         
     | 
| 
         @@ -165,7 +165,7 @@ module Puma::Rack 
     | 
|
| 
       165 
165 
     | 
    
         
             
                    require config
         
     | 
| 
       166 
166 
     | 
    
         
             
                    app = Object.const_get(::File.basename(config, '.rb').capitalize)
         
     | 
| 
       167 
167 
     | 
    
         
             
                  end
         
     | 
| 
       168 
     | 
    
         
            -
                   
     | 
| 
      
 168 
     | 
    
         
            +
                  [app, options]
         
     | 
| 
       169 
169 
     | 
    
         
             
                end
         
     | 
| 
       170 
170 
     | 
    
         | 
| 
       171 
171 
     | 
    
         
             
                def self.new_from_string(builder_script, file="(rackup)")
         
     | 
| 
         @@ -173,7 +173,7 @@ module Puma::Rack 
     | 
|
| 
       173 
173 
     | 
    
         
             
                    TOPLEVEL_BINDING, file, 0
         
     | 
| 
       174 
174 
     | 
    
         
             
                end
         
     | 
| 
       175 
175 
     | 
    
         | 
| 
       176 
     | 
    
         
            -
                def initialize(default_app = nil 
     | 
| 
      
 176 
     | 
    
         
            +
                def initialize(default_app = nil, &block)
         
     | 
| 
       177 
177 
     | 
    
         
             
                  @use, @map, @run, @warmup = [], nil, default_app, nil
         
     | 
| 
       178 
178 
     | 
    
         | 
| 
       179 
179 
     | 
    
         
             
                  # Conditionally load rack now, so that any rack middlewares,
         
     | 
| 
         @@ -183,7 +183,7 @@ module Puma::Rack 
     | 
|
| 
       183 
183 
     | 
    
         
             
                  rescue LoadError
         
     | 
| 
       184 
184 
     | 
    
         
             
                  end
         
     | 
| 
       185 
185 
     | 
    
         | 
| 
       186 
     | 
    
         
            -
                  instance_eval(&block) if  
     | 
| 
      
 186 
     | 
    
         
            +
                  instance_eval(&block) if block
         
     | 
| 
       187 
187 
     | 
    
         
             
                end
         
     | 
| 
       188 
188 
     | 
    
         | 
| 
       189 
189 
     | 
    
         
             
                def self.app(default_app = nil, &block)
         
     | 
| 
         @@ -276,7 +276,7 @@ module Puma::Rack 
     | 
|
| 
       276 
276 
     | 
    
         
             
                  app = @map ? generate_map(@run, @map) : @run
         
     | 
| 
       277 
277 
     | 
    
         
             
                  fail "missing run or map statement" unless app
         
     | 
| 
       278 
278 
     | 
    
         
             
                  app = @use.reverse.inject(app) { |a,e| e[a] }
         
     | 
| 
       279 
     | 
    
         
            -
                  @warmup 
     | 
| 
      
 279 
     | 
    
         
            +
                  @warmup&.call app
         
     | 
| 
       280 
280 
     | 
    
         
             
                  app
         
     | 
| 
       281 
281 
     | 
    
         
             
                end
         
     | 
| 
       282 
282 
     | 
    
         | 
| 
         @@ -287,7 +287,7 @@ module Puma::Rack 
     | 
|
| 
       287 
287 
     | 
    
         
             
                private
         
     | 
| 
       288 
288 
     | 
    
         | 
| 
       289 
289 
     | 
    
         
             
                def generate_map(default_app, mapping)
         
     | 
| 
       290 
     | 
    
         
            -
                   
     | 
| 
      
 290 
     | 
    
         
            +
                  require_relative 'urlmap'
         
     | 
| 
       291 
291 
     | 
    
         | 
| 
       292 
292 
     | 
    
         
             
                  mapped = default_app ? {'/' => default_app} : {}
         
     | 
| 
       293 
293 
     | 
    
         
             
                  mapping.each { |r,b| mapped[r] = self.class.new(default_app, &b).to_app }
         
     | 
    
        data/lib/puma/rack_default.rb
    CHANGED
    
    | 
         @@ -1,9 +1,24 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative '../rack/handler/puma'
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
      
 5 
     | 
    
         
            +
            # rackup was removed in Rack 3, it is now a separate gem
         
     | 
| 
      
 6 
     | 
    
         
            +
            if Object.const_defined? :Rackup
         
     | 
| 
      
 7 
     | 
    
         
            +
              module Rackup
         
     | 
| 
      
 8 
     | 
    
         
            +
                module Handler
         
     | 
| 
      
 9 
     | 
    
         
            +
                  def self.default(options = {})
         
     | 
| 
      
 10 
     | 
    
         
            +
                    ::Rackup::Handler::Puma
         
     | 
| 
      
 11 
     | 
    
         
            +
                  end
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
       8 
13 
     | 
    
         
             
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
            elsif Object.const_defined?(:Rack) && Rack.release < '3'
         
     | 
| 
      
 15 
     | 
    
         
            +
              module Rack
         
     | 
| 
      
 16 
     | 
    
         
            +
                module Handler
         
     | 
| 
      
 17 
     | 
    
         
            +
                  def self.default(options = {})
         
     | 
| 
      
 18 
     | 
    
         
            +
                    ::Rack::Handler::Puma
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
            else
         
     | 
| 
      
 23 
     | 
    
         
            +
              raise "Rack 3 must be used with the Rackup gem"
         
     | 
| 
       9 
24 
     | 
    
         
             
            end
         
     | 
    
        data/lib/puma/reactor.rb
    CHANGED
    
    | 
         @@ -1,7 +1,5 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            require 'puma/queue_close' unless ::Queue.instance_methods.include? :close
         
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
3 
     | 
    
         
             
            module Puma
         
     | 
| 
       6 
4 
     | 
    
         
             
              class UnsupportedBackend < StandardError; end
         
     | 
| 
       7 
5 
     | 
    
         | 
| 
         @@ -22,10 +20,12 @@ module Puma 
     | 
|
| 
       22 
20 
     | 
    
         
             
                # its timeout elapses, or when the Reactor shuts down.
         
     | 
| 
       23 
21 
     | 
    
         
             
                def initialize(backend, &block)
         
     | 
| 
       24 
22 
     | 
    
         
             
                  require 'nio'
         
     | 
| 
       25 
     | 
    
         
            -
                   
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
      
 23 
     | 
    
         
            +
                  valid_backends = [:auto, *::NIO::Selector.backends]
         
     | 
| 
      
 24 
     | 
    
         
            +
                  unless valid_backends.include?(backend)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    raise ArgumentError.new("unsupported IO selector backend: #{backend} (available backends: #{valid_backends.join(', ')})")
         
     | 
| 
       27 
26 
     | 
    
         
             
                  end
         
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  @selector = ::NIO::Selector.new(NIO::Selector.backends.delete(backend))
         
     | 
| 
       29 
29 
     | 
    
         
             
                  @input = Queue.new
         
     | 
| 
       30 
30 
     | 
    
         
             
                  @timeouts = []
         
     | 
| 
       31 
31 
     | 
    
         
             
                  @block = block
         
     | 
| 
         @@ -50,7 +50,7 @@ module Puma 
     | 
|
| 
       50 
50 
     | 
    
         
             
                  @input << client
         
     | 
| 
       51 
51 
     | 
    
         
             
                  @selector.wakeup
         
     | 
| 
       52 
52 
     | 
    
         
             
                  true
         
     | 
| 
       53 
     | 
    
         
            -
                rescue ClosedQueueError
         
     | 
| 
      
 53 
     | 
    
         
            +
                rescue ClosedQueueError, IOError # Ignore if selector is already closed
         
     | 
| 
       54 
54 
     | 
    
         
             
                  false
         
     | 
| 
       55 
55 
     | 
    
         
             
                end
         
     | 
| 
       56 
56 
     | 
    
         | 
| 
         @@ -61,12 +61,13 @@ module Puma 
     | 
|
| 
       61 
61 
     | 
    
         
             
                    @selector.wakeup
         
     | 
| 
       62 
62 
     | 
    
         
             
                  rescue IOError # Ignore if selector is already closed
         
     | 
| 
       63 
63 
     | 
    
         
             
                  end
         
     | 
| 
       64 
     | 
    
         
            -
                  @thread 
     | 
| 
      
 64 
     | 
    
         
            +
                  @thread&.join
         
     | 
| 
       65 
65 
     | 
    
         
             
                end
         
     | 
| 
       66 
66 
     | 
    
         | 
| 
       67 
67 
     | 
    
         
             
                private
         
     | 
| 
       68 
68 
     | 
    
         | 
| 
       69 
69 
     | 
    
         
             
                def select_loop
         
     | 
| 
      
 70 
     | 
    
         
            +
                  close_selector = true
         
     | 
| 
       70 
71 
     | 
    
         
             
                  begin
         
     | 
| 
       71 
72 
     | 
    
         
             
                    until @input.closed? && @input.empty?
         
     | 
| 
       72 
73 
     | 
    
         
             
                      # Wakeup any registered object that receives incoming data.
         
     | 
| 
         @@ -76,7 +77,7 @@ module Puma 
     | 
|
| 
       76 
77 
     | 
    
         | 
| 
       77 
78 
     | 
    
         
             
                      # Wakeup all objects that timed out.
         
     | 
| 
       78 
79 
     | 
    
         
             
                      timed_out = @timeouts.take_while {|t| t.timeout == 0}
         
     | 
| 
       79 
     | 
    
         
            -
                      timed_out.each 
     | 
| 
      
 80 
     | 
    
         
            +
                      timed_out.each { |c| wakeup! c }
         
     | 
| 
       80 
81 
     | 
    
         | 
| 
       81 
82 
     | 
    
         
             
                      unless @input.empty?
         
     | 
| 
       82 
83 
     | 
    
         
             
                        until @input.empty?
         
     | 
| 
         @@ -89,11 +90,19 @@ module Puma 
     | 
|
| 
       89 
90 
     | 
    
         
             
                  rescue StandardError => e
         
     | 
| 
       90 
91 
     | 
    
         
             
                    STDERR.puts "Error in reactor loop escaped: #{e.message} (#{e.class})"
         
     | 
| 
       91 
92 
     | 
    
         
             
                    STDERR.puts e.backtrace
         
     | 
| 
       92 
     | 
    
         
            -
             
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                    # NoMethodError may be rarely raised when calling @selector.select, which
         
     | 
| 
      
 95 
     | 
    
         
            +
                    # is odd.  Regardless, it may continue for thousands of calls if retried.
         
     | 
| 
      
 96 
     | 
    
         
            +
                    # Also, when it raises, @selector.close also raises an error.
         
     | 
| 
      
 97 
     | 
    
         
            +
                    if NoMethodError === e
         
     | 
| 
      
 98 
     | 
    
         
            +
                      close_selector = false
         
     | 
| 
      
 99 
     | 
    
         
            +
                    else
         
     | 
| 
      
 100 
     | 
    
         
            +
                      retry
         
     | 
| 
      
 101 
     | 
    
         
            +
                    end
         
     | 
| 
       93 
102 
     | 
    
         
             
                  end
         
     | 
| 
       94 
103 
     | 
    
         
             
                  # Wakeup all remaining objects on shutdown.
         
     | 
| 
       95 
104 
     | 
    
         
             
                  @timeouts.each(&@block)
         
     | 
| 
       96 
     | 
    
         
            -
                  @selector.close
         
     | 
| 
      
 105 
     | 
    
         
            +
                  @selector.close if close_selector
         
     | 
| 
       97 
106 
     | 
    
         
             
                end
         
     | 
| 
       98 
107 
     | 
    
         | 
| 
       99 
108 
     | 
    
         
             
                # Start monitoring the object.
         
     |