puma 5.6.5 → 6.0.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puma might be problematic. Click here for more details.

Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +120 -11
  3. data/README.md +21 -17
  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/nginx.md +1 -1
  8. data/docs/testing_benchmarks_local_files.md +150 -0
  9. data/docs/testing_test_rackup_ci_files.md +36 -0
  10. data/ext/puma_http11/extconf.rb +11 -8
  11. data/ext/puma_http11/http11_parser.c +1 -1
  12. data/ext/puma_http11/http11_parser.h +1 -1
  13. data/ext/puma_http11/http11_parser.java.rl +2 -2
  14. data/ext/puma_http11/http11_parser.rl +2 -2
  15. data/ext/puma_http11/http11_parser_common.rl +2 -2
  16. data/ext/puma_http11/mini_ssl.c +36 -15
  17. data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
  18. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +1 -1
  19. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +156 -53
  20. data/ext/puma_http11/puma_http11.c +17 -9
  21. data/lib/puma/app/status.rb +3 -3
  22. data/lib/puma/binder.rb +36 -42
  23. data/lib/puma/cli.rb +11 -17
  24. data/lib/puma/client.rb +26 -13
  25. data/lib/puma/cluster/worker.rb +13 -11
  26. data/lib/puma/cluster/worker_handle.rb +4 -1
  27. data/lib/puma/cluster.rb +28 -25
  28. data/lib/puma/configuration.rb +74 -58
  29. data/lib/puma/const.rb +14 -18
  30. data/lib/puma/control_cli.rb +3 -6
  31. data/lib/puma/detect.rb +2 -0
  32. data/lib/puma/dsl.rb +96 -52
  33. data/lib/puma/error_logger.rb +17 -9
  34. data/lib/puma/events.rb +6 -126
  35. data/lib/puma/io_buffer.rb +39 -4
  36. data/lib/puma/jruby_restart.rb +2 -1
  37. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  38. data/lib/puma/launcher.rb +96 -156
  39. data/lib/puma/log_writer.rb +137 -0
  40. data/lib/puma/minissl/context_builder.rb +23 -12
  41. data/lib/puma/minissl.rb +82 -11
  42. data/lib/puma/plugin/tmp_restart.rb +1 -1
  43. data/lib/puma/rack/builder.rb +4 -4
  44. data/lib/puma/rack_default.rb +1 -1
  45. data/lib/puma/reactor.rb +4 -4
  46. data/lib/puma/request.rb +334 -166
  47. data/lib/puma/runner.rb +41 -20
  48. data/lib/puma/server.rb +55 -71
  49. data/lib/puma/single.rb +10 -10
  50. data/lib/puma/state_file.rb +1 -4
  51. data/lib/puma/systemd.rb +3 -2
  52. data/lib/puma/thread_pool.rb +16 -16
  53. data/lib/puma/util.rb +0 -11
  54. data/lib/puma.rb +12 -9
  55. data/lib/rack/handler/puma.rb +9 -9
  56. metadata +7 -3
  57. data/lib/puma/queue_close.rb +0 -26
data/lib/puma/minissl.rb CHANGED
@@ -1,11 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  begin
4
- require 'io/wait'
4
+ require 'io/wait' unless Puma::HAS_NATIVE_IO_WAIT
5
5
  rescue LoadError
6
6
  end
7
7
 
8
8
  # need for Puma::MiniSSL::OPENSSL constants used in `HAS_TLS1_3`
9
+ # use require, see https://github.com/puma/puma/pull/2381
9
10
  require 'puma/puma_http11'
10
11
 
11
12
  module Puma
@@ -13,15 +14,16 @@ module Puma
13
14
  # Define constant at runtime, as it's easy to determine at built time,
14
15
  # but Puma could (it shouldn't) be loaded with an older OpenSSL version
15
16
  # @version 5.0.0
16
- HAS_TLS1_3 = !IS_JRUBY &&
17
- (OPENSSL_VERSION[/ \d+\.\d+\.\d+/].split('.').map(&:to_i) <=> [1,1,1]) != -1 &&
18
- (OPENSSL_LIBRARY_VERSION[/ \d+\.\d+\.\d+/].split('.').map(&:to_i) <=> [1,1,1]) !=-1
17
+ HAS_TLS1_3 = IS_JRUBY ||
18
+ ((OPENSSL_VERSION[/ \d+\.\d+\.\d+/].split('.').map(&:to_i) <=> [1,1,1]) != -1 &&
19
+ (OPENSSL_LIBRARY_VERSION[/ \d+\.\d+\.\d+/].split('.').map(&:to_i) <=> [1,1,1]) !=-1)
19
20
 
20
21
  class Socket
