puma 3.6.0 → 3.12.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 +5 -5
- data/{History.txt → History.md} +293 -79
- data/README.md +143 -227
- data/docs/architecture.md +36 -0
- data/{DEPLOYMENT.md → docs/deployment.md} +0 -0
- data/docs/images/puma-connection-flow-no-reactor.png +0 -0
- data/docs/images/puma-connection-flow.png +0 -0
- data/docs/images/puma-general-arch.png +0 -0
- data/docs/plugins.md +28 -0
- data/docs/restart.md +39 -0
- data/docs/signals.md +56 -3
- data/docs/systemd.md +124 -22
- data/ext/puma_http11/extconf.rb +2 -0
- data/ext/puma_http11/http11_parser.c +85 -84
- data/ext/puma_http11/http11_parser.h +1 -0
- data/ext/puma_http11/http11_parser.rl +10 -9
- data/ext/puma_http11/io_buffer.c +7 -7
- data/ext/puma_http11/mini_ssl.c +62 -6
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +13 -16
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +15 -2
- data/ext/puma_http11/puma_http11.c +1 -0
- data/lib/puma.rb +13 -5
- data/lib/puma/app/status.rb +8 -0
- data/lib/puma/binder.rb +21 -14
- data/lib/puma/cli.rb +49 -33
- data/lib/puma/client.rb +39 -4
- data/lib/puma/cluster.rb +51 -11
- data/lib/puma/commonlogger.rb +19 -20
- data/lib/puma/compat.rb +3 -7
- data/lib/puma/configuration.rb +133 -130
- data/lib/puma/const.rb +13 -37
- data/lib/puma/control_cli.rb +38 -35
- data/lib/puma/convenient.rb +3 -3
- data/lib/puma/detect.rb +3 -1
- data/lib/puma/dsl.rb +80 -58
- data/lib/puma/events.rb +6 -8
- data/lib/puma/io_buffer.rb +1 -1
- data/lib/puma/jruby_restart.rb +0 -1
- data/lib/puma/launcher.rb +52 -30
- data/lib/puma/minissl.rb +73 -4
- data/lib/puma/null_io.rb +6 -13
- data/lib/puma/plugin/tmp_restart.rb +1 -2
- data/lib/puma/rack/builder.rb +3 -0
- data/lib/puma/rack/urlmap.rb +9 -8
- data/lib/puma/reactor.rb +135 -0
- data/lib/puma/runner.rb +23 -1
- data/lib/puma/server.rb +117 -34
- data/lib/puma/single.rb +14 -3
- data/lib/puma/thread_pool.rb +67 -20
- data/lib/puma/util.rb +1 -5
- data/lib/rack/handler/puma.rb +58 -17
- data/tools/jungle/README.md +12 -2
- data/tools/jungle/init.d/README.md +9 -2
- data/tools/jungle/init.d/puma +32 -62
- data/tools/jungle/init.d/run-puma +5 -1
- data/tools/jungle/rc.d/README.md +74 -0
- data/tools/jungle/rc.d/puma +61 -0
- data/tools/jungle/rc.d/puma.conf +10 -0
- data/tools/trickletest.rb +1 -1
- metadata +22 -92
- data/Gemfile +0 -13
- data/Manifest.txt +0 -77
- data/Rakefile +0 -158
- data/lib/puma/rack/backports/uri/common_18.rb +0 -59
- data/lib/puma/rack/backports/uri/common_192.rb +0 -55
- data/puma.gemspec +0 -52
    
        data/lib/puma/app/status.rb
    CHANGED
    
    | @@ -55,6 +55,14 @@ module Puma | |
| 55 55 | 
             
                        return rack_response(200, OK_STATUS)
         | 
| 56 56 | 
             
                      end
         | 
| 57 57 |  | 
| 58 | 
            +
                    when /\/gc$/
         | 
| 59 | 
            +
                      GC.start
         | 
| 60 | 
            +
                      return rack_response(200, OK_STATUS)
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                    when /\/gc-stats$/
         | 
| 63 | 
            +
                      json = "{" + GC.stat.map { |k, v| "\"#{k}\": #{v}" }.join(",") + "}"
         | 
| 64 | 
            +
                      return rack_response(200, json)
         | 
| 65 | 
            +
             | 
| 58 66 | 
             
                    when /\/stats$/
         | 
| 59 67 | 
             
                      return rack_response(200, @cli.stats)
         | 
| 60 68 | 
             
                    else
         | 
    
        data/lib/puma/binder.rb
    CHANGED
    
    | @@ -1,5 +1,8 @@ | |
| 1 | 
            -
            require 'puma/const'
         | 
| 2 1 | 
             
            require 'uri'
         | 
| 2 | 
            +
            require 'socket'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require 'puma/const'
         | 
| 5 | 
            +
            require 'puma/util'
         | 
| 3 6 |  | 
| 4 7 | 
             
            module Puma
         | 
| 5 8 | 
             
              class Binder
         | 
