puma 5.6.5-java → 6.0.1-java
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 +120 -11
- data/README.md +21 -17
- data/bin/puma-wild +1 -1
- data/docs/compile_options.md +34 -0
- data/docs/fork_worker.md +1 -3
- data/docs/nginx.md +1 -1
- 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 +11 -8
- data/ext/puma_http11/http11_parser.c +1 -1
- data/ext/puma_http11/http11_parser.h +1 -1
- 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 +2 -2
- data/ext/puma_http11/mini_ssl.c +36 -15
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +1 -1
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +156 -53
- data/ext/puma_http11/puma_http11.c +17 -9
- data/lib/puma/app/status.rb +3 -3
- data/lib/puma/binder.rb +36 -42
- data/lib/puma/cli.rb +11 -17
- data/lib/puma/client.rb +26 -13
- data/lib/puma/cluster/worker.rb +13 -11
- data/lib/puma/cluster/worker_handle.rb +4 -1
- data/lib/puma/cluster.rb +28 -25
- data/lib/puma/configuration.rb +74 -58
- data/lib/puma/const.rb +14 -18
- data/lib/puma/control_cli.rb +3 -6
- data/lib/puma/detect.rb +2 -0
- data/lib/puma/dsl.rb +96 -52
- data/lib/puma/error_logger.rb +17 -9
- 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/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +96 -156
- data/lib/puma/log_writer.rb +137 -0
- data/lib/puma/minissl/context_builder.rb +23 -12
- data/lib/puma/minissl.rb +82 -11
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/puma_http11.jar +0 -0
- data/lib/puma/rack/builder.rb +4 -4
- data/lib/puma/rack_default.rb +1 -1
- data/lib/puma/reactor.rb +4 -4
- data/lib/puma/request.rb +334 -166
- data/lib/puma/runner.rb +41 -20
- data/lib/puma/server.rb +55 -71
- data/lib/puma/single.rb +10 -10
- data/lib/puma/state_file.rb +1 -4
- data/lib/puma/systemd.rb +3 -2
- data/lib/puma/thread_pool.rb +16 -16
- data/lib/puma/util.rb +0 -11
- data/lib/puma.rb +12 -9
- data/lib/rack/handler/puma.rb +9 -9
- metadata +8 -4
- data/lib/puma/queue_close.rb +0 -26
    
        data/lib/puma/dsl.rb
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 3 | 
            +
            require_relative 'const'
         | 
| 4 | 
            +
            require_relative 'util'
         | 
| 5 5 |  | 
| 6 6 | 
             
            module Puma
         | 
| 7 7 | 
             
              # The methods that are available for use inside the configuration file.
         | 
| @@ -32,8 +32,24 @@ module Puma | |
| 32 32 | 
             
              # You can also find many examples being used by the test suite in
         | 
| 33 33 | 
             
              # +test/config+.
         | 
| 34 34 | 
             
              #
         | 
| 35 | 
            +
              # Puma v6 adds the option to specify a key name (String or Symbol) to the
         | 
| 36 | 
            +
              # hooks that run inside the forked workers.  All the hooks run inside the
         | 
| 37 | 
            +
              # {Puma::Cluster::Worker#run} method.
         | 
| 38 | 
            +
              #
         | 
| 39 | 
            +
              # Previously, the worker index and the LogWriter instance were passed to the
         | 
| 40 | 
            +
              # hook blocks/procs.  If a key name is specified, a hash is passed as the last
         | 
| 41 | 
            +
              # parameter.  This allows storage of data, typically objects that are created
         | 
| 42 | 
            +
              # before the worker that need to be passed to the hook when the worker is shutdown.
         | 
| 43 | 
            +
              #
         | 
| 44 | 
            +
              # The following hooks have been updated:
         | 
| 45 | 
            +
              #
         | 
| 46 | 
            +
              #     | DSL Method         |  Options Key            | Fork Block Location |
         | 
| 47 | 
            +
              #     | on_worker_boot     | :before_worker_boot     | inside, before      |
         | 
| 48 | 
            +
              #     | on_worker_shutdown | :before_worker_shutdown | inside, after       |
         | 
| 49 | 
            +
              #     | on_refork          | :before_refork          | inside              |
         | 
| 50 | 
            +
              #
         | 
| 35 51 | 
             
              class DSL
         | 
| 36 | 
            -
                 | 
| 52 | 
            +
                ON_WORKER_KEY = [String, Symbol].freeze
         | 
| 37 53 |  | 
| 38 54 | 
             
                # convenience method so logic can be used in CI
         | 
| 39 55 | 
             
                # @see ssl_bind
         | 
| @@ -52,25 +68,53 @@ module Puma | |
| 52 68 | 
             
                  backlog_str = opts[:backlog] ? "&backlog=#{Integer(opts[:backlog])}" : ''
         | 
| 53 69 |  | 
| 54 70 | 
             
                  if defined?(JRUBY_VERSION)
         | 
| 55 | 
            -
                     | 
| 56 | 
            -
             | 
| 71 | 
            +
                    cipher_suites = opts[:ssl_cipher_list] ? "&ssl_cipher_list=#{opts[:ssl_cipher_list]}" : nil # old name
         | 
| 72 | 
            +
                    cipher_suites = "#{cipher_suites}&cipher_suites=#{opts[:cipher_suites]}" if opts[:cipher_suites]
         | 