21
22
  def initialize(socket, engine)
22
23
  @socket = socket
23
24
  @engine = engine
24
25
  @peercert = nil
26
+ @reuse = nil
25
27
  end
26
28
 
27
29
  # @!attribute [r] to_io
@@ -50,7 +52,7 @@ module Puma
50
52
  # is made with TLSv1.3 as an available protocol
51
53
  # @version 5.0.0
52
54
  def bad_tlsv1_3?
53
- HAS_TLS1_3 && @engine.ssl_vers_st == ['TLSv1.3', 'SSLERR']
55
+ HAS_TLS1_3 && ssl_version_state == ['TLSv1.3', 'SSLERR']
54
56
  end
55
57
  private :bad_tlsv1_3?
56
58
 
@@ -123,7 +125,7 @@ module Puma
123
125
  while true
124
126
  wrote = @engine.write data
125
127
 
126
- enc_wr = ''.dup
128
+ enc_wr = +''
127
129
  while (enc = @engine.extract)
128
130
  enc_wr << enc
129
131
  end
@@ -195,10 +197,6 @@ module Puma
195
197
  if IS_JRUBY
196
198
  OPENSSL_NO_SSL3 = false
197
199
  OPENSSL_NO_TLS1 = false
198
-
199
- class SSLError < StandardError
200
- # Define this for jruby even though it isn't used.
201
- end
202
200
  end
203
201
 
204
202
  class Context
@@ -212,6 +210,9 @@ module Puma
212
210
  @cert = nil
213
211
  @key_pem = nil
214
212
  @cert_pem = nil
213
+ @reuse = nil
214
+ @reuse_cache_size = nil
215
+ @reuse_timeout = nil
215
216
  end
216
217
 
217
218
  def check_file(file, desc)
@@ -222,16 +223,55 @@ module Puma
222
223
  if IS_JRUBY
223
224
  # jruby-specific Context properties: java uses a keystore and password pair rather than a cert/key pair
224
225
  attr_reader :keystore
226
+ attr_reader :keystore_type
225
227
  attr_accessor :keystore_pass
226
- attr_accessor :ssl_cipher_list
228
+ attr_reader :truststore
229
+ attr_reader :truststore_type
230
+ attr_accessor :truststore_pass
231
+ attr_reader :cipher_suites
232
+ attr_reader :protocols
227
233
 
228
234
  def keystore=(keystore)
229
235
  check_file keystore, 'Keystore'
230
236
  @keystore = keystore
231
237
  end
232
238
 
239
+ def truststore=(truststore)
240
+ # NOTE: historically truststore was assumed the same as keystore, this is kept for backwards
241
+ # compatibility, to rely on JVM's trust defaults we allow setting `truststore = :default`
242
+ unless truststore.eql?(:default)
243
+ raise ArgumentError, "No such truststore file '#{truststore}'" unless File.exist?(truststore)
244
+ end
245
+ @truststore = truststore
246
+ end
247
+
248
+ def keystore_type=(type)
249
+ raise ArgumentError, "Invalid keystore type: #{type.inspect}" unless ['pkcs12', 'jks', nil].include?(type)
250
+ @keystore_type = type
251
+ end
252
+
253
+ def truststore_type=(type)
254
+ raise ArgumentError, "Invalid truststore type: #{type.inspect}" unless ['pkcs12', 'jks', nil].include?(type)
255
+ @truststore_type = type
256
+ end
257
+
258
+ def cipher_suites=(list)
259
+ list = list.split(',').map(&:strip) if list.is_a?(String)
260
+ @cipher_suites = list
261
+ end
262
+
263
+ # aliases for backwards compatibility
264
+ alias_method :ssl_cipher_list, :cipher_suites
265
+ alias_method :ssl_cipher_list=, :cipher_suites=
266
+
267
+ def protocols=(list)
268
+ list = list.split(',').map(&:strip) if list.is_a?(String)
269
+ @protocols = list
270
+ end
271
+
233
272
  def check
234
273
  raise "Keystore not configured" unless @keystore
274
+ # @truststore defaults to @keystore due backwards compatibility
235
275
  end
236
276
 
237
277
  else
@@ -244,6 +284,8 @@ module Puma
244
284
  attr_accessor :ssl_cipher_filter
245
285
  attr_accessor :verification_flags
246
286
 
287
+ attr_reader :reuse, :reuse_cache_size, :reuse_timeout
288
+
247
289
  def key=(key)
248
290
  check_file key, 'Key'
249
291
  @key = key
@@ -273,6 +315,35 @@ module Puma
273
315
  raise "Key not configured" if @key.nil? && @key_pem.nil?