| @@ -102,7 +105,7 @@ module Puma | |
| 102 105 | 
             
                        io = add_tcp_listener uri.host, uri.port, opt, bak
         | 
| 103 106 | 
             
                      end
         | 
| 104 107 |  | 
| 105 | 
            -
                      @listeners << [str, io]
         | 
| 108 | 
            +
                      @listeners << [str, io] if io
         | 
| 106 109 | 
             
                    when "unix"
         | 
| 107 110 | 
             
                      path = "#{uri.host}#{uri.path}".gsub("%20", " ")
         | 
| 108 111 |  | 
| @@ -117,7 +120,7 @@ module Puma | |
| 117 120 |  | 
| 118 121 | 
             
                        umask = nil
         | 
| 119 122 | 
             
                        mode = nil
         | 
| 120 | 
            -
                        backlog =  | 
| 123 | 
            +
                        backlog = 1024
         | 
| 121 124 |  | 
| 122 125 | 
             
                        if uri.query
         | 
| 123 126 | 
             
                          params = Util.parse_query uri.query
         | 
| @@ -140,11 +143,11 @@ module Puma | |
| 140 143 |  | 
| 141 144 | 
             
                      @listeners << [str, io]
         | 
| 142 145 | 
             
                    when "ssl"
         | 
| 143 | 
            -
                      MiniSSL.check
         | 
| 144 | 
            -
             | 
| 145 146 | 
             
                      params = Util.parse_query uri.query
         | 
| 146 147 | 
             
                      require 'puma/minissl'
         | 
| 147 148 |  | 
| 149 | 
            +
                      MiniSSL.check
         | 
| 150 | 
            +
             | 
| 148 151 | 
             
                      ctx = MiniSSL::Context.new
         | 
| 149 152 |  | 
| 150 153 | 
             
                      if defined?(JRUBY_VERSION)
         | 
| @@ -159,6 +162,7 @@ module Puma | |
| 159 162 | 
             
                        end
         | 
| 160 163 |  | 
| 161 164 | 
             
                        ctx.keystore_pass = params['keystore-pass']
         | 
| 165 | 
            +
                        ctx.ssl_cipher_list = params['ssl_cipher_list'] if params['ssl_cipher_list']
         | 
| 162 166 | 
             
                      else
         | 
| 163 167 | 
             
                        unless params['key']
         | 
| 164 168 | 
             
                          @events.error "Please specify the SSL key via 'key='"
         | 
| @@ -179,6 +183,7 @@ module Puma | |
| 179 183 | 
             
                        end
         | 
| 180 184 |  | 
| 181 185 | 
             
                        ctx.ca = params['ca'] if params['ca']
         | 
| 186 | 
            +
                        ctx.ssl_cipher_filter = params['ssl_cipher_filter'] if params['ssl_cipher_filter']
         | 
| 182 187 | 
             
                      end
         | 
| 183 188 |  | 
| 184 189 | 
             
                      if params['verify_mode']
         | 
| @@ -206,7 +211,7 @@ module Puma | |
| 206 211 | 
             
                        io = add_ssl_listener uri.host, uri.port, ctx
         | 
| 207 212 | 
             
                      end
         | 
| 208 213 |  | 
| 209 | 
            -
                      @listeners << [str, io]
         | 
| 214 | 
            +
                      @listeners << [str, io] if io
         | 
| 210 215 | 
             
                    else
         | 
| 211 216 | 
             
                      logger.error "Invalid URI: #{str}"
         | 
| 212 217 | 
             
                    end
         | 
| @@ -242,9 +247,10 @@ module Puma | |
| 242 247 | 
             
                  end
         | 
| 243 248 | 
             
                end
         | 
| 244 249 |  | 
| 245 | 
            -
                def  | 
| 246 | 
            -
                   | 
| 247 | 
            -
             | 
| 250 | 
            +
                def loopback_addresses
         | 
| 251 | 
            +
                  Socket.ip_address_list.select do |addrinfo|
         | 
| 252 | 
            +
                    addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
         | 
| 253 | 
            +
                  end.map { |addrinfo| addrinfo.ip_address }.uniq
         | 
| 248 254 | 
             
                end
         | 
| 249 255 |  | 
| 250 256 | 
             
                # Tell the server to listen on host +host+, port +port+.
         | 
| @@ -256,7 +262,7 @@ module Puma | |
| 256 262 | 
             
                #
         | 
| 257 263 | 
             
                def add_tcp_listener(host, port, optimize_for_latency=true, backlog=1024)
         | 
| 258 264 | 
             
                  if host == "localhost"
         | 
| 259 | 
            -
                     | 
| 265 | 
            +
                    loopback_addresses.each do |addr|
         | 
| 260 266 | 
             
                      add_tcp_listener addr, port, optimize_for_latency, backlog
         | 
| 261 267 | 
             
                    end
         | 
| 262 268 | 
             
                    return
         | 
