puma 5.6.9-java → 6.6.0-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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +465 -18
  3. data/README.md +152 -42
  4. data/bin/puma-wild +1 -1
  5. data/docs/compile_options.md +34 -0
  6. data/docs/fork_worker.md +12 -4
  7. data/docs/java_options.md +54 -0
  8. data/docs/kubernetes.md +12 -0
  9. data/docs/nginx.md +1 -1
  10. data/docs/plugins.md +4 -0
  11. data/docs/restart.md +1 -0
  12. data/docs/signals.md +2 -2
  13. data/docs/stats.md +8 -3
  14. data/docs/systemd.md +13 -7
  15. data/docs/testing_benchmarks_local_files.md +150 -0
  16. data/docs/testing_test_rackup_ci_files.md +36 -0
  17. data/ext/puma_http11/extconf.rb +27 -17
  18. data/ext/puma_http11/http11_parser.c +1 -1
  19. data/ext/puma_http11/http11_parser.h +1 -1
  20. data/ext/puma_http11/http11_parser.java.rl +2 -2
  21. data/ext/puma_http11/http11_parser.rl +2 -2
  22. data/ext/puma_http11/http11_parser_common.rl +2 -2
  23. data/ext/puma_http11/mini_ssl.c +137 -19
  24. data/ext/puma_http11/org/jruby/puma/Http11.java +31 -10
  25. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +1 -1
  26. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +157 -53
  27. data/ext/puma_http11/puma_http11.c +21 -10
  28. data/lib/puma/app/status.rb +4 -4
  29. data/lib/puma/binder.rb +60 -55
  30. data/lib/puma/cli.rb +22 -20
  31. data/lib/puma/client.rb +93 -30
  32. data/lib/puma/cluster/worker.rb +27 -17
  33. data/lib/puma/cluster/worker_handle.rb +8 -6
  34. data/lib/puma/cluster.rb +121 -47
  35. data/lib/puma/commonlogger.rb +21 -14
  36. data/lib/puma/configuration.rb +101 -65
  37. data/lib/puma/const.rb +141 -93
  38. data/lib/puma/control_cli.rb +19 -15
  39. data/lib/puma/detect.rb +7 -4
  40. data/lib/puma/dsl.rb +521 -88
  41. data/lib/puma/error_logger.rb +22 -13
  42. data/lib/puma/events.rb +6 -126
  43. data/lib/puma/io_buffer.rb +39 -4
  44. data/lib/puma/jruby_restart.rb +0 -15
  45. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  46. data/lib/puma/launcher.rb +121 -181
  47. data/lib/puma/log_writer.rb +147 -0
  48. data/lib/puma/minissl/context_builder.rb +27 -12
  49. data/lib/puma/minissl.rb +105 -11
  50. data/lib/puma/null_io.rb +42 -2
  51. data/lib/puma/plugin/systemd.rb +90 -0
  52. data/lib/puma/plugin/tmp_restart.rb +1 -1
  53. data/lib/puma/puma_http11.jar +0 -0
  54. data/lib/puma/rack/builder.rb +6 -6
  55. data/lib/puma/rack/urlmap.rb +1 -1
  56. data/lib/puma/rack_default.rb +19 -4
  57. data/lib/puma/reactor.rb +19 -10
  58. data/lib/puma/request.rb +368 -169
  59. data/lib/puma/runner.rb +65 -22
  60. data/lib/puma/sd_notify.rb +146 -0
  61. data/lib/puma/server.rb +161 -102
  62. data/lib/puma/single.rb +13 -11
  63. data/lib/puma/state_file.rb +3 -6
  64. data/lib/puma/thread_pool.rb +71 -21
  65. data/lib/puma/util.rb +1 -12
  66. data/lib/puma.rb +9 -10
  67. data/lib/rack/handler/puma.rb +116 -86
  68. data/tools/Dockerfile +2 -2
  69. metadata +17 -12
  70. data/lib/puma/queue_close.rb +0 -26
  71. data/lib/puma/systemd.rb +0 -46
  72. data/lib/rack/version_restriction.rb +0 -15
data/lib/puma/binder.rb CHANGED
@@ -3,24 +3,15 @@
3
3
  require 'uri'
4
4
  require 'socket'
5
5
 
6
- require 'puma/const'
7
- require 'puma/util'
8
- require 'puma/configuration'
6
+ require_relative 'const'
7
+ require_relative 'util'
8
+ require_relative 'configuration'
9
9
 
10
10
  module Puma
11
11
 
12
12
  if HAS_SSL