274
316
  raise "Cert not configured" if @cert.nil? && @cert_pem.nil?
275
317
  end
318
+
319
+ # Controls session reuse. Allowed values are as follows:
320
+ # * 'off' - matches the behavior of Puma 5.6 and earlier. This is included
321
+ # in case reuse 'on' is made the default in future Puma versions.
322
+ # * 'dflt' - sets session reuse on, with OpenSSL default cache size of
323
+ # 20k and default timeout of 300 seconds.
324
+ # * 's,t' - where s and t are integer strings, for size and timeout.
325
+ # * 's' - where s is an integer strings for size.
326
+ # * ',t' - where t is an integer strings for timeout.
327
+ #
328
+ def reuse=(reuse_str)
329
+ case reuse_str
330
+ when 'off'
331
+ @reuse = nil
332
+ when 'dflt'
333
+ @reuse = true
334
+ when /\A\d+\z/
335
+ @reuse = true
336
+ @reuse_cache_size = reuse_str.to_i
337
+ when /\A\d+,\d+\z/
338
+ @reuse = true
339
+ size, time = reuse_str.split ','
340
+ @reuse_cache_size = size.to_i
341
+ @reuse_timeout = time.to_i
342
+ when /\A,\d+\z/
343
+ @reuse = true
344
+ @reuse_timeout = reuse_str.delete(',').to_i
345
+ end
346
+ end
276
347
  end
277
348
 
278
349
  # disables TLSv1
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'puma/plugin'
3
+ require_relative '../plugin'
4
4
 
5
5
  Puma::Plugin.create do
6
6
  def start(launcher)
@@ -102,13 +102,13 @@ module Puma::Rack
102
102
  begin
103
103
  info = []
104
104
  server = Rack::Handler.get(options[:server]) || Rack::Handler.default(options)
105
- if server && server.respond_to?(:valid_options)
105
+ if server&.respond_to?(:valid_options)
106
106
  info << ""
107
107
  info << "Server-specific options for #{server.name}:"
108
108
 
109
109
  has_options = false
110
110
  server.valid_options.each do |name, description|
111
- next if name.to_s =~ /^(Host|Port)[^a-zA-Z]/ # ignore handler's host and port options, we do our own.
111
+ next if /^(Host|Port)[^a-zA-Z]/.match? name.to_s # ignore handler's host and port options, we do our own.
112
112
 
113
113
  info << " -O %-21s %s" % [name, description]
114
114
  has_options = true
@@ -276,7 +276,7 @@ module Puma::Rack
276
276
  app = @map ? generate_map(@run, @map) : @run
277
277
  fail "missing run or map statement" unless app
278
278
  app = @use.reverse.inject(app) { |a,e| e[a] }
279
- @warmup.call(app) if @warmup
279
+ @warmup&.call app
280
280
  app
281
281
  end
282
282
 
@@ -287,7 +287,7 @@ module Puma::Rack
287
287
  private
288
288
 
289
289
  def generate_map(default_app, mapping)
290
- require 'puma/rack/urlmap'
290
+ require_relative 'urlmap'
291
291
 
292
292
  mapped = default_app ? {'/' => default_app} : {}
293
293
  mapping.each { |r,b| mapped[r] = self.class.new(default_app, &b).to_app }
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rack/handler/puma'
3
+ require_relative '../rack/handler/puma'
4
4
 
5
5
  module Rack::Handler
6
6
  def self.default(options = {})
data/lib/puma/reactor.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'puma/queue_close' unless ::Queue.instance_methods.include? :close
3
+ require_relative 'queue_close' unless ::Queue.instance_methods.include? :close
4
4
 
5
5
  module Puma
6
6
  class UnsupportedBackend < StandardError; end
@@ -50,7 +50,7 @@ module Puma
50
50
  @input << client
51
51
  @selector.wakeup
52
52
  true
53
- rescue ClosedQueueError
53
+ rescue ClosedQueueError, IOError # Ignore if selector is already closed
54
54
  false
55
55
  end
56
56
 
@@ -61,7 +61,7 @@ module Puma
61
61
  @selector.wakeup
62
62
  rescue IOError # Ignore if selector is already closed
63
63
  end
64
- @thread.join if @thread
64
+ @thread&.join
65
65
  end
66
66
 
67
67
  private
@@ -76,7 +76,7 @@ module Puma
76
76
 
77
77
  # Wakeup all objects that timed out.
78
78
  timed_out = @timeouts.take_while {|t| t.timeout == 0}
79
- timed_out.each(&method(:wakeup!))
79
+ timed_out.each { |c| wakeup! c }
80
80
 
81
81
  unless @input.empty?
82
82
  until @input.empty?