| @@ -295,8 +301,8 @@ module Puma | |
| 295 301 | 
             
                  MiniSSL.check
         | 
| 296 302 |  | 
| 297 303 | 
             
                  if host == "localhost"
         | 
| 298 | 
            -
                     | 
| 299 | 
            -
                      add_ssl_listener addr, port, optimize_for_latency, backlog
         | 
| 304 | 
            +
                    loopback_addresses.each do |addr|
         | 
| 305 | 
            +
                      add_ssl_listener addr, port, ctx, optimize_for_latency, backlog
         | 
| 300 306 | 
             
                    end
         | 
| 301 307 | 
             
                    return
         | 
| 302 308 | 
             
                  end
         | 
| @@ -309,6 +315,7 @@ module Puma | |
| 309 315 | 
             
                  s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
         | 
| 310 316 | 
             
                  s.listen backlog
         | 
| 311 317 |  | 
| 318 | 
            +
             | 
| 312 319 | 
             
                  ssl = MiniSSL::Server.new s, ctx
         | 
| 313 320 | 
             
                  env = @proto_env.dup
         | 
| 314 321 | 
             
                  env[HTTPS_KEY] = HTTPS
         | 
| @@ -340,7 +347,7 @@ module Puma | |
| 340 347 |  | 
| 341 348 | 
             
                # Tell the server to listen on +path+ as a UNIX domain socket.
         | 
| 342 349 | 
             
                #
         | 