13
- require 'puma/minissl'
14
- require 'puma/minissl/context_builder'
15
-
16
- # Odd bug in 'pure Ruby' nio4r version 2.5.2, which installs with Ruby 2.3.
17
- # NIO doesn't create any OpenSSL objects, but it rescues an OpenSSL error.
18
- # The bug was that it did not require openssl.
19
- # @todo remove when Ruby 2.3 support is dropped
20
- #
21
- if windows? && RbConfig::CONFIG['ruby_version'] == '2.3.0'
22
- require 'openssl'
23
- end
13
+ require_relative 'minissl'
14
+ require_relative 'minissl/context_builder'
24
15
  end
25
16
 
26
17
  class Binder
@@ -28,22 +19,23 @@ module Puma
28
19
 
29
20
  RACK_VERSION = [1,6].freeze
30
21
 
31
- def initialize(events, conf = Configuration.new)
32
- @events = events
22
+ def initialize(log_writer, conf = Configuration.new, env: ENV)
23
+ @log_writer = log_writer
33
24
  @conf = conf
34
25
  @listeners = []
35
26
  @inherited_fds = {}
36
27
  @activated_sockets = {}
37
28
  @unix_paths = []
29
+ @env = env
38
30
 
39
31
  @proto_env = {
40
32
  "rack.version".freeze => RACK_VERSION,
41
- "rack.errors".freeze => events.stderr,
33
+ "rack.errors".freeze => log_writer.stderr,
42
34
  "rack.multithread".freeze => conf.options[:max_threads] > 1,
43
35
  "rack.multiprocess".freeze => conf.options[:workers] >= 1,
44
36
  "rack.run_once".freeze => false,
45
37
  RACK_URL_SCHEME => conf.options[:rack_url_scheme],
46
- "SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
38
+ "SCRIPT_NAME".freeze => env['SCRIPT_NAME'] || "",
47
39
 
48
40
  # I'd like to set a default CONTENT_TYPE here but some things
49
41
  # depend on their not being a default set and inferring
@@ -51,14 +43,12 @@ module Puma
51
43
  # infer properly.
52
44
 
53
45
  "QUERY_STRING".freeze => "",
54
- SERVER_PROTOCOL => HTTP_11,
55
46
  SERVER_SOFTWARE => PUMA_SERVER_STRING,
56
47
  GATEWAY_INTERFACE => CGI_VER
57
48
  }
58
49
 
59
50
  @envs = {}
60
51
  @ios = []
61
- localhost_authority
62
52
  end
63
53
 
64
54
  attr_reader :ios
@@ -80,7 +70,7 @@ module Puma
80
70
  # @!attribute [r] connected_ports
81
71
  # @version 5.0.0
82
72
  def connected_ports
83
- ios.map { |io| io.addr[1] }.uniq
73
+ t = ios.map { |io| io.addr[1] }; t.uniq!; t
84
74
  end
85
75
 
86
76
  # @version 5.0.0
@@ -98,7 +88,7 @@ module Puma
98
88
  # @version 5.0.0
99
89
  #
100
90
  def create_activated_fds(env_hash)
101
- @events.debug "ENV['LISTEN_FDS'] #{ENV['LISTEN_FDS'].inspect} env_hash['LISTEN_PID'] #{env_hash['LISTEN_PID'].inspect}"
91
+ @log_writer.debug "ENV['LISTEN_FDS'] #{@env['LISTEN_FDS'].inspect} env_hash['LISTEN_PID'] #{env_hash['LISTEN_PID'].inspect}"
102
92
  return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
103
93
  env_hash['LISTEN_FDS'].to_i.times do |index|
104
94
  sock = TCPServer.for_fd(socket_activation_fd(index))
@@ -106,11 +96,11 @@ module Puma
106
96
  [:unix, Socket.unpack_sockaddr_un(sock.getsockname)]
107
97
  rescue ArgumentError # Try to parse as a port/ip
108
98
  port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
109
- addr = "[#{addr}]" if addr =~ /\:/
99
+ addr = "[#{addr}]" if addr&.include? ':'
110
100
  [:tcp, addr, port]
111
101
  end
112
102
  @activated_sockets[key] = sock
113
- @events.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
103
+ @log_writer.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
114
104
  end
115
105
  ["LISTEN_FDS", "LISTEN_PID"] # Signal to remove these keys from ENV
116
106
  end
@@ -152,29 +142,37 @@ module Puma
152
142
  end
153
143
  end
154
144
 
155
- def parse(binds, logger, log_msg = 'Listening')
145
+ def before_parse(&block)
146
+ @before_parse ||= []
147
+ @before_parse << block if block
148
+ @before_parse
149
+ end
150
+
151
+ def parse(binds, log_writer = nil, log_msg = 'Listening')
152
+ before_parse.each(&:call)
153
+ log_writer ||= @log_writer
156
154
  binds.each do |str|