| 73 | 
            +
                    protocols = opts[:protocols] ? "&protocols=#{opts[:protocols]}" : nil
         | 
| 57 74 |  | 
| 58 75 | 
             
                    keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
         | 
| 76 | 
            +
                    keystore_additions = "#{keystore_additions}&keystore-type=#{opts[:keystore_type]}" if opts[:keystore_type]
         | 
| 77 | 
            +
                    if opts[:truststore]
         | 
| 78 | 
            +
                      truststore_additions = "&truststore=#{opts[:truststore]}"
         | 
| 79 | 
            +
                      truststore_additions = "#{truststore_additions}&truststore-pass=#{opts[:truststore_pass]}" if opts[:truststore_pass]
         | 
| 80 | 
            +
                      truststore_additions = "#{truststore_additions}&truststore-type=#{opts[:truststore_type]}" if opts[:truststore_type]
         | 
| 81 | 
            +
                    end
         | 
| 59 82 |  | 
| 60 | 
            -
                    "ssl://#{host}:#{port}?#{keystore_additions}#{ | 
| 83 | 
            +
                    "ssl://#{host}:#{port}?#{keystore_additions}#{truststore_additions}#{cipher_suites}#{protocols}" \
         | 
| 61 84 | 
             
                      "&verify_mode=#{verify}#{tls_str}#{ca_additions}#{backlog_str}"
         | 
| 62 85 | 
             
                  else
         | 
| 63 | 
            -
                    ssl_cipher_filter = opts[:ssl_cipher_filter] ?
         | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
                     | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 69 | 
            -
                     | 
| 70 | 
            -
             | 
| 86 | 
            +
                    ssl_cipher_filter = opts[:ssl_cipher_filter] ? "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" : nil
         | 
| 87 | 
            +
                    v_flags = (ary = opts[:verification_flags]) ? "&verification_flags=#{Array(ary).join ','}" : nil
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                    cert_flags = (cert = opts[:cert]) ? "cert=#{Puma::Util.escape(cert)}" : nil
         | 
| 90 | 
            +
                    key_flags = (key = opts[:key]) ? "&key=#{Puma::Util.escape(key)}" : nil
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                    reuse_flag =
         | 
| 93 | 
            +
                      if (reuse = opts[:reuse])
         | 
| 94 | 
            +
                        if reuse == true
         | 
| 95 | 
            +
                          '&reuse=dflt'
         | 
| 96 | 
            +
                        elsif reuse.is_a?(Hash) && (reuse.key?(:size) || reuse.key?(:timeout))
         | 
| 97 | 
            +
                          val = +''
         | 
| 98 | 
            +
                          if (size = reuse[:size]) && Integer === size
         | 
| 99 | 
            +
                            val << size.to_s
         | 
| 100 | 
            +
                          end
         | 
| 101 | 
            +
                          if (timeout = reuse[:timeout]) && Integer === timeout
         | 
| 102 | 
            +
                            val << ",#{timeout}"
         | 
| 103 | 
            +
                          end
         | 
| 104 | 
            +
                          if val.empty?
         | 
| 105 | 
            +
                            nil
         | 
| 106 | 
            +
                          else
         | 
| 107 | 
            +
                            "&reuse=#{val}"
         | 
| 108 | 
            +
                          end
         | 
| 109 | 
            +
                        else
         | 
| 110 | 
            +
                          nil
         | 
| 111 | 
            +
                        end
         | 
| 112 | 
            +
                      else
         | 
| 113 | 
            +
                        nil
         | 
| 114 | 
            +
                      end
         | 
| 71 115 |  | 
| 72 | 
            -
                    "ssl://#{host}:#{port}?#{cert_flags}#{key_flags}" \
         | 
| 73 | 
            -
                      "#{ | 
| 116 | 
            +
                    "ssl://#{host}:#{port}?#{cert_flags}#{key_flags}#{ssl_cipher_filter}" \
         | 
| 117 | 
            +
                      "#{reuse_flag}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}#{backlog_str}"
         | 
| 74 118 | 
             
                  end
         | 
| 75 119 | 
             
                end
         | 
| 76 120 |  | 
| @@ -106,7 +150,7 @@ module Puma | |
| 106 150 | 
             
                end
         | 
| 107 151 |  | 
| 108 152 | 
             
                def default_host
         | 
| 109 | 
            -
                  @options[:default_host] || Configuration:: | 
| 153 | 
            +
                  @options[:default_host] || Configuration::DEFAULTS[:tcp_host]
         | 
| 110 154 | 
             
                end
         | 
| 111 155 |  | 
| 112 156 | 
             
                def inject(&blk)
         | 
| @@ -206,6 +250,7 @@ module Puma | |
| 206 250 | 
             
                #
         | 
| 207 251 | 
             
                # * Set the socket backlog depth with +backlog+, default is 1024.
         | 
| 208 252 | 
             
                # * Set up an SSL certificate with +key+ & +cert+.
         | 
| 253 | 
            +
                # * Set up an SSL certificate for mTLS with +key+, +cert+, +ca+ and +verify_mode+.
         | 
| 209 254 | 
             
                # * Set whether to optimize for low latency instead of throughput with
         | 
| 210 255 | 
             
                #   +low_latency+, default is to not optimize for low latency. This is done
         | 
| 211 256 | 
             
                #   via +Socket::TCP_NODELAY+.
         | 
