puma 5.6.7 → 6.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +327 -16
  3. data/README.md +79 -29
  4. data/bin/puma-wild +1 -1
  5. data/docs/compile_options.md +34 -0
  6. data/docs/fork_worker.md +1 -3
  7. data/docs/kubernetes.md +12 -0
  8. data/docs/nginx.md +1 -1
  9. data/docs/restart.md +1 -0
  10. data/docs/systemd.md +3 -6
  11. data/docs/testing_benchmarks_local_files.md +150 -0
  12. data/docs/testing_test_rackup_ci_files.md +36 -0
  13. data/ext/puma_http11/extconf.rb +16 -9
  14. data/ext/puma_http11/http11_parser.c +1 -1
  15. data/ext/puma_http11/http11_parser.h +1 -1
  16. data/ext/puma_http11/http11_parser.java.rl +2 -2
  17. data/ext/puma_http11/http11_parser.rl +2 -2
  18. data/ext/puma_http11/http11_parser_common.rl +2 -2
  19. data/ext/puma_http11/mini_ssl.c +127 -19
  20. data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
  21. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +1 -1
  22. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +157 -53
  23. data/ext/puma_http11/puma_http11.c +17 -9
  24. data/lib/puma/app/status.rb +4 -4
  25. data/lib/puma/binder.rb +50 -53
  26. data/lib/puma/cli.rb +16 -18
  27. data/lib/puma/client.rb +86 -19
  28. data/lib/puma/cluster/worker.rb +18 -11
  29. data/lib/puma/cluster/worker_handle.rb +4 -1
  30. data/lib/puma/cluster.rb +102 -40
  31. data/lib/puma/commonlogger.rb +21 -14
  32. data/lib/puma/configuration.rb +77 -59
  33. data/lib/puma/const.rb +129 -92
  34. data/lib/puma/control_cli.rb +15 -11
  35. data/lib/puma/detect.rb +7 -4
  36. data/lib/puma/dsl.rb +250 -56
  37. data/lib/puma/error_logger.rb +18 -9
  38. data/lib/puma/events.rb +6 -126
  39. data/lib/puma/io_buffer.rb +39 -4
  40. data/lib/puma/jruby_restart.rb +2 -1
  41. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  42. data/lib/puma/launcher.rb +102 -175
  43. data/lib/puma/log_writer.rb +147 -0
  44. data/lib/puma/minissl/context_builder.rb +26 -12
  45. data/lib/puma/minissl.rb +104 -11
  46. data/lib/puma/null_io.rb +16 -2
  47. data/lib/puma/plugin/systemd.rb +90 -0
  48. data/lib/puma/plugin/tmp_restart.rb +1 -1
  49. data/lib/puma/rack/builder.rb +6 -6
  50. data/lib/puma/rack/urlmap.rb +1 -1
  51. data/lib/puma/rack_default.rb +19 -4
  52. data/lib/puma/reactor.rb +19 -10
  53. data/lib/puma/request.rb +365 -170
  54. data/lib/puma/runner.rb +56 -20
  55. data/lib/puma/sd_notify.rb +149 -0
  56. data/lib/puma/server.rb +137 -89
  57. data/lib/puma/single.rb +13 -11
  58. data/lib/puma/state_file.rb +3 -6
  59. data/lib/puma/thread_pool.rb +57 -19
  60. data/lib/puma/util.rb +0 -11
  61. data/lib/puma.rb +9 -10
  62. data/lib/rack/handler/puma.rb +113 -86
  63. data/tools/Dockerfile +2 -2
  64. metadata +11 -7
  65. data/lib/puma/queue_close.rb +0 -26
  66. data/lib/puma/systemd.rb +0 -46
  67. data/lib/rack/version_restriction.rb +0 -15
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
- @debug = ENV.key? 'PUMA_DEBUG'
34
- @error_logger = ErrorLogger.new(@stderr)
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
@@ -1,11 +1,46 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'stringio'
4
+
3
5
  module Puma
4
- class IOBuffer < String
5
- def append(*args)
6
- args.each { |a| concat(a) }
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
- alias reset clear
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
@@ -16,7 +16,8 @@ module Puma
16
16
  def self.chdir_exec(dir, argv)
17
17
  chdir(dir)
18
18
  cmd = argv.first
19
- argv = ([:string] * argv.size).zip(argv).flatten
19
+ argv = ([:string] * argv.size).zip(argv)
20
+ argv.flatten!
20
21
  argv << :string
21
22
  argv << nil
22
23
  execlp(cmd, *argv)
@@ -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