157
155
  uri = URI.parse str
158
156
  case uri.scheme
159
157
  when "tcp"
160
158
  if fd = @inherited_fds.delete(str)
161
159
  io = inherit_tcp_listener uri.host, uri.port, fd
162
- logger.log "* Inherited #{str}"
160
+ log_writer.log "* Inherited #{str}"
163
161
  elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
164
162
  io = inherit_tcp_listener uri.host, uri.port, sock
165
- logger.log "* Activated #{str}"
163
+ log_writer.log "* Activated #{str}"
166
164
  else
167
165
  ios_len = @ios.length
168
166
  params = Util.parse_query uri.query
169
167
 
170
- opt = params.key?('low_latency') && params['low_latency'] != 'false'
168
+ low_latency = params.key?('low_latency') && params['low_latency'] != 'false'
171
169
  backlog = params.fetch('backlog', 1024).to_i
172
170
 
173
- io = add_tcp_listener uri.host, uri.port, opt, backlog
171
+ io = add_tcp_listener uri.host, uri.port, low_latency, backlog
174
172
 
175
173
  @ios[ios_len..-1].each do |i|
176
174
  addr = loc_addr_str i
177
- logger.log "* #{log_msg} on http://#{addr}"
175
+ log_writer.log "* #{log_msg} on http://#{addr}"
178
176
  end
179
177
  end
180
178
 
@@ -191,12 +189,12 @@ module Puma
191
189
  if fd = @inherited_fds.delete(str)
192
190
  @unix_paths << path unless abstract || File.exist?(path)
193
191
  io = inherit_unix_listener path, fd
194
- logger.log "* Inherited #{str}"
192
+ log_writer.log "* Inherited #{str}"
195
193
  elsif sock = @activated_sockets.delete([ :unix, path ]) ||
196
- @activated_sockets.delete([ :unix, File.realdirpath(path) ])
194
+ !abstract && @activated_sockets.delete([ :unix, File.realdirpath(path) ])
197
195
  @unix_paths << path unless abstract || File.exist?(path)
198
196
  io = inherit_unix_listener path, sock
199
- logger.log "* Activated #{str}"
197
+ log_writer.log "* Activated #{str}"
200
198
  else
201
199
  umask = nil
202
200
  mode = nil
@@ -220,11 +218,12 @@ module Puma
220
218
 
221
219
  @unix_paths << path unless abstract || File.exist?(path)
222
220
  io = add_unix_listener path, umask, mode, backlog
223
- logger.log "* #{log_msg} on #{str}"
221
+ log_writer.log "* #{log_msg} on #{str}"
224
222
  end
225
223
 
226
224
  @listeners << [str, io]
227
225
  when "ssl"
226
+ cert_key = %w[cert key]
228
227
 
229
228
  raise "Puma compiled without SSL support" unless HAS_SSL
230
229
 
@@ -233,49 +232,51 @@ module Puma
233
232
  # If key and certs are not defined and localhost gem is required.
234
233
  # localhost gem will be used for self signed
235
234
  # Load localhost authority if not loaded.
236
- if params.values_at('cert', 'key').all? { |v| v.to_s.empty? }
235
+ # Ruby 3 `values_at` accepts an array, earlier do not
236
+ if params.values_at(*cert_key).all? { |v| v.to_s.empty? }
237
237
  ctx = localhost_authority && localhost_authority_context
238
238
  end
239
239
 
240
240
  ctx ||=
241
241
  begin
242
242
  # Extract cert_pem and key_pem from options[:store] if present
243
- ['cert', 'key'].each do |v|
244
- if params[v] && params[v].start_with?('store:')
243
+ cert_key.each do |v|
244
+ if params[v]&.start_with?('store:')
245
245
  index = Integer(params.delete(v).split('store:').last)
246
246
  params["#{v}_pem"] = @conf.options[:store][index]
247
247
  end
248
248
  end
249
- MiniSSL::ContextBuilder.new(params, @events).context
249
+ MiniSSL::ContextBuilder.new(params, @log_writer).context
250
250
  end
251
251
 
252
252
  if fd = @inherited_fds.delete(str)
253
- logger.log "* Inherited #{str}"
253
+ log_writer.log "* Inherited #{str}"
254
254
  io = inherit_ssl_listener fd, ctx
255
255
  elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
256
256
  io = inherit_ssl_listener sock, ctx