| @@ -215,6 +260,8 @@ module Puma | |
| 215 260 | 
             
                #   bind 'unix:///var/run/puma.sock?backlog=512'
         | 
| 216 261 | 
             
                # @example SSL cert
         | 
| 217 262 | 
             
                #   bind 'ssl://127.0.0.1:9292?key=key.key&cert=cert.pem'
         | 
| 263 | 
            +
                # @example SSL cert for mutual TLS (mTLS)
         | 
| 264 | 
            +
                #   bind 'ssl://127.0.0.1:9292?key=key.key&cert=cert.pem&ca=ca.pem&verify_mode=force_peer'
         | 
| 218 265 | 
             
                # @example Disable optimization for low latency
         | 
| 219 266 | 
             
                #   bind 'tcp://0.0.0.0:9292?low_latency=false'
         | 
| 220 267 | 
             
                # @example Socket permissions
         | 
| @@ -452,6 +499,10 @@ module Puma | |
| 452 499 | 
             
                # Puma will assume you are using the +localhost+ gem and try to load the
         | 
| 453 500 | 
             
                # appropriate files.
         | 
| 454 501 | 
             
                #
         | 
| 502 | 
            +
                # When using the options hash parameter, the `reuse:` value is either
         | 
| 503 | 
            +
                # `true`, which sets reuse 'on' with default values, or a hash, with `:size`
         | 
| 504 | 
            +
                # and/or `:timeout` keys, each with integer values.
         | 
| 505 | 
            +
                #
         | 
| 455 506 | 
             
                # @example
         | 
| 456 507 | 
             
                #   ssl_bind '127.0.0.1', '9292', {
         | 
| 457 508 | 
             
                #     cert: path_to_cert,
         | 
| @@ -459,6 +510,7 @@ module Puma | |
| 459 510 | 
             
                #     ssl_cipher_filter: cipher_filter, # optional
         | 
| 460 511 | 
             
                #     verify_mode: verify_mode,         # default 'none'
         | 
| 461 512 | 
             
                #     verification_flags: flags,        # optional, not supported by JRuby
         | 
| 513 | 
            +
                #     reuse: true                       # optional
         | 
| 462 514 | 
             
                #   }
         | 
| 463 515 | 
             
                #
         | 
| 464 516 | 
             
                # @example Using self-signed certificate with the +localhost+ gem:
         | 
| @@ -468,6 +520,7 @@ module Puma | |
| 468 520 | 
             
                #   ssl_bind '127.0.0.1', '9292', {
         | 
| 469 521 | 
             
                #     cert_pem: File.read(path_to_cert),
         | 
| 470 522 | 
             
                #     key_pem: File.read(path_to_key),
         | 
| 523 | 
            +
                #     reuse: {size: 2_000, timeout: 20} # optional
         | 
| 471 524 | 
             
                #   }
         | 
| 472 525 | 
             
                #
         | 
| 473 526 | 
             
                # @example For JRuby, two keys are required: +keystore+ & +keystore_pass+
         | 
| @@ -560,9 +613,8 @@ module Puma | |
| 560 613 | 
             
                #   on_worker_boot do
         | 
| 561 614 | 
             
                #     puts 'Before worker boot...'
         | 
| 562 615 | 
             
                #   end
         | 
| 563 | 
            -
                def on_worker_boot(&block)
         | 
| 564 | 
            -
                   | 
| 565 | 
            -
                  @options[:before_worker_boot] << block
         | 
| 616 | 
            +
                def on_worker_boot(key = nil, &block)
         | 
| 617 | 
            +
                  process_hook :before_worker_boot, key, block, 'on_worker_boot'
         | 
| 566 618 | 
             
                end
         | 
| 567 619 |  | 
| 568 620 | 
             
                # Code to run immediately before a worker shuts
         | 
| @@ -577,9 +629,8 @@ module Puma | |
| 577 629 | 
             
                #   on_worker_shutdown do
         | 
| 578 630 | 
             
                #     puts 'On worker shutdown...'
         | 
| 579 631 | 
             
                #   end
         | 
| 580 | 
            -
                def on_worker_shutdown(&block)
         | 
| 581 | 
            -
                   | 
| 582 | 
            -
                  @options[:before_worker_shutdown] << block
         | 
| 632 | 
            +
                def on_worker_shutdown(key = nil, &block)
         | 
| 633 | 
            +
                  process_hook :before_worker_shutdown, key, block, 'on_worker_shutdown'
         | 
| 583 634 | 
             
                end
         | 
| 584 635 |  | 
| 585 636 | 
             
                # Code to run in the master right before a worker is started. The worker's
         | 
| @@ -593,8 +644,7 @@ module Puma | |
| 593 644 | 
             
                #     puts 'Before worker fork...'
         | 
| 594 645 | 
             
                #   end
         | 
| 595 646 | 
             
                def on_worker_fork(&block)
         | 
| 596 | 
            -
                   | 
| 597 | 
            -
                  @options[:before_worker_fork] << block
         | 
| 647 | 
            +
                  process_hook :before_worker_fork, nil, block, 'on_worker_fork'
         | 
| 598 648 | 
             
                end
         | 
| 599 649 |  | 
| 600 650 | 
             
                # Code to run in the master after a worker has been started. The worker's
         | 
| @@ -608,8 +658,7 @@ module Puma | |
| 608 658 | 
             
                #     puts 'After worker fork...'
         | 