| 343 | 
            -
                def add_unix_listener(path, umask=nil, mode=nil, backlog= | 
| 350 | 
            +
                def add_unix_listener(path, umask=nil, mode=nil, backlog=1024)
         | 
| 344 351 | 
             
                  @unix_paths << path
         | 
| 345 352 |  | 
| 346 353 | 
             
                  # Let anyone connect by default
         | 
| @@ -361,7 +368,7 @@ module Puma | |
| 361 368 | 
             
                    end
         | 
| 362 369 |  | 
| 363 370 | 
             
                    s = UNIXServer.new(path)
         | 
| 364 | 
            -
                    s.listen backlog | 
| 371 | 
            +
                    s.listen backlog
         | 
| 365 372 | 
             
                    @ios << s
         | 
| 366 373 | 
             
                  ensure
         | 
| 367 374 | 
             
                    File.umask old_mask
         | 
    
        data/lib/puma/cli.rb
    CHANGED
    
    | @@ -1,7 +1,11 @@ | |
| 1 1 | 
             
            require 'optparse'
         | 
| 2 2 | 
             
            require 'uri'
         | 
| 3 3 |  | 
| 4 | 
            +
            require 'puma'
         | 
| 5 | 
            +
            require 'puma/configuration'
         | 
| 4 6 | 
             
            require 'puma/launcher'
         | 
| 7 | 
            +
            require 'puma/const'
         | 
| 8 | 
            +
            require 'puma/events'
         | 
| 5 9 |  | 
| 6 10 | 
             
            module Puma
         | 
| 7 11 | 
             
              class << self
         | 
| @@ -44,21 +48,21 @@ module Puma | |
| 44 48 | 
             
                    @parser.parse! @argv
         | 
| 45 49 |  | 
| 46 50 | 
             
                    if file = @argv.shift
         | 
| 47 | 
            -
                      @conf.configure do | | 
| 48 | 
            -
                         | 
| 51 | 
            +
                      @conf.configure do |user_config, file_config|
         | 
| 52 | 
            +
                        file_config.rackup file
         | 
| 49 53 | 
             
                      end
         | 
| 50 54 | 
             
                    end
         | 
| 51 55 | 
             
                  rescue UnsupportedOption
         | 
| 52 56 | 
             
                    exit 1
         | 
| 53 57 | 
             
                  end
         | 
| 54 58 |  | 
| 55 | 
            -
                  @conf.configure do | | 
| 59 | 
            +
                  @conf.configure do |user_config, file_config|
         | 
| 56 60 | 
             
                    if @stdout || @stderr
         | 
| 57 | 
            -
                       | 
| 61 | 
            +
                      user_config.stdout_redirect @stdout, @stderr, @append
         | 
| 58 62 | 
             
                    end
         | 
| 59 63 |  | 
| 60 64 | 
             
                    if @control_url
         | 
| 61 | 
            -
                       | 
| 65 | 
            +
                      user_config.activate_control_app @control_url, @control_options
         | 
| 62 66 | 
             
                    end
         | 
| 63 67 | 
             
                  end
         | 
| 64 68 |  | 
| @@ -80,27 +84,35 @@ module Puma | |
| 80 84 | 
             
                  raise UnsupportedOption
         | 
| 81 85 | 
             
                end
         | 
| 82 86 |  | 
| 87 | 
            +
                def configure_control_url(command_line_arg)
         | 
| 88 | 
            +
                  if command_line_arg
         | 
| 89 | 
            +
                    @control_url = command_line_arg
         | 
| 90 | 
            +
                  elsif Puma.jruby?
         | 
| 91 | 
            +
                    unsupported "No default url available on JRuby"
         | 
| 92 | 
            +
                  end
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
             | 
| 83 95 | 
             
                # Build the OptionParser object to handle the available options.
         | 
| 84 96 | 
             
                #
         | 
| 85 97 |  | 
| 86 98 | 
             
                def setup_options
         | 
| 87 | 
            -
                  @conf = Configuration.new do | | 
| 99 | 
            +
                  @conf = Configuration.new do |user_config, file_config|
         | 
| 88 100 | 
             
                    @parser = OptionParser.new do |o|
         | 
| 89 101 | 
             
                      o.on "-b", "--bind URI", "URI to bind to (tcp://, unix://, ssl://)" do |arg|
         | 
| 90 | 
            -
                         | 
| 102 | 
            +
                        user_config.bind arg
         | 
| 91 103 | 
             
                      end
         | 
| 92 104 |  | 
| 93 105 | 
             
                      o.on "-C", "--config PATH", "Load PATH as a config file" do |arg|
         | 
| 94 | 
            -
                         | 
| 106 | 
            +
                        file_config.load arg
         | 
| 95 107 | 
             
                      end
         | 
| 96 108 |  | 
| 97 | 
            -
                      o.on "--control URL", "The bind url to use for the control server" | 
| 98 | 
            -
                         | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 102 | 
            -
             | 
| 103 | 
            -
                         | 
| 109 | 
            +
                      o.on "--control-url URL", "The bind url to use for the control server. Use 'auto' to use temp unix server" do |arg|
         | 
| 110 | 
            +
                        configure_control_url(arg)
         | 
| 111 | 
            +
                      end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                      # alias --control-url for backwards-compatibility
         | 
| 114 | 
            +
                      o.on "--control URL", "DEPRECATED alias for --control-url" do |arg|
         | 
| 115 | 
            +
                        configure_control_url(arg)
         | 
| 104 116 | 
             
                      end
         | 
| 105 117 |  | 
| 106 118 | 
             
                      o.on "--control-token TOKEN",
         | 
| @@ -109,21 +121,21 @@ module Puma | |
| 109 121 | 
             
                      end
         | 
| 110 122 |  | 
| 111 123 | 
             
                      o.on "-d", "--daemon", "Daemonize the server into the background" do
         | 
| 112 | 
            -
                         | 
| 113 | 
            -
                         | 
| 124 | 
            +
                        user_config.daemonize
         | 
| 125 | 
            +
                        user_config.quiet
         | 
| 114 126 | 
             
                      end
         | 
| 115 127 |  | 
| 116 128 | 
             
                      o.on "--debug", "Log lowlevel debugging information" do
         | 
| 117 | 
            -
                         | 
| 129 | 
            +
                        user_config.debug
         | 
| 118 130 | 
             
                      end
         | 
| 119 131 |  | 
| 120 132 | 
             
                      o.on "--dir DIR", "Change to DIR before starting" do |d|
         | 
| 121 | 
            -
                         | 
| 133 | 
            +
                        user_config.directory d
         | 
| 122 134 | 
             
                      end
         | 
| 123 135 |  | 
| 124 136 | 
             
                      o.on "-e", "--environment ENVIRONMENT",
         | 
| 125 137 | 
             
                        "The environment to run the Rack app on (default development)" do |arg|
         | 
| 126 | 
            -
                         | 
| 138 | 
            +
                        user_config.environment arg
         | 
| 127 139 | 
             
                      end
         | 
| 128 140 |  | 
| 129 141 | 
             
                      o.on "-I", "--include PATH", "Specify $LOAD_PATH directories" do |arg|
         | 
| @@ -132,50 +144,54 @@ module Puma | |
| 132 144 |  | 
| 133 145 | 
             
                      o.on "-p", "--port PORT", "Define the TCP port to bind to",
         | 
| 134 146 | 
             
                        "Use -b for more advanced options" do |arg|
         | 
| 135 | 
            -
                         | 
| 147 | 
            +
                        user_config.bind "tcp://#{Configuration::DefaultTCPHost}:#{arg}"
         | 
| 136 148 | 
             
                      end
         | 
| 137 149 |  | 
| 138 150 | 
             
                      o.on "--pidfile PATH", "Use PATH as a pidfile" do |arg|
         | 
| 139 | 
            -
                         | 
| 151 | 
            +
                        user_config.pidfile arg
         | 
| 140 152 | 
             
                      end
         | 
| 141 153 |  | 
| 142 154 | 
             
                      o.on "--preload", "Preload the app. Cluster mode only" do
         | 
| 143 | 
            -
                         | 
| 155 | 
            +
                        user_config.preload_app!
         | 
| 144 156 | 
             
                      end
         | 
| 145 157 |  | 
| 146 158 | 
             
                      o.on "--prune-bundler", "Prune out the bundler env if possible" do
         | 
| 147 | 
            -
                         | 
| 159 | 
            +
                        user_config.prune_bundler
         | 
| 148 160 | 
             
                      end
         | 
| 149 161 |  | 
| 150 162 | 
             
                      o.on "-q", "--quiet", "Do not log requests internally (default true)" do
         | 
| 151 | 
            -
                         | 
| 163 | 
            +
                        user_config.quiet
         | 
| 152 164 | 
             
                      end
         | 
| 153 165 |  | 
| 154 166 | 
             
                      o.on "-v", "--log-requests", "Log requests as they occur" do
         | 
| 155 | 
            -
                         | 
| 167 | 
            +
                        user_config.log_requests
         | 
| 156 168 | 
             
                      end
         | 
| 157 169 |  | 
| 158 170 | 
             
                      o.on "-R", "--restart-cmd CMD",
         | 
| 159 171 | 
             
                        "The puma command to run during a hot restart",
         | 
| 160 172 | 
             
                        "Default: inferred" do |cmd|
         | 
| 161 | 
            -
                         | 
| 173 | 
            +
                        user_config.restart_command cmd
         | 
| 162 174 | 
             
                      end
         | 
| 163 175 |  | 
| 164 176 | 
             
                      o.on "-S", "--state PATH", "Where to store the state details" do |arg|
         | 
| 165 | 
            -
                         | 
| 177 | 
            +
                        user_config.state_path arg
         | 
| 166 178 | 
             
                      end
         | 
| 167 179 |  | 
| 168 180 | 
             
                      o.on '-t', '--threads INT', "min:max threads to use (default 0:16)" do |arg|
         | 
| 169 181 | 
             
                        min, max = arg.split(":")
         | 
| 170 182 | 
             
                        if max
         | 
| 171 | 
            -
                           | 
| 183 | 
            +
                          user_config.threads min, max
         | 
| 172 184 | 
             
                        else
         | 
| 173 | 
            -
                           | 
| 185 | 
            +
                          user_config.threads min, min
         | 
| 174 186 | 
             
                        end
         | 
| 175 187 | 
             
                      end
         | 
| 176 188 |  | 
| 177 189 | 
             
                      o.on "--tcp-mode", "Run the app in raw TCP mode instead of HTTP mode" do
         | 
| 178 | 
            -
                         | 
| 190 | 
            +
                        user_config.tcp_mode!
         | 
| 191 | 
            +
                      end
         | 
| 192 | 
            +
             | 
| 193 | 
            +
                      o.on "--early-hints", "Enable early hints support" do
         | 
| 194 | 
            +
                        user_config.early_hints
         | 
| 179 195 | 
             
                      end
         | 
| 180 196 |  | 
| 181 197 | 
             
                      o.on "-V", "--version", "Print the version information" do
         | 
| @@ -185,11 +201,11 @@ module Puma | |
| 185 201 |  | 
| 186 202 | 
             
                      o.on "-w", "--workers COUNT",
         | 
| 187 203 | 
             
                        "Activate cluster mode: How many worker processes to create" do |arg|
         | 
| 188 | 
            -
                         | 
| 204 | 
            +
                        user_config.workers arg
         | 
| 189 205 | 
             
                      end
         | 
| 190 206 |  | 
| 191 207 | 
             
                      o.on "--tag NAME", "Additional text to display in process listing" do |arg|
         | 
| 192 | 
            -
                         | 
| 208 | 
            +
                        user_config.tag arg
         | 
| 193 209 | 
             
                      end
         | 
| 194 210 |  | 
| 195 211 | 
             
                      o.on "--redirect-stdout FILE", "Redirect STDOUT to a specific file" do |arg|
         | 
    
        data/lib/puma/client.rb
    CHANGED
    
    | @@ -21,6 +21,17 @@ module Puma | |
| 21 21 |  | 
| 22 22 | 
             
              class ConnectionError < RuntimeError; end
         | 
| 23 23 |  | 
| 24 | 
            +
              # An instance of this class represents a unique request from a client.
         | 
| 25 | 
            +
              # For example a web request from a browser or from CURL. This
         | 
| 26 | 
            +
              #
         | 
| 27 | 
            +
              # An instance of `Puma::Client` can be used as if it were an IO object
         | 
| 28 | 
            +
              # for example it is passed into `IO.select` inside of the `Puma::Reactor`.
         | 
| 29 | 
            +
              # This is accomplished by the `to_io` method which gets called on any
         | 
| 30 | 
            +
              # non-IO objects being used with the IO api such as `IO.select.
         | 
| 31 | 
            +
              #
         | 
| 32 | 
            +
              # Instances of this class are responsible for knowing if
         | 
| 33 | 
            +
              # the header and body are fully buffered via the `try_to_finish` method.
         | 
| 34 | 
            +
              # They can be used to "time out" a response via the `timeout_at` reader.
         | 
| 24 35 | 
             
              class Client
         | 
| 25 36 | 
             
                include Puma::Const
         | 
| 26 37 | 
             
                extend  Puma::Delegation
         | 
| @@ -111,6 +122,7 @@ module Puma | |
| 111 122 | 
             
                  begin
         | 
| 112 123 | 
             
                    @io.close
         | 
| 113 124 | 
             
                  rescue IOError
         | 
| 125 | 
            +
                    Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
         | 
| 114 126 | 
             
                  end
         | 
| 115 127 | 
             
                end
         | 
| 116 128 |  | 
| @@ -155,7 +167,8 @@ module Puma | |
| 155 167 | 
             
                      len = line.strip.to_i(16)
         | 
| 156 168 | 
             
                      if len == 0
         | 
| 157 169 | 
             
                        @body.rewind
         | 
| 158 | 
            -
                         | 
| 170 | 
            +
                        rest = io.read
         | 
| 171 | 
            +
                        @buffer = rest.empty? ? nil : rest
         | 
| 159 172 | 
             
                        @requests_served += 1
         | 
| 160 173 | 
             
                        @ready = true
         | 
| 161 174 | 
             
                        return true
         | 
| @@ -215,14 +228,20 @@ module Puma | |
| 215 228 | 
             
                end
         | 
| 216 229 |  | 
| 217 230 | 
             
                def setup_body
         | 
| 218 | 
            -
                  @ | 
| 231 | 
            +
                  if @env[HTTP_EXPECT] == CONTINUE
         | 
| 232 | 
            +
                    # TODO allow a hook here to check the headers before
         | 
| 233 | 
            +
                    # going forward
         | 
| 234 | 
            +
                    @io << HTTP_11_100
         | 
| 235 | 
            +
                    @io.flush
         | 
| 236 | 
            +
                  end
         | 
| 237 | 
            +
             | 
| 219 238 | 
             
                  @read_header = false
         | 
| 220 239 |  | 
| 221 240 | 
             
                  body = @parser.body
         | 
| 222 241 |  | 
| 223 242 | 
             
                  te = @env[TRANSFER_ENCODING2]
         | 
| 224 243 |  | 
| 225 | 
            -
                  if te ==  | 
| 244 | 
            +
                  if te && CHUNKED.casecmp(te) == 0
         | 
| 226 245 | 
             
                    return setup_chunked_body(body)
         | 
| 227 246 | 
             
                  end
         | 
| 228 247 |  | 
| @@ -276,6 +295,14 @@ module Puma | |
| 276 295 | 
             
                    raise ConnectionError, "Connection error detected during read"
         | 
| 277 296 | 
             
                  end
         | 
| 278 297 |  | 
| 298 | 
            +
                  # No data means a closed socket
         | 
| 299 | 
            +
                  unless data
         | 
| 300 | 
            +
                    @buffer = nil
         | 
| 301 | 
            +
                    @requests_served += 1
         | 
| 302 | 
            +
                    @ready = true
         | 
| 303 | 
            +
                    raise EOFError
         | 
| 304 | 
            +
                  end
         | 
| 305 | 
            +
             | 
| 279 306 | 
             
                  if @buffer
         | 
| 280 307 | 
             
                    @buffer << data
         | 
| 281 308 | 
             
                  else
         | 
| @@ -290,7 +317,7 @@ module Puma | |
| 290 317 | 
             
                    raise HttpParserError,
         | 
| 291 318 | 
             
                      "HEADER is longer than allowed, aborting client early."
         | 
| 292 319 | 
             
                  end
         | 
| 293 | 
            -
             | 
| 320 | 
            +
             | 
| 294 321 | 
             
                  false
         | 
| 295 322 | 
             
                end
         | 
| 296 323 |  | 
| @@ -305,6 +332,14 @@ module Puma | |
| 305 332 | 
             
                      raise e
         | 
| 306 333 | 
             
                    end
         | 
| 307 334 |  | 
| 335 | 
            +
                    # No data means a closed socket
         | 
| 336 | 
            +
                    unless data
         | 
| 337 | 
            +
                      @buffer = nil
         | 
| 338 | 
            +
                      @requests_served += 1
         | 
| 339 | 
            +
                      @ready = true
         | 
| 340 | 
            +
                      raise EOFError
         | 
| 341 | 
            +
                    end
         | 
| 342 | 
            +
             | 
| 308 343 | 
             
                    if @buffer
         | 
| 309 344 | 
             
                      @buffer << data
         | 
| 310 345 | 
             
                    else
         | 
    
        data/lib/puma/cluster.rb
    CHANGED
    
    | @@ -1,8 +1,24 @@ | |
| 1 1 | 
             
            require 'puma/runner'
         | 
| 2 | 
            +
            require 'puma/util'
         | 
| 3 | 
            +
            require 'puma/plugin'
         | 
| 4 | 
            +
             | 
| 2 5 | 
             
            require 'time'
         | 
| 3 6 |  | 
| 4 7 | 
             
            module Puma
         | 
| 8 | 
            +
              # This class is instantiated by the `Puma::Launcher` and used
         | 
| 9 | 
            +
              # to boot and serve a Ruby application when puma "workers" are needed
         | 
| 10 | 
            +
              # i.e. when using multi-processes. For example `$ puma -w 5`
         | 
| 11 | 
            +
              #
         | 
| 12 | 
            +
              # At the core of this class is running an instance of `Puma::Server` which
         | 
| 13 | 
            +
              # gets created via the `start_server` method from the `Puma::Runner` class
         | 
| 14 | 
            +
              # that this inherits from.
         | 
| 15 | 
            +
              #
         | 
| 16 | 
            +
              # An instance of this class will spawn the number of processes passed in
         | 
| 17 | 
            +
              # via the `spawn_workers` method call. Each worker will have it's own
         | 
| 18 | 
            +
              # instance of a `Puma::Server`.
         | 
| 5 19 | 
             
              class Cluster < Runner
         | 
| 20 | 
            +
                WORKER_CHECK_INTERVAL = 5
         | 
| 21 | 
            +
             | 
| 6 22 | 
             
                def initialize(cli, events)
         | 
| 7 23 | 
             
                  super cli, events
         | 
| 8 24 |  | 
| @@ -19,7 +35,7 @@ module Puma | |
| 19 35 | 
             
                  @workers.each { |x| x.term }
         | 
| 20 36 |  | 
| 21 37 | 
             
                  begin
         | 
| 22 | 
            -
                    Process. | 
| 38 | 
            +
                    @workers.each { |w| Process.waitpid(w.pid) }
         | 
| 23 39 | 
             
                  rescue Interrupt
         | 
| 24 40 | 
             
                    log "! Cancelled waiting for workers"
         | 
| 25 41 | 
             
                  end
         | 
| @@ -110,6 +126,7 @@ module Puma | |
| 110 126 |  | 
| 111 127 | 
             
                def spawn_workers
         | 
| 112 128 | 
             
                  diff = @options[:workers] - @workers.size
         | 
| 129 | 
            +
                  return if diff < 1
         | 
| 113 130 |  | 
| 114 131 | 
             
                  master = Process.pid
         | 
| 115 132 |  | 
| @@ -135,6 +152,21 @@ module Puma | |
| 135 152 | 
             
                  end
         | 
| 136 153 | 
             
                end
         | 
| 137 154 |  | 
| 155 | 
            +
                def cull_workers
         | 
| 156 | 
            +
                  diff = @workers.size - @options[:workers]
         | 
| 157 | 
            +
                  return if diff < 1
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                  debug "Culling #{diff.inspect} workers"
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                  workers_to_cull = @workers[-diff,diff]
         | 
| 162 | 
            +
                  debug "Workers to cull: #{workers_to_cull.inspect}"
         | 
| 163 | 
            +
             | 
| 164 | 
            +
                  workers_to_cull.each do |worker|
         | 
| 165 | 
            +
                    log "- Worker #{worker.index} (pid: #{worker.pid}) terminating"
         | 
| 166 | 
            +
                    worker.term
         | 
| 167 | 
            +
                  end
         | 
| 168 | 
            +
                end
         | 
| 169 | 
            +
             | 
| 138 170 | 
             
                def next_worker_index
         | 
| 139 171 | 
             
                  all_positions =  0...@options[:workers]
         | 
| 140 172 | 
             
                  occupied_positions = @workers.map { |w| w.index }
         | 
| @@ -149,7 +181,7 @@ module Puma | |
| 149 181 | 
             
                def check_workers(force=false)
         | 
| 150 182 | 
             
                  return if !force && @next_check && @next_check >= Time.now
         | 
| 151 183 |  | 
| 152 | 
            -
                  @next_check = Time.now +  | 
| 184 | 
            +
                  @next_check = Time.now + WORKER_CHECK_INTERVAL
         | 
| 153 185 |  | 
| 154 186 | 
             
                  any = false
         | 
| 155 187 |  | 
| @@ -175,6 +207,7 @@ module Puma | |
| 175 207 |  | 
| 176 208 | 
             
                  @workers.delete_if(&:dead?)
         | 
| 177 209 |  | 
| 210 | 
            +
                  cull_workers
         | 
| 178 211 | 
             
                  spawn_workers
         | 
| 179 212 |  | 
| 180 213 | 
             
                  if all_workers_booted?
         | 
| @@ -202,12 +235,13 @@ module Puma | |
| 202 235 | 
             
                  begin
         | 
| 203 236 | 
             
                    @wakeup.write "!" unless @wakeup.closed?
         | 
| 204 237 | 
             
                  rescue SystemCallError, IOError
         | 
| 238 | 
            +
                    Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
         | 
| 205 239 | 
             
                  end
         | 
| 206 240 | 
             
                end
         | 
| 207 241 |  | 
| 208 242 | 
             
                def worker(index, master)
         | 
| 209 | 
            -
                  title | 
| 210 | 
            -
                  title  | 
| 243 | 
            +
                  title  = "puma: cluster worker #{index}: #{master}"
         | 
| 244 | 
            +
                  title += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
         | 
| 211 245 | 
             
                  $0 = title
         | 
| 212 246 |  | 
| 213 247 | 
             
                  Signal.trap "SIGINT", "IGNORE"
         | 
| @@ -245,6 +279,7 @@ module Puma | |
| 245 279 | 
             
                  begin
         | 
| 246 280 | 
             
                    @worker_write << "b#{Process.pid}\n"
         | 
| 247 281 | 
             
                  rescue SystemCallError, IOError
         | 
| 282 | 
            +
                    Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
         | 
| 248 283 | 
             
                    STDERR.puts "Master seems to have exited, exiting."
         | 
| 249 284 | 
             
                    return
         | 
| 250 285 | 
             
                  end
         | 
| @@ -253,13 +288,16 @@ module Puma | |
| 253 288 | 
             
                    base_payload = "p#{Process.pid}"
         | 
| 254 289 |  | 
| 255 290 | 
             
                    while true
         | 
| 256 | 
            -
                      sleep  | 
| 291 | 
            +
                      sleep WORKER_CHECK_INTERVAL
         | 
| 257 292 | 
             
                      begin
         | 
| 258 | 
            -
                        b = server.backlog
         | 
| 259 | 
            -
                        r = server.running
         | 
| 260 | 
            -
                         | 
| 293 | 
            +
                        b = server.backlog || 0
         | 
| 294 | 
            +
                        r = server.running || 0
         | 
| 295 | 
            +
                        t = server.pool_capacity || 0
         | 
| 296 | 
            +
                        m = server.max_threads || 0
         | 
| 297 | 
            +
                        payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads": #{m} }\n!
         | 
| 261 298 | 
             
                        io << payload
         | 
| 262 299 | 
             
                      rescue IOError
         | 
| 300 | 
            +
                        Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
         | 
| 263 301 | 
             
                        break
         | 
| 264 302 | 
             
                      end
         | 
| 265 303 | 
             
                    end
         | 
| @@ -315,7 +353,7 @@ module Puma | |
| 315 353 | 
             
                def stats
         | 
| 316 354 | 
             
                  old_worker_count = @workers.count { |w| w.phase != @phase }
         | 
| 317 355 | 
             
                  booted_worker_count = @workers.count { |w| w.booted? }
         | 
| 318 | 
            -
                  worker_status = '[' + @workers.map{ |w| %Q!{ "pid": #{w.pid}, "index": #{w.index}, "phase": #{w.phase}, "booted": #{w.booted?}, "last_checkin": "#{w.last_checkin.utc.iso8601}", "last_status": #{w.last_status} }!}.join(",") + ']'
         | 
| 356 | 
            +
                  worker_status = '[' + @workers.map { |w| %Q!{ "pid": #{w.pid}, "index": #{w.index}, "phase": #{w.phase}, "booted": #{w.booted?}, "last_checkin": "#{w.last_checkin.utc.iso8601}", "last_status": #{w.last_status} }!}.join(",") + ']'
         | 
| 319 357 | 
             
                  %Q!{ "workers": #{@workers.size}, "phase": #{@phase}, "booted_workers": #{booted_worker_count}, "old_workers": #{old_worker_count}, "worker_status": #{worker_status} }!
         | 
| 320 358 | 
             
                end
         | 
| 321 359 |  | 
| @@ -337,7 +375,6 @@ module Puma | |
| 337 375 |  | 
| 338 376 | 
             
                  Signal.trap "TTOU" do
         | 
| 339 377 | 
             
                    @options[:workers] -= 1 if @options[:workers] >= 2
         | 
| 340 | 
            -
                    @workers.last.term
         | 
| 341 378 | 
             
                    wakeup!
         | 
| 342 379 | 
             
                  end
         | 
| 343 380 |  | 
| @@ -351,7 +388,10 @@ module Puma | |
| 351 388 | 
             
                      log "Early termination of worker"
         | 
| 352 389 | 
             
                      exit! 0
         | 
| 353 390 | 
             
                    else
         | 
| 391 | 
            +
                      stop_workers
         | 
| 354 392 | 
             
                      stop
         | 
| 393 | 
            +
             | 
| 394 | 
            +
                      raise SignalException, "SIGTERM"
         | 
| 355 395 | 
             
                    end
         | 
| 356 396 | 
             
                  end
         | 
| 357 397 | 
             
                end
         | 
| @@ -445,7 +485,7 @@ module Puma | |
| 445 485 |  | 
| 446 486 | 
             
                        force_check = false
         | 
| 447 487 |  | 
| 448 | 
            -
                        res = IO.select([read], nil, nil,  | 
| 488 | 
            +
                        res = IO.select([read], nil, nil, WORKER_CHECK_INTERVAL)
         | 
| 449 489 |  | 
| 450 490 | 
             
                        if res
         | 
| 451 491 | 
             
                          req = read.read_nonblock(1)
         |