257
- logger.log "* Activated #{str}"
257
+ log_writer.log "* Activated #{str}"
258
258
  else
259
259
  ios_len = @ios.length
260
260
  backlog = params.fetch('backlog', 1024).to_i
261
- io = add_ssl_listener uri.host, uri.port, ctx, optimize_for_latency = true, backlog
261
+ low_latency = params['low_latency'] != 'false'
262
+ io = add_ssl_listener uri.host, uri.port, ctx, low_latency, backlog
262
263
 
263
264
  @ios[ios_len..-1].each do |i|
264
265
  addr = loc_addr_str i
265
- logger.log "* #{log_msg} on ssl://#{addr}?#{uri.query}"
266
+ log_writer.log "* #{log_msg} on ssl://#{addr}?#{uri.query}"
266
267
  end
267
268
  end
268
269
 
269
270
  @listeners << [str, io] if io
270
271
  else
271
- logger.error "Invalid URI: #{str}"
272
+ log_writer.error "Invalid URI: #{str}"
272
273
  end
273
274
  end
274
275
 
275
276
  # If we inherited fds but didn't use them (because of a
276
277
  # configuration change), then be sure to close them.
277
278
  @inherited_fds.each do |str, fd|
278
- logger.log "* Closing unused inherited connection: #{str}"
279
+ log_writer.log "* Closing unused inherited connection: #{str}"
279
280
 
280
281
  begin
281
282
  IO.for_fd(fd).close
@@ -295,7 +296,7 @@ module Puma
295
296
  fds = @ios.map(&:to_i)
296
297
  @activated_sockets.each do |key, sock|
297
298
  next if fds.include? sock.to_i
298
- logger.log "* Closing unused activated socket: #{key.first}://#{key[1..-1].join ':'}"
299
+ log_writer.log "* Closing unused activated socket: #{key.first}://#{key[1..-1].join ':'}"
299
300
  begin
300
301
  sock.close
301
302
  rescue SystemCallError
@@ -319,7 +320,7 @@ module Puma
319
320
  local_certificates_path = File.expand_path("~/.localhost")
320
321
  [File.join(local_certificates_path, "localhost.key"), File.join(local_certificates_path, "localhost.crt")]
321
322
  end
322
- MiniSSL::ContextBuilder.new({ "key" => key_path, "cert" => crt_path }, @events).context
323
+ MiniSSL::ContextBuilder.new({ "key" => key_path, "cert" => crt_path }, @log_writer).context
323
324
  end
324
325
 
325
326
  # Tell the server to listen on host +host+, port +port+.
@@ -337,7 +338,7 @@ module Puma
337
338
  return
338
339
  end
339
340
 
340
- host = host[1..-2] if host and host[0..0] == '['
341
+ host = host[1..-2] if host&.start_with? '['
341
342
  tcp_server = TCPServer.new(host, port)
342
343
 
343
344
  if optimize_for_latency
@@ -371,7 +372,7 @@ module Puma
371
372
  return
372
373
  end
373
374
 
374
- host = host[1..-2] if host[0..0] == '['
375
+ host = host[1..-2] if host&.start_with? '['
375
376
  s = TCPServer.new(host, port)
376
377
  if optimize_for_latency
377
378
  s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
@@ -457,11 +458,14 @@ module Puma
457
458
 
458
459
  def close_listeners
459
460
  @listeners.each do |l, io|
460
- io.close unless io.closed?
461
- uri = URI.parse l
462
- next unless uri.scheme == 'unix'
463
- unix_path = "#{uri.host}#{uri.path}"
464
- File.unlink unix_path if @unix_paths.include?(unix_path) && File.exist?(unix_path)
461
+ begin
462
+ io.close unless io.closed?
463
+ uri = URI.parse l
464
+ next unless uri.scheme == 'unix'
465
+ unix_path = "#{uri.host}#{uri.path}"
466
+ File.unlink unix_path if @unix_paths.include?(unix_path) && File.exist?(unix_path)
467
+ rescue Errno::EBADF
468
+ end
465
469
  end
466
470
  end
467
471
 
@@ -482,9 +486,10 @@ module Puma
482
486
 
483
487
  # @!attribute [r] loopback_addresses
484
488
  def loopback_addresses
485
- Socket.ip_address_list.select do |addrinfo|
489
+ t = Socket.ip_address_list.select do |addrinfo|
486
490
  addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
487
- end.map { |addrinfo| addrinfo.ip_address }.uniq
491
+ end
492
+ t.map! { |addrinfo| addrinfo.ip_address }; t.uniq!; t
488
493
  end
489
494
 
490
495
  def loc_addr_str(io)
data/lib/puma/cli.rb CHANGED
@@ -3,11 +3,11 @@
3
3
  require 'optparse'
4
4
  require 'uri'
5
5
 
6
- require 'puma'
7
- require 'puma/configuration'
8
- require 'puma/launcher'
9
- require 'puma/const'
10
- require 'puma/events'
6
+ require_relative '../puma'
7
+ require_relative 'configuration'
8
+ require_relative 'launcher'
9
+ require_relative 'const'
10
+ require_relative 'log_writer'
11
11
 
12
12
  module Puma
13
13
  class << self
@@ -21,19 +21,13 @@ module Puma
21
21
  # Handles invoke a Puma::Server in a command line style.
22
22
  #
23
23
  class CLI
24
- # @deprecated 6.0.0
25
- KEYS_NOT_TO_PERSIST_IN_STATE = Launcher::KEYS_NOT_TO_PERSIST_IN_STATE
26
-
27
24
  # Create a new CLI object using +argv+ as the command line
28
25
  # arguments.
29
26
  #
30
- # +stdout+ and +stderr+ can be set to IO-like objects which
31
- # this object will report status on.
32
- #
33
- def initialize(argv, events=Events.stdio)
27
+ def initialize(argv, log_writer = LogWriter.stdio, events = Events.new, env: ENV)
34
28
  @debug = false
35
29
  @argv = argv.dup
36
-
30
+ @log_writer = log_writer
37
31
  @events = events
38
32
 
39
33
  @conf = nil
@@ -45,7 +39,7 @@ module Puma
45
39
  @control_url = nil
46
40
  @control_options = {}
47
41
 
48
- setup_options
42
+ setup_options env
49
43
 
50
44
  begin
51
45
  @parser.parse! @argv
@@ -69,7 +63,7 @@ module Puma
69
63
  end
70
64
  end
71
65
 
72
- @launcher = Puma::Launcher.new(@conf, :events => @events, :argv => argv)
66
+ @launcher = Puma::Launcher.new(@conf, env: ENV, log_writer: @log_writer, events: @events, argv: argv)
73
67
  end
74
68
 
75
69
  attr_reader :launcher
@@ -83,7 +77,7 @@ module Puma
83
77
 
84
78
  private
85
79
  def unsupported(str)
86
- @events.error(str)
80
+ @log_writer.error(str)
87
81
  raise UnsupportedOption
88
82
  end
89
83
 
@@ -98,8 +92,8 @@ module Puma
98
92
  # Build the OptionParser object to handle the available options.
99
93
  #
100
94
 
101
- def setup_options
102
- @conf = Configuration.new do |user_config, file_config|
95
+ def setup_options(env = ENV)
96
+ @conf = Configuration.new({}, {events: @events}, env) do |user_config, file_config|
103
97
  @parser = OptionParser.new do |o|
104
98
  o.on "-b", "--bind URI", "URI to bind to (tcp://, unix://, ssl://)" do |arg|
105
99
  user_config.bind arg
@@ -150,15 +144,23 @@ module Puma
150
144
  $LOAD_PATH.unshift(*arg.split(':'))
151
145
  end
152
146
 
147
+ o.on "--idle-timeout SECONDS", "Number of seconds until the next request before automatic shutdown" do |arg|
148
+ user_config.idle_timeout arg
149
+ end
150
+
153
151
  o.on "-p", "--port PORT", "Define the TCP port to bind to",
154
152
  "Use -b for more advanced options" do |arg|
155
- user_config.bind "tcp://#{Configuration::DefaultTCPHost}:#{arg}"
153
+ user_config.bind "tcp://#{Configuration::DEFAULTS[:tcp_host]}:#{arg}"
156
154
  end
157
155
 
158
156
  o.on "--pidfile PATH", "Use PATH as a pidfile" do |arg|
159
157
  user_config.pidfile arg
160
158
  end
161
159
 
160
+ o.on "--plugin PLUGIN", "Load the given PLUGIN. Can be used multiple times to load multiple plugins." do |arg|
161
+ user_config.plugin arg
162
+ end
163
+
162
164
  o.on "--preload", "Preload the app. Cluster mode only" do
163
165
  user_config.preload_app!
164
166
  end
@@ -186,7 +188,7 @@ module Puma
186
188
  end
187
189
 
188
190
  o.on "-s", "--silent", "Do not log prompt messages other than errors" do
189
- @events = Events.new NullIO.new, $stderr
191
+ @log_writer = LogWriter.new(NullIO.new, $stderr)
190
192
  end
191
193
 
192
194
  o.on "-S", "--state PATH", "Where to store the state details" do |arg|