| 609 659 | 
             
                #   end
         | 
| 610 660 | 
             
                def after_worker_fork(&block)
         | 
| 611 | 
            -
                   | 
| 612 | 
            -
                  @options[:after_worker_fork] << block
         | 
| 661 | 
            +
                  process_hook :after_worker_fork, nil, block, 'after_worker_fork'
         | 
| 613 662 | 
             
                end
         | 
| 614 663 |  | 
| 615 664 | 
             
                alias_method :after_worker_boot, :after_worker_fork
         | 
| @@ -632,9 +681,8 @@ module Puma | |
| 632 681 | 
             
                #   end
         | 
| 633 682 | 
             
                # @version 5.0.0
         | 
| 634 683 | 
             
                #
         | 
| 635 | 
            -
                def on_refork(&block)
         | 
| 636 | 
            -
                   | 
| 637 | 
            -
                  @options[:before_refork] << block
         | 
| 684 | 
            +
                def on_refork(key = nil, &block)
         | 
| 685 | 
            +
                  process_hook :before_refork, key, block, 'on_refork'
         | 
| 638 686 | 
             
                end
         | 
| 639 687 |  | 
| 640 688 | 
             
                # Code to run out-of-band when the worker is idle.
         | 
| @@ -647,8 +695,7 @@ module Puma | |
| 647 695 | 
             
                #
         | 
| 648 696 | 
             
                # This can be called multiple times to add several hooks.
         | 
| 649 697 | 
             
                def out_of_band(&block)
         | 
| 650 | 
            -
                   | 
| 651 | 
            -
                  @options[:out_of_band] << block
         | 
| 698 | 
            +
                  process_hook :out_of_band, nil, block, 'out_of_band'
         | 
| 652 699 | 
             
                end
         | 
| 653 700 |  | 
| 654 701 | 
             
                # The directory to operate out of.
         | 
| @@ -778,7 +825,7 @@ module Puma | |
| 778 825 | 
             
                #
         | 
| 779 826 | 
             
                def worker_timeout(timeout)
         | 
| 780 827 | 
             
                  timeout = Integer(timeout)
         | 
| 781 | 
            -
                  min = @options.fetch(:worker_check_interval,  | 
| 828 | 
            +
                  min = @options.fetch(:worker_check_interval, Configuration::DEFAULTS[:worker_check_interval])
         | 
| 782 829 |  | 
| 783 830 | 
             
                  if timeout <= min
         | 
| 784 831 | 
             
                    raise "The minimum worker_timeout must be greater than the worker reporting interval (#{min})"
         | 
| @@ -882,13 +929,16 @@ module Puma | |
| 882 929 | 
             
                # There are 5 possible values:
         | 
| 883 930 | 
             
                #
         | 
| 884 931 | 
             
                # 1. **:socket** (the default) - read the peername from the socket using the
         | 
| 885 | 
            -
                #    syscall. This is the normal behavior.
         | 
| 932 | 
            +
                #    syscall. This is the normal behavior. If this fails for any reason (e.g.,
         | 
| 933 | 
            +
                #    if the peer disconnects between the connection being accepted and the getpeername
         | 
| 934 | 
            +
                #    system call), Puma will return "0.0.0.0"
         | 
| 886 935 | 
             
                # 2. **:localhost** - set the remote address to "127.0.0.1"
         | 
| 887 936 | 
             
                # 3. **header: <http_header>**- set the remote address to the value of the
         | 
| 888 937 | 
             
                #    provided http header. For instance:
         | 
| 889 938 | 
             
                #    `set_remote_address header: "X-Real-IP"`.
         | 
| 890 939 | 
             
                #    Only the first word (as separated by spaces or comma) is used, allowing
         | 
| 891 | 
            -
                #    headers such as X-Forwarded-For to be used as well.
         | 
| 940 | 
            +
                #    headers such as X-Forwarded-For to be used as well. If this header is absent,
         | 
| 941 | 
            +
                #    Puma will fall back to the behavior of :socket
         | 
| 892 942 | 
             
                # 4. **proxy_protocol: :v1**- set the remote address to the value read from the
         | 
| 893 943 | 
             
                #    HAproxy PROXY protocol, version 1. If the request does not have the PROXY
         | 
| 894 944 | 
             
                #    protocol attached to it, will fall back to :socket
         | 
| @@ -942,23 +992,6 @@ module Puma | |
| 942 992 | 
             
                  @options[:fork_worker] = Integer(after_requests)
         | 
| 943 993 | 
             
                end
         | 
| 944 994 |  | 
| 945 | 
            -
                # When enabled, Puma will GC 4 times before forking workers.
         | 
| 946 | 
            -
                # If available (Ruby 2.7+), we will also call GC.compact.
         | 
| 947 | 
            -
                # Not recommended for non-MRI Rubies.
         | 
| 948 | 
            -
                #
         | 
| 949 | 
            -
                # Based on the work of Koichi Sasada and Aaron Patterson, this option may
         | 
| 950 | 
            -
                # decrease memory utilization of preload-enabled cluster-mode Pumas. It will
         | 
| 951 | 
            -
                # also increase time to boot and fork. See your logs for details on how much
         | 
| 952 | 
            -
                # time this adds to your boot process. For most apps, it will be less than one
         | 
| 953 | 
            -
                # second.
         | 
| 954 | 
            -
                #
         | 
| 955 | 
            -
                # @see Puma::Cluster#nakayoshi_gc
         | 
| 956 | 
            -
                # @version 5.0.0
         | 
| 957 | 
            -
                #
         | 
| 958 | 
            -
                def nakayoshi_fork(enabled=true)
         | 
| 959 | 
            -
                  @options[:nakayoshi_fork] = enabled
         | 
| 960 | 
            -
                end
         | 
| 961 | 
            -
             | 
| 962 995 | 
             
                # The number of requests to attempt inline before sending a client back to
         | 
| 963 996 | 
             
                # the reactor to be subject to normal ordering.
         | 
| 964 997 | 
             
                #
         | 
| @@ -1008,5 +1041,16 @@ module Puma | |
| 1008 1041 | 
             
                    end
         | 
| 1009 1042 | 
             
                  end
         | 
| 1010 1043 | 
             
                end
         | 
| 1044 | 
            +
             | 
| 1045 | 
            +
                def process_hook(options_key, key, block, meth)
         | 
| 1046 | 
            +
                  @options[options_key] ||= []
         | 
| 1047 | 
            +
                  if ON_WORKER_KEY.include? key.class
         | 
| 1048 | 
            +
                    @options[options_key] << [block, key.to_sym]
         | 
| 1049 | 
            +
                  elsif key.nil?
         | 
| 1050 | 
            +
                    @options[options_key] << block
         | 
| 1051 | 
            +
                  else
         | 
| 1052 | 
            +
                    raise "'#{method}' key must be String or Symbol"
         | 
| 1053 | 
            +
                  end
         | 
| 1054 | 
            +
                end
         | 
| 1011 1055 | 
             
              end
         | 
| 1012 1056 | 
             
            end
         | 
    
        data/lib/puma/error_logger.rb
    CHANGED
    
    | @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
             | 
| 3 | 
            +
            require_relative 'const'
         | 
| 4 4 |  | 
| 5 5 | 
             
            module Puma
         | 
| 6 6 | 
             
              # The implementation of a detailed error logging.
         | 
| @@ -13,6 +13,8 @@ module Puma | |
| 13 13 |  | 
| 14 14 | 
             
                REQUEST_FORMAT = %{"%s %s%s" - (%s)}
         | 
| 15 15 |  | 
| 16 | 
            +
                LOG_QUEUE = Queue.new
         | 
| 17 | 
            +
             | 
| 16 18 | 
             
                def initialize(ioerr)
         | 
| 17 19 | 
             
                  @ioerr = ioerr
         | 
| 18 20 |  | 
| @@ -31,7 +33,7 @@ module Puma | |
| 31 33 | 
             
                #   and before all remaining info.
         | 
| 32 34 | 
             
                #
         | 
| 33 35 | 
             
                def info(options={})
         | 
| 34 | 
            -
                   | 
| 36 | 
            +
                  internal_write title(options)
         | 
| 35 37 | 
             
                end
         | 
| 36 38 |  | 
| 37 39 | 
             
                # Print occurred error details only if
         | 
| @@ -53,7 +55,7 @@ module Puma | |
| 53 55 | 
             
                  string_block << request_dump(req) if request_parsed?(req)
         | 
| 54 56 | 
             
                  string_block << error.backtrace if error
         | 
| 55 57 |  | 
| 56 | 
            -
                   | 
| 58 | 
            +
                  internal_write string_block.join("\n")
         | 
| 57 59 | 
             
                end
         | 
| 58 60 |  | 
| 59 61 | 
             
                def title(options={})
         | 
| @@ -93,12 +95,18 @@ module Puma | |
| 93 95 | 
             
                  req && req.env[REQUEST_METHOD]
         | 
| 94 96 | 
             
                end
         | 
| 95 97 |  | 
| 96 | 
            -
                 | 
| 97 | 
            -
             | 
| 98 | 
            -
             | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 98 | 
            +
                def internal_write(str)
         | 
| 99 | 
            +
                  LOG_QUEUE << str
         | 
| 100 | 
            +
                  while (w_str = LOG_QUEUE.pop(true)) do
         | 
| 101 | 
            +
                    begin
         | 
| 102 | 
            +
                      @ioerr.is_a?(IO) and @ioerr.wait_writable(1)
         | 
| 103 | 
            +
                      @ioerr.write "#{w_str}\n"
         | 
| 104 | 
            +
                      @ioerr.flush unless @ioerr.sync
         | 
| 105 | 
            +
                    rescue Errno::EPIPE, Errno::EBADF, IOError
         | 
| 106 | 
            +
                    end
         | 
| 107 | 
            +
                  end
         | 
| 108 | 
            +
                rescue ThreadError
         | 
| 102 109 | 
             
                end
         | 
| 110 | 
            +
                private :internal_write
         | 
| 103 111 | 
             
              end
         | 
| 104 112 | 
             
            end
         | 
    
        data/lib/puma/events.rb
    CHANGED
    
    | @@ -1,52 +1,23 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require "puma/null_io"
         | 
| 4 | 
            -
            require 'puma/error_logger'
         | 
| 5 | 
            -
            require 'stringio'
         | 
| 6 | 
            -
             | 
| 7 3 | 
             
            module Puma
         | 
| 8 | 
            -
              # The default implement of an event sink object used by Server
         | 
| 9 | 
            -
              # for when certain kinds of events occur in the life of the server.
         | 
| 10 | 
            -
              #
         | 
| 11 | 
            -
              # The methods available are the events that the Server fires.
         | 
| 12 | 
            -
              #
         | 
| 13 | 
            -
              class Events
         | 
| 14 | 
            -
                class DefaultFormatter
         | 
| 15 | 
            -
                  def call(str)
         | 
| 16 | 
            -
                    str
         | 
| 17 | 
            -
                  end
         | 
| 18 | 
            -
                end
         | 
| 19 | 
            -
             | 
| 20 | 
            -
                class PidFormatter
         | 
| 21 | 
            -
                  def call(str)
         | 
| 22 | 
            -
                    "[#{$$}] #{str}"
         | 
| 23 | 
            -
                  end
         | 
| 24 | 
            -
                end
         | 
| 25 | 
            -
             | 
| 26 | 
            -
                # Create an Events object that prints to +stdout+ and +stderr+.
         | 
| 27 | 
            -
                #
         | 
| 28 | 
            -
                def initialize(stdout, stderr)
         | 
| 29 | 
            -
                  @formatter = DefaultFormatter.new
         | 
| 30 | 
            -
                  @stdout = stdout
         | 
| 31 | 
            -
                  @stderr = stderr
         | 
| 32 4 |  | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 5 | 
            +
              # This is an event sink used by `Puma::Server` to handle
         | 
| 6 | 
            +
              # lifecycle events such as :on_booted, :on_restart, and :on_stopped.
         | 
| 7 | 
            +
              # Using `Puma::DSL` it is possible to register callback hooks
         | 
| 8 | 
            +
              # for each event type.
         | 
| 9 | 
            +
              class Events
         | 
| 35 10 |  | 
| 11 | 
            +
                def initialize
         | 
| 36 12 | 
             
                  @hooks = Hash.new { |h,k| h[k] = [] }
         | 
| 37 13 | 
             
                end
         | 
| 38 14 |  | 
| 39 | 
            -
                attr_reader :stdout, :stderr
         | 
| 40 | 
            -
                attr_accessor :formatter
         | 
| 41 | 
            -
             | 
| 42 15 | 
             
                # Fire callbacks for the named hook
         | 
| 43 | 
            -
                #
         | 
| 44 16 | 
             
                def fire(hook, *args)
         | 
| 45 17 | 
             
                  @hooks[hook].each { |t| t.call(*args) }
         | 
| 46 18 | 
             
                end
         | 
| 47 19 |  | 
| 48 20 | 
             
                # Register a callback for a given hook
         | 
| 49 | 
            -
                #
         | 
| 50 21 | 
             
                def register(hook, obj=nil, &blk)
         | 
| 51 22 | 
             
                  if obj and blk
         | 
| 52 23 | 
             
                    raise "Specify either an object or a block, not both"
         | 
| @@ -59,79 +30,6 @@ module Puma | |
| 59 30 | 
             
                  h
         | 
| 60 31 | 
             
                end
         | 
| 61 32 |  | 
| 62 | 
            -
                # Write +str+ to +@stdout+
         | 
| 63 | 
            -
                #
         | 
| 64 | 
            -
                def log(str)
         | 
| 65 | 
            -
                  @stdout.puts format(str) if @stdout.respond_to? :puts
         | 
| 66 | 
            -
             | 
| 67 | 
            -
                  @stdout.flush unless @stdout.sync
         | 
| 68 | 
            -
                rescue Errno::EPIPE
         | 
| 69 | 
            -
                end
         | 
| 70 | 
            -
             | 
| 71 | 
            -
                def write(str)
         | 
| 72 | 
            -
                  @stdout.write format(str)
         | 
| 73 | 
            -
                end
         | 
| 74 | 
            -
             | 
| 75 | 
            -
                def debug(str)
         | 
| 76 | 
            -
                  log("% #{str}") if @debug
         | 
| 77 | 
            -
                end
         | 
| 78 | 
            -
             | 
| 79 | 
            -
                # Write +str+ to +@stderr+
         | 
| 80 | 
            -
                #
         | 
| 81 | 
            -
                def error(str)
         | 
| 82 | 
            -
                  @error_logger.info(text: format("ERROR: #{str}"))
         | 
| 83 | 
            -
                  exit 1
         | 
| 84 | 
            -
                end
         | 
| 85 | 
            -
             | 
| 86 | 
            -
                def format(str)
         | 
| 87 | 
            -
                  formatter.call(str)
         | 
| 88 | 
            -
                end
         | 
| 89 | 
            -
             | 
| 90 | 
            -
                # An HTTP connection error has occurred.
         | 
| 91 | 
            -
                # +error+ a connection exception, +req+ the request,
         | 
| 92 | 
            -
                # and +text+ additional info
         | 
| 93 | 
            -
                # @version 5.0.0
         | 
| 94 | 
            -
                #
         | 
| 95 | 
            -
                def connection_error(error, req, text="HTTP connection error")
         | 
| 96 | 
            -
                  @error_logger.info(error: error, req: req, text: text)
         | 
| 97 | 
            -
                end
         | 
| 98 | 
            -
             | 
| 99 | 
            -
                # An HTTP parse error has occurred.
         | 
| 100 | 
            -
                # +error+ a parsing exception,
         | 
| 101 | 
            -
                # and +req+ the request.
         | 
| 102 | 
            -
                #
         | 
| 103 | 
            -
                def parse_error(error, req)
         | 
| 104 | 
            -
                  @error_logger.info(error: error, req: req, text: 'HTTP parse error, malformed request')
         | 
| 105 | 
            -
                end
         | 
| 106 | 
            -
             | 
| 107 | 
            -
                # An SSL error has occurred.
         | 
| 108 | 
            -
                # @param error <Puma::MiniSSL::SSLError>
         | 
| 109 | 
            -
                # @param ssl_socket <Puma::MiniSSL::Socket>
         | 
| 110 | 
            -
                #
         | 
| 111 | 
            -
                def ssl_error(error, ssl_socket)
         | 
| 112 | 
            -
                  peeraddr = ssl_socket.peeraddr.last rescue "<unknown>"
         | 
| 113 | 
            -
                  peercert = ssl_socket.peercert
         | 
| 114 | 
            -
                  subject = peercert ? peercert.subject : nil
         | 
| 115 | 
            -
                  @error_logger.info(error: error, text: "SSL error, peer: #{peeraddr}, peer cert: #{subject}")
         | 
| 116 | 
            -
                end
         | 
| 117 | 
            -
             | 
| 118 | 
            -
                # An unknown error has occurred.
         | 
| 119 | 
            -
                # +error+ an exception object, +req+ the request,
         | 
| 120 | 
            -
                # and +text+ additional info
         | 
| 121 | 
            -
                #
         | 
| 122 | 
            -
                def unknown_error(error, req=nil, text="Unknown error")
         | 
| 123 | 
            -
                  @error_logger.info(error: error, req: req, text: text)
         | 
| 124 | 
            -
                end
         | 
| 125 | 
            -
             | 
| 126 | 
            -
                # Log occurred error debug dump.
         | 
| 127 | 
            -
                # +error+ an exception object, +req+ the request,
         | 
| 128 | 
            -
                # and +text+ additional info
         | 
| 129 | 
            -
                # @version 5.0.0
         | 
| 130 | 
            -
                #
         | 
| 131 | 
            -
                def debug_error(error, req=nil, text="")
         | 
| 132 | 
            -
                  @error_logger.debug(error: error, req: req, text: text)
         | 
| 133 | 
            -
                end
         | 
| 134 | 
            -
             | 
| 135 33 | 
             
                def on_booted(&block)
         | 
| 136 34 | 
             
                  register(:on_booted, &block)
         | 
| 137 35 | 
             
                end
         | 
| @@ -155,23 +53,5 @@ module Puma | |
| 155 53 | 
             
                def fire_on_stopped!
         | 
| 156 54 | 
             
                  fire(:on_stopped)
         | 
| 157 55 | 
             
                end
         | 
| 158 | 
            -
             | 
| 159 | 
            -
                DEFAULT = new(STDOUT, STDERR)
         | 
| 160 | 
            -
             | 
| 161 | 
            -
                # Returns an Events object which writes its status to 2 StringIO
         | 
| 162 | 
            -
                # objects.
         | 
| 163 | 
            -
                #
         | 
| 164 | 
            -
                def self.strings
         | 
| 165 | 
            -
                  Events.new StringIO.new, StringIO.new
         | 
| 166 | 
            -
                end
         | 
| 167 | 
            -
             | 
| 168 | 
            -
                def self.stdio
         | 
| 169 | 
            -
                  Events.new $stdout, $stderr
         | 
| 170 | 
            -
                end
         | 
| 171 | 
            -
             | 
| 172 | 
            -
                def self.null
         | 
| 173 | 
            -
                  n = NullIO.new
         | 
| 174 | 
            -
                  Events.new n, n
         | 
| 175 | 
            -
                end
         | 
| 176 56 | 
             
              end
         | 
| 177 57 | 
             
            end
         | 
    
        data/lib/puma/io_buffer.rb
    CHANGED
    
    | @@ -1,11 +1,46 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require 'stringio'
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            module Puma
         | 
| 4 | 
            -
              class IOBuffer <  | 
| 5 | 
            -
                def  | 
| 6 | 
            -
                   | 
| 6 | 
            +
              class IOBuffer < StringIO
         | 
| 7 | 
            +
                def initialize
         | 
| 8 | 
            +
                  super.binmode
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def empty?
         | 
| 12 | 
            +
                  length.zero?
         | 
| 7 13 | 
             
                end
         | 
| 8 14 |  | 
| 9 | 
            -
                 | 
| 15 | 
            +
                def reset
         | 
| 16 | 
            +
                  truncate 0
         | 
| 17 | 
            +
                  rewind
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def to_s
         | 
| 21 | 
            +
                  rewind
         | 
| 22 | 
            +
                  read
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                # Read & Reset - returns contents and resets
         | 
| 26 | 
            +
                # @return [String] StringIO contents
         | 
| 27 | 
            +
                def read_and_reset
         | 
| 28 | 
            +
                  rewind
         | 
| 29 | 
            +
                  str = read
         | 
| 30 | 
            +
                  truncate 0
         | 
| 31 | 
            +
                  rewind
         | 
| 32 | 
            +
                  str
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                alias_method :clear, :reset
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                # before Ruby 2.5, `write` would only take one argument
         | 
| 38 | 
            +
                if RUBY_VERSION >= '2.5' && RUBY_ENGINE != 'truffleruby'
         | 
| 39 | 
            +
                  alias_method :append, :write
         | 
| 40 | 
            +
                else
         | 
| 41 | 
            +
                  def append(*strs)
         | 
| 42 | 
            +
                    strs.each { |str| write str }
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
                end
         | 
| 10 45 | 
             
              end
         | 
| 11 46 | 
             
            end
         | 
    
        data/lib/puma/jruby_restart.rb
    CHANGED
    
    
| @@ -0,0 +1,104 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Puma
         | 
| 4 | 
            +
              class Launcher
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                # This class is used to pickup Gemfile changes during
         | 
| 7 | 
            +
                # application restarts.
         | 
| 8 | 
            +
                class BundlePruner
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def initialize(original_argv, extra_runtime_dependencies, log_writer)
         | 
| 11 | 
            +
                    @original_argv = Array(original_argv)
         | 
| 12 | 
            +
                    @extra_runtime_dependencies = Array(extra_runtime_dependencies)
         | 
| 13 | 
            +
                    @log_writer = log_writer
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def prune
         | 
| 17 | 
            +
                    return if ENV['PUMA_BUNDLER_PRUNED']
         | 
| 18 | 
            +
                    return unless defined?(Bundler)
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    require_rubygems_min_version!
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    unless puma_wild_path
         | 
| 23 | 
            +
                      log "! Unable to prune Bundler environment, continuing"
         | 
| 24 | 
            +
                      return
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                    dirs = paths_to_require_after_prune
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    log '* Pruning Bundler environment'
         | 
| 30 | 
            +
                    home = ENV['GEM_HOME']
         | 
| 31 | 
            +
                    bundle_gemfile = Bundler.original_env['BUNDLE_GEMFILE']
         | 
| 32 | 
            +
                    bundle_app_config = Bundler.original_env['BUNDLE_APP_CONFIG']
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    with_unbundled_env do
         | 
| 35 | 
            +
                      ENV['GEM_HOME'] = home
         | 
| 36 | 
            +
                      ENV['BUNDLE_GEMFILE'] = bundle_gemfile
         | 
| 37 | 
            +
                      ENV['PUMA_BUNDLER_PRUNED'] = '1'
         | 
| 38 | 
            +
                      ENV["BUNDLE_APP_CONFIG"] = bundle_app_config
         | 
| 39 | 
            +
                      args = [Gem.ruby, puma_wild_path, '-I', dirs.join(':')] + @original_argv
         | 
| 40 | 
            +
                      # Ruby 2.0+ defaults to true which breaks socket activation
         | 
| 41 | 
            +
                      args += [{:close_others => false}]
         | 
| 42 | 
            +
                      Kernel.exec(*args)
         | 
| 43 | 
            +
                    end
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  private
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  def require_rubygems_min_version!
         | 
| 49 | 
            +
                    min_version = Gem::Version.new('2.2')
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                    return if min_version <= Gem::Version.new(Gem::VERSION)
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                    raise "prune_bundler is not supported on your version of RubyGems. " \
         | 
| 54 | 
            +
                          "You must have RubyGems #{min_version}+ to use this feature."
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  def puma_wild_path
         | 
| 58 | 
            +
                    puma_lib_dir = puma_require_paths.detect { |x| File.exist? File.join(x, '../bin/puma-wild') }
         | 
| 59 | 
            +
                    File.expand_path(File.join(puma_lib_dir, '../bin/puma-wild'))
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  def with_unbundled_env
         | 
| 63 | 
            +
                    bundler_ver = Gem::Version.new(Bundler::VERSION)
         | 
| 64 | 
            +
                    if bundler_ver < Gem::Version.new('2.1.0')
         | 
| 65 | 
            +
                      Bundler.with_clean_env { yield }
         | 
| 66 | 
            +
                    else
         | 
| 67 | 
            +
                      Bundler.with_unbundled_env { yield }
         | 
| 68 | 
            +
                    end
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                  def paths_to_require_after_prune
         | 
| 72 | 
            +
                    puma_require_paths + extra_runtime_deps_paths
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  def extra_runtime_deps_paths
         | 
| 76 | 
            +
                    t = @extra_runtime_dependencies.map do |dep_name|
         | 
| 77 | 
            +
                      if (spec = spec_for_gem(dep_name))
         | 
| 78 | 
            +
                        require_paths_for_gem(spec)
         | 
| 79 | 
            +
                      else
         | 
| 80 | 
            +
                        log "* Could not load extra dependency: #{dep_name}"
         | 
| 81 | 
            +
                        nil
         | 
| 82 | 
            +
                      end
         | 
| 83 | 
            +
                    end
         | 
| 84 | 
            +
                    t.flatten!; t.compact!; t
         | 
| 85 | 
            +
                  end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                  def puma_require_paths
         | 
| 88 | 
            +
                    require_paths_for_gem(spec_for_gem('puma'))
         | 
| 89 | 
            +
                  end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                  def spec_for_gem(gem_name)
         | 
| 92 | 
            +
                    Bundler.rubygems.loaded_specs(gem_name)
         | 
| 93 | 
            +
                  end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                  def require_paths_for_gem(gem_spec)
         | 
| 96 | 
            +
                    gem_spec.full_require_paths
         | 
| 97 | 
            +
                  end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                  def log(str)
         | 
| 100 | 
            +
                    @log_writer.log(str)
         | 
| 101 | 
            +
                  end
         | 
| 102 | 
            +
                end
         | 
| 103 | 
            +
              end
         | 
| 104 | 
            +
            end
         |