puma 5.3.1 → 5.4.0

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bcabc9a852bfbed380445b6649ee9e27a2e4741994a27b579e5c4fe94ab6e282
4
- data.tar.gz: 00ae9ffe82807c9419afaaf955af384f2c87b07914ab36e32c060a83df4075e9
3
+ metadata.gz: 8615fe84e162e127ef524e304e0cdc330d4f66b57fcd3339c9c32883fef73011
4
+ data.tar.gz: 505bf893eb69b1e910320dcb4769fe259cfa880e764900c8d1091b6f9baa7533
5
5
  SHA512:
6
- metadata.gz: 6a6d4ca8d9ffeea284756f5cc6418b0d6649e360ab5b76bec8db8a6bd6d5cf059957725f3ac50949fd15bc1e54f31c6f50b1b5073cef6814dfca042d84cc04e6
7
- data.tar.gz: f5ad303fd4d4d3256fa0b20a0d608c2cfdea87901c8c56407420078cac5cd69591a686d12153865855532d0702122d34c80bf60ae4aed1f5ec081d20d3414f7e
6
+ metadata.gz: 1c347073ca1dc41fb975cd0cfd19fecfa72780e11f08151caed06993b1045a0c2204911d77cbee967b332c68adc4940ad0c0f1f1b26cc105f1d3c923f3fee4f8
7
+ data.tar.gz: a67f6b4a6959b3f47deb0966e7c2a35697cc1021f77cc7598bdad080c60ef6705be39c0352da76c20bab5fd3165d1e207db8df0bad9ec4633dd5bbefdee3b39e
data/History.md CHANGED
@@ -1,7 +1,29 @@
1
+ ## 5.4.0 / 2021-07-28
2
+
3
+ * Features
4
+ * Better/expanded names for threadpool threads ([#2657])
5
+ * Allow pkg_config for OpenSSL ([#2648], [#1412])
6
+ * Add `rack_url_scheme` to Puma::DSL, allows setting of `rack.url_scheme` header ([#2586], [#2569])
7
+
8
+ * Bugfixes
9
+ * `Binder#parse` - allow for symlinked unix path, add create_activated_fds debug ENV ([#2643], [#2638])
10
+ * Fix deprecation warning: minissl.c - Use Random.bytes if available ([#2642])
11
+ * Client certificates: set session id context while creating SSLContext ([#2633])
12
+
13
+ * Refactor
14
+ * Replace `IO.select` with `IO#wait_*` when checking a single IO ([#2666])
15
+
16
+ ## 5.3.2 / 2021-05-21
17
+
18
+ * Bugfixes
19
+ * Gracefully handle Rack not accepting CLI options ([#2630], [#2626])
20
+ * Fix sigterm misbehavior ([#2629])
21
+ * Improvements to keepalive-connection shedding ([#2628])
22
+
1
23
  ## 5.3.1 / 2021-05-11
2
24
 
3
25
  * Security
4
- * Close keepalive connections after the maximum number of fast inlined requests (#2625)
26
+ * Close keepalive connections after the maximum number of fast inlined requests (CVE-2021-29509) ([#2625])
5
27
 
6
28
  ## 5.3.0 / 2021-05-07
7
29
 
@@ -216,7 +238,7 @@
216
238
  ## 4.3.8 / 2021-05-11
217
239
 
218
240
  * Security
219
- * Close keepalive connections after the maximum number of fast inlined requests (#2625)
241
+ * Close keepalive connections after the maximum number of fast inlined requests (CVE-2021-29509) ([#2625])
220
242
 
221
243
  ## 4.3.7 / 2020-11-30
222
244
 
@@ -1746,6 +1768,21 @@ be added back in a future date when a java Puma::MiniSSL is added.
1746
1768
  * Bugfixes
1747
1769
  * Your bugfix goes here <Most recent on the top, like GitHub> (#Github Number)
1748
1770
 
1771
+ [#2657]:https://github.com/puma/puma/pull/2657 "PR by @olivierbellone, merged 2021-07-13"
1772
+ [#2648]:https://github.com/puma/puma/pull/2648 "PR by @MSP-Greg, merged 2021-06-27"
1773
+ [#1412]:https://github.com/puma/puma/issues/1412 "Issue by @x-yuri, closed 2021-06-27"
1774
+ [#2586]:https://github.com/puma/puma/pull/2586 "PR by @MSP-Greg, merged 2021-05-26"
1775
+ [#2569]:https://github.com/puma/puma/issues/2569 "Issue by @tarragon, closed 2021-05-26"
1776
+ [#2643]:https://github.com/puma/puma/pull/2643 "PR by @MSP-Greg, merged 2021-06-27"
1777
+ [#2638]:https://github.com/puma/puma/issues/2638 "Issue by @gingerlime, closed 2021-06-27"
1778
+ [#2642]:https://github.com/puma/puma/pull/2642 "PR by @MSP-Greg, merged 2021-06-16"
1779
+ [#2633]:https://github.com/puma/puma/pull/2633 "PR by @onlined, merged 2021-06-04"
1780
+ [#2666]:https://github.com/puma/puma/pull/2666 "PR by @MSP-Greg, merged 2021-07-25"
1781
+ [#2630]:https://github.com/puma/puma/pull/2630 "PR by @seangoedecke, merged 2021-05-20"
1782
+ [#2626]:https://github.com/puma/puma/issues/2626 "Issue by @rorymckinley, closed 2021-05-20"
1783
+ [#2629]:https://github.com/puma/puma/pull/2629 "PR by @ye-lin-aung, merged 2021-05-20"
1784
+ [#2628]:https://github.com/puma/puma/pull/2628 "PR by @wjordan, merged 2021-05-20"
1785
+ [#2625]:https://github.com/puma/puma/issues/2625 "Issue by @jarthod, closed 2021-05-11"
1749
1786
  [#2564]:https://github.com/puma/puma/pull/2564 "PR by @MSP-Greg, merged 2021-04-24"
1750
1787
  [#2526]:https://github.com/puma/puma/issues/2526 "Issue by @nerdrew, closed 2021-04-24"
1751
1788
  [#2559]:https://github.com/puma/puma/pull/2559 "PR by @ylecuyer, merged 2021-03-11"
@@ -1760,7 +1797,7 @@ be added back in a future date when a java Puma::MiniSSL is added.
1760
1797
  [#2605]:https://github.com/puma/puma/pull/2605 "PR by @pascalbetz, merged 2021-04-26"
1761
1798
  [#2584]:https://github.com/puma/puma/issues/2584 "Issue by @kaorihinata, closed 2021-04-26"
1762
1799
  [#2607]:https://github.com/puma/puma/pull/2607 "PR by @calvinxiao, merged 2021-04-23"
1763
- [#2552]:https://github.com/puma/puma/issues/2552 "Issue by @feliperaul, opened 2021-02-09"
1800
+ [#2552]:https://github.com/puma/puma/issues/2552 "Issue by @feliperaul, closed 2021-05-24"
1764
1801
  [#2606]:https://github.com/puma/puma/pull/2606 "PR by @wjordan, merged 2021-04-20"
1765
1802
  [#2574]:https://github.com/puma/puma/issues/2574 "Issue by @darkhelmet, closed 2021-04-20"
1766
1803
  [#2567]:https://github.com/puma/puma/pull/2567 "PR by @kddeisz, merged 2021-04-19"
data/docs/deployment.md CHANGED
@@ -97,20 +97,5 @@ and use `runit` or hell, even `monit`.
97
97
  ## Restarting
98
98
 
99
99
  You probably will want to deploy some new code at some point, and you'd like
100
- puma to start running that new code. Minimizing the amount of time the server
101
- is unavailable would be nice as well. Here's how to do it:
102
-
103
- 1. Don't use `preload!`. This dirties the master process and means it will have
104
- to shutdown all the workers and re-exec itself to get your new code. It is not compatible with phased-restart and `prune_bundler` as well.
105
-
106
- 1. Use `prune_bundler`. This makes it so that the cluster master will detach itself
107
- from a Bundler context on start. This allows the cluster workers to load your app
108
- and start a brand new Bundler context within the worker only. This means your
109
- master remains pristine and can live on between new releases of your code.
110
-
111
- 1. Use phased-restart (`SIGUSR1` or `pumactl phased-restart`). This tells the master
112
- to kill off one worker at a time and restart them in your new code. This minimizes
113
- downtime and staggers the restart nicely. **WARNING** This means that both your
114
- old code and your new code will be running concurrently. Most deployment solutions
115
- already cause that, but it's worth warning you about it again. Be careful with your
116
- migrations, etc!
100
+ puma to start running that new code. There are a few options for restarting
101
+ puma, described separately in our [restart documentation](restart.md).
@@ -11,9 +11,18 @@ end
11
11
  unless ENV["DISABLE_SSL"]
12
12
  dir_config("openssl")
13
13
 
14
- if %w'crypto libeay32'.find {|crypto| have_library(crypto, 'BIO_read')} and
14
+ found_ssl = if pkg_config 'openssl'
15
+ puts 'using OpenSSL pkgconfig (openssl.pc)'
16
+ true
17
+ elsif %w'crypto libeay32'.find {|crypto| have_library(crypto, 'BIO_read')} &&
15
18
  %w'ssl ssleay32'.find {|ssl| have_library(ssl, 'SSL_CTX_new')}
19
+ true
20
+ else
21
+ puts '** Puma will be compiled without SSL support'
22
+ false
23
+ end
16
24
 
25
+ if found_ssl
17
26
  have_header "openssl/bio.h"
18
27
 
19
28
  # below is yes for 1.0.2 & later
@@ -25,6 +34,14 @@ unless ENV["DISABLE_SSL"]
25
34
 
26
35
  have_func "X509_STORE_up_ref"
27
36
  have_func("SSL_CTX_set_ecdh_auto(NULL, 0)", "openssl/ssl.h")
37
+
38
+ # Random.bytes available in Ruby 2.5 and later, Random::DEFAULT deprecated in 3.0
39
+ if Random.respond_to?(:bytes)
40
+ $defs.push("-DHAVE_RANDOM_BYTES")
41
+ puts "checking for Random.bytes... yes"
42
+ else
43
+ puts "checking for Random.bytes... no"
44
+ end
28
45
  end
29
46
  end
30
47
 
@@ -208,7 +208,7 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
208
208
  #endif
209
209
  int ssl_options;
210
210
  VALUE key, cert, ca, verify_mode, ssl_cipher_filter, no_tlsv1, no_tlsv1_1,
211
- verification_flags;
211
+ verification_flags, session_id_bytes;
212
212
  DH *dh;
213
213
 
214
214
  #if OPENSSL_VERSION_NUMBER < 0x10002000L
@@ -309,6 +309,21 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
309
309
  } else {
310
310
  SSL_CTX_set_verify(ctx, NUM2INT(verify_mode), engine_verify_callback);
311
311
  }
312
+
313
+ // Random.bytes available in Ruby 2.5 and later, Random::DEFAULT deprecated in 3.0
314
+ session_id_bytes = rb_funcall(
315
+ #ifdef HAVE_RANDOM_BYTES
316
+ rb_cRandom,
317
+ #else
318
+ rb_const_get(rb_cRandom, rb_intern_const("DEFAULT")),
319
+ #endif
320
+ rb_intern_const("bytes"),
321
+ 1, ULL2NUM(SSL_MAX_SSL_SESSION_ID_LENGTH));
322
+
323
+ SSL_CTX_set_session_id_context(ctx,
324
+ (unsigned char *) RSTRING_PTR(session_id_bytes),
325
+ SSL_MAX_SSL_SESSION_ID_LENGTH);
326
+
312
327
  // printf("\ninitialize end security_level %d\n", SSL_CTX_get_security_level(ctx));
313
328
  rb_obj_freeze(self);
314
329
  return self;
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require 'puma/json'
2
+ require 'puma/json_serialization'
3
3
 
4
4
  module Puma
5
5
  module App
@@ -46,17 +46,17 @@ module Puma
46
46
  GC.start ; 200
47
47
 
48
48
  when 'gc-stats'
49
- Puma::JSON.generate GC.stat
49
+ Puma::JSONSerialization.generate GC.stat
50
50
 
51
51
  when 'stats'
52
- Puma::JSON.generate @launcher.stats
52
+ Puma::JSONSerialization.generate @launcher.stats
53
53
 
54
54
  when 'thread-backtraces'
55
55
  backtraces = []
56
56
  @launcher.thread_status do |name, backtrace|
57
57
  backtraces << { name: name, backtrace: backtrace }
58
58
  end
59
- Puma::JSON.generate backtraces
59
+ Puma::JSONSerialization.generate backtraces
60
60
 
61
61
  else
62
62
  return rack_response(404, "Unsupported action", 'text/plain')
data/lib/puma/binder.rb CHANGED
@@ -41,6 +41,7 @@ module Puma
41
41
  "rack.multithread".freeze => conf.options[:max_threads] > 1,
42
42
  "rack.multiprocess".freeze => conf.options[:workers] >= 1,
43
43
  "rack.run_once".freeze => false,
44
+ RACK_URL_SCHEME => conf.options[:rack_url_scheme],
44
45
  "SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
45
46
 
46
47
  # I'd like to set a default CONTENT_TYPE here but some things
@@ -95,6 +96,7 @@ module Puma
95
96
  # @version 5.0.0
96
97
  #
97
98
  def create_activated_fds(env_hash)
99
+ @events.debug "ENV['LISTEN_FDS'] #{ENV['LISTEN_FDS'].inspect} env_hash['LISTEN_PID'] #{env_hash['LISTEN_PID'].inspect}"
98
100
  return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
99
101
  env_hash['LISTEN_FDS'].to_i.times do |index|
100
102
  sock = TCPServer.for_fd(socket_activation_fd(index))
@@ -163,7 +165,7 @@ module Puma
163
165
  ios_len = @ios.length
164
166
  params = Util.parse_query uri.query
165
167
 
166
- opt = params.key?('low_latency')
168
+ opt = params.key?('low_latency') && params['low_latency'] != 'false'
167
169
  bak = params.fetch('backlog', 1024).to_i
168
170
 
169
171
  io = add_tcp_listener uri.host, uri.port, opt, bak
@@ -188,7 +190,8 @@ module Puma
188
190
  @unix_paths << path unless abstract
189
191
  io = inherit_unix_listener path, fd
190
192
  logger.log "* Inherited #{str}"
191
- elsif sock = @activated_sockets.delete([ :unix, path ])
193
+ elsif sock = @activated_sockets.delete([ :unix, path ]) ||
194
+ @activated_sockets.delete([ :unix, File.realdirpath(path) ])
192
195
  @unix_paths << path unless abstract || File.exist?(path)
193
196
  io = inherit_unix_listener path, sock
194
197
  logger.log "* Activated #{str}"
data/lib/puma/client.rb CHANGED
@@ -69,6 +69,7 @@ module Puma
69
69
  @hijacked = false
70
70
 
71
71
  @peerip = nil
72
+ @listener = nil
72
73
  @remote_addr_header = nil
73
74
 
74
75
  @body_remain = 0
@@ -81,7 +82,7 @@ module Puma
81
82
 
82
83
  attr_writer :peerip
83
84
 
84
- attr_accessor :remote_addr_header
85
+ attr_accessor :remote_addr_header, :listener
85
86
 
86
87
  def_delegators :@io, :closed?
87
88
 
@@ -142,8 +143,7 @@ module Puma
142
143
  return false
143
144
  else
144
145
  begin
145
- if fast_check &&
146
- IO.select([@to_io], nil, nil, FAST_TRACK_KA_TIMEOUT)
146
+ if fast_check && @to_io.wait_readable(FAST_TRACK_KA_TIMEOUT)
147
147
  return try_to_finish
148
148
  end
149
149
  rescue IOError
@@ -201,13 +201,13 @@ module Puma
201
201
 
202
202
  def eagerly_finish
203
203
  return true if @ready
204
- return false unless IO.select([@to_io], nil, nil, 0)
204
+ return false unless @to_io.wait_readable(0)
205
205
  try_to_finish
206
206
  end
207
207
 
208
208
  def finish(timeout)
209
209
  return if @ready
210
- IO.select([@to_io], nil, nil, timeout) || timeout! until try_to_finish
210
+ @to_io.wait_readable(timeout) || timeout! until try_to_finish
211
211
  end
212
212
 
213
213
  def timeout!
@@ -308,7 +308,7 @@ module Puma
308
308
 
309
309
  @body_remain = remain
310
310
 
311
- return false
311
+ false
312
312
  end
313
313
 
314
314
  def read_body
@@ -33,9 +33,9 @@ module Puma
33
33
  Signal.trap "SIGINT", "IGNORE"
34
34
  Signal.trap "SIGCHLD", "DEFAULT"
35
35
 
36
- Thread.new do
36
+ Thread.new do
37
37
  Puma.set_thread_name "worker check pipe"
38
- IO.select [@check_pipe]
38
+ @check_pipe.wait_readable
39
39
  log "! Detected parent died, dying"
40
40
  exit! 1
41
41
  end
@@ -54,7 +54,14 @@ module Puma
54
54
  # things in shape before booting the app.
55
55
  @launcher.config.run_hooks :before_worker_boot, index, @launcher.events
56
56
 
57
+ begin
57
58
  server = @server ||= start_server
59
+ rescue Exception => e
60
+ log "! Unable to start worker"
61
+ log e.backtrace[0]
62
+ exit 1
63
+ end
64
+
58
65
  restart_server = Queue.new << true << false
59
66
 
60
67
  fork_worker = @options[:fork_worker] && index == 0
data/lib/puma/cluster.rb CHANGED
@@ -426,9 +426,7 @@ module Puma
426
426
 
427
427
  check_workers
428
428
 
429
- res = IO.select([read], nil, nil, [0, @next_check - Time.now].max)
430
-
431
- if res
429
+ if read.wait_readable([0, @next_check - Time.now].max)
432
430
  req = read.read_nonblock(1)
433
431
 
434
432
  @next_check = Time.now if req == "!"
@@ -343,6 +343,8 @@ module Puma
343
343
  raise "Missing rackup file '#{rackup}'" unless File.exist?(rackup)
344
344
 
345
345
  rack_app, rack_options = rack_builder.parse_file(rackup)
346
+ rack_options = rack_options || {}
347
+
346
348
  @options.file_options.merge!(rack_options)
347
349
 
348
350
  config_ru_binds = []
data/lib/puma/const.rb CHANGED
@@ -100,8 +100,8 @@ module Puma
100
100
  # too taxing on performance.
101
101
  module Const
102
102
 
103
- PUMA_VERSION = VERSION = "5.3.1".freeze
104
- CODE_NAME = "Sweetnighter".freeze
103
+ PUMA_VERSION = VERSION = "5.4.0".freeze
104
+ CODE_NAME = "Super Flight".freeze
105
105
 
106
106
  PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
107
107
 
data/lib/puma/dsl.rb CHANGED
@@ -201,7 +201,7 @@ module Puma
201
201
  # * Set the socket backlog depth with +backlog+, default is 1024.
202
202
  # * Set up an SSL certificate with +key+ & +cert+.
203
203
  # * Set whether to optimize for low latency instead of throughput with
204
- # +low_latency+, default is to optimize for low latency. This is done
204
+ # +low_latency+, default is to not optimize for low latency. This is done
205
205
  # via +Socket::TCP_NODELAY+.
206
206
  # * Set socket permissions with +umask+.
207
207
  #
@@ -381,6 +381,13 @@ module Puma
381
381
  @options[:rackup] ||= path.to_s
382
382
  end
383
383
 
384
+ # Allows setting `env['rack.url_scheme']`.
385
+ # Only necessary if X-Forwarded-Proto is not being set by your proxy
386
+ # Normal values are 'http' or 'https'.
387
+ def rack_url_scheme(scheme=nil)
388
+ @options[:rack_url_scheme] = scheme
389
+ end
390
+
384
391
  def early_hints(answer=true)
385
392
  @options[:early_hints] = answer
386
393
  end
@@ -17,7 +17,7 @@ module Puma
17
17
  # be particularly full-featured or fast. It just has to handle the few places
18
18
  # where Puma relies on JSON serialization internally.
19
19
 
20
- module JSON
20
+ module JSONSerialization
21
21
  QUOTE = /"/
22
22
  BACKSLASH = /\\/
23
23
  CONTROL_CHAR_TO_ESCAPE = /[\x00-\x1F]/ # As required by ECMA-404
data/lib/puma/minissl.rb CHANGED
@@ -162,7 +162,7 @@ module Puma
162
162
  end
163
163
 
164
164
  def read_and_drop(timeout = 1)
165
- return :timeout unless IO.select([@socket], nil, nil, timeout)
165
+ return :timeout unless @socket.wait_readable(timeout)
166
166
  case @socket.read_nonblock(1024, exception: false)
167
167
  when nil
168
168
  :eof
data/lib/puma/plugin.rb CHANGED
@@ -91,7 +91,7 @@ module Puma
91
91
  path = ary.first[CALLER_FILE]
92
92
 
93
93
  m = %r!puma/plugin/([^/]*)\.rb$!.match(path)
94
- return m[1]
94
+ m[1]
95
95
  end
96
96
 
97
97
  def self.create(&blk)
@@ -165,7 +165,7 @@ module Puma::Rack
165
165
  require config
166
166
  app = Object.const_get(::File.basename(config, '.rb').capitalize)
167
167
  end
168
- return app, options
168
+ [app, options]
169
169
  end
170
170
 
171
171
  def self.new_from_string(builder_script, file="(rackup)")
data/lib/puma/request.rb CHANGED
@@ -26,9 +26,10 @@ module Puma
26
26
  # Finally, it'll return +true+ on keep-alive connections.
27
27
  # @param client [Puma::Client]
28
28
  # @param lines [Puma::IOBuffer]
29
+ # @param requests [Integer]
29
30
  # @return [Boolean,:async]
30
31
  #
31
- def handle_request(client, lines)
32
+ def handle_request(client, lines, requests)
32
33
  env = client.env
33
34
  io = client.io # io may be a MiniSSL::Socket
34
35
 
@@ -50,7 +51,7 @@ module Puma
50
51
  head = env[REQUEST_METHOD] == HEAD
51
52
 
52
53
  env[RACK_INPUT] = body
53
- env[RACK_URL_SCHEME] = default_server_port(env) == PORT_443 ? HTTPS : HTTP
54
+ env[RACK_URL_SCHEME] ||= default_server_port(env) == PORT_443 ? HTTPS : HTTP
54
55
 
55
56
  if @early_hints
56
57
  env[EARLY_HINTS] = lambda { |headers|
@@ -110,7 +111,7 @@ module Puma
110
111
 
111
112
  cork_socket io
112
113
 
113
- str_headers(env, status, headers, res_info, lines)
114
+ str_headers(env, status, headers, res_info, lines, requests, client)
114
115
 
115
116
  line_ending = LINE_END
116
117
 
@@ -175,7 +176,7 @@ module Puma
175
176
  after_reply.each { |o| o.call }
176
177
  end
177
178
 
178
- return res_info[:keep_alive]
179
+ res_info[:keep_alive]
179
180
  end
180
181
 
181
182
  # @param env [Hash] see Puma::Client#env, from request
@@ -200,7 +201,7 @@ module Puma
200
201
  begin
201
202
  n = io.syswrite str
202
203
  rescue Errno::EAGAIN, Errno::EWOULDBLOCK
203
- if !IO.select(nil, [io], nil, WRITE_TIMEOUT)
204
+ unless io.wait_writable WRITE_TIMEOUT
204
205
  raise ConnectionError, "Socket timeout writing data"
205
206
  end
206
207
 
@@ -367,9 +368,11 @@ module Puma
367
368
  # @param headers [Hash] the headers returned by the Rack application
368
369
  # @param res_info [Hash] used to pass info between this method and #handle_request
369
370
  # @param lines [Puma::IOBuffer] modified inn place
371
+ # @param requests [Integer] number of inline requests handled
372
+ # @param client [Puma::Client]
370
373
  # @version 5.0.3
371
374
  #
372
- def str_headers(env, status, headers, res_info, lines)
375
+ def str_headers(env, status, headers, res_info, lines, requests, client)
373
376
  line_ending = LINE_END
374
377
  colon = COLON
375
378
 
@@ -410,6 +413,14 @@ module Puma
410
413
  # if running without request queueing
411
414
  res_info[:keep_alive] &&= @queue_requests
412
415
 
416
+ # Close the connection after a reasonable number of inline requests
417
+ # if the server is at capacity and the listener has a new connection ready.
418
+ # This allows Puma to service connections fairly when the number
419
+ # of concurrent connections exceeds the size of the threadpool.
420
+ res_info[:keep_alive] &&= requests < @max_fast_inline ||
421
+ @thread_pool.busy_threads < @max_threads ||
422
+ !client.listener.to_io.wait_readable(0)
423
+
413
424
  res_info[:response_hijack] = nil
414
425
 
415
426
  headers.each do |k, vs|
data/lib/puma/server.rb CHANGED
@@ -14,6 +14,7 @@ require 'puma/io_buffer'
14
14
  require 'puma/request'
15
15
 
16
16
  require 'socket'
17
+ require 'io/wait'
17
18
  require 'forwardable'
18
19
 
19
20
  module Puma
@@ -227,6 +228,7 @@ module Puma
227
228
  @status = :run
228
229
 
229
230
  @thread_pool = ThreadPool.new(
231
+ thread_name,
230
232
  @min_threads,
231
233
  @max_threads,
232
234
  ::Puma::IOBuffer,
@@ -341,6 +343,7 @@ module Puma
341
343
  end
342
344
  drain += 1 if shutting_down?
343
345
  client = Client.new io, @binder.env(sock)
346
+ client.listener = sock
344
347
  if remote_addr_value
345
348
  client.peerip = remote_addr_value
346
349
  elsif remote_addr_header
@@ -395,7 +398,7 @@ module Puma
395
398
  return true
396
399
  end
397
400
 
398
- return false
401
+ false
399
402
  end
400
403
 
401
404
  # Given a connection on +client+, handle the incoming requests,
@@ -434,7 +437,7 @@ module Puma
434
437
 
435
438
  while true
436
439
  @requests_count += 1
437
- case handle_request(client, buffer)
440
+ case handle_request(client, buffer, requests + 1)
438
441
  when false
439
442
  break
440
443
  when :async
@@ -447,23 +450,17 @@ module Puma
447
450
 
448
451
  requests += 1
449
452
 
450
- # Closing keepalive sockets after they've made a reasonable
451
- # number of requests allows Puma to service many connections
452
- # fairly, even when the number of concurrent connections exceeds
453
- # the size of the threadpool. It also allows cluster mode Pumas
454
- # to keep load evenly distributed across workers, because clients
455
- # are randomly assigned a new worker when opening a new connection.
456
- #
457
- # Previously, Puma would kick connections in this conditional back
458
- # to the reactor. However, because this causes the todo set to increase
459
- # in size, the wait_until_full mutex would never unlock, leaving
460
- # any additional connections unserviced.
461
- break if requests >= @max_fast_inline
462
-
463
- check_for_more_data = @status == :run
453
+ # As an optimization, try to read the next request from the
454
+ # socket for a short time before returning to the reactor.
455
+ fast_check = @status == :run
456
+
457
+ # Always pass the client back to the reactor after a reasonable
458
+ # number of inline requests if there are other requests pending.
459
+ fast_check = false if requests >= @max_fast_inline &&
460
+ @thread_pool.backlog > 0
464
461
 
465
462
  next_request_ready = with_force_shutdown(client) do
466
- client.reset(check_for_more_data)
463
+ client.reset(fast_check)
467
464
  end
468
465
 
469
466
  unless next_request_ready
@@ -29,7 +29,7 @@ module Puma
29
29
  # The block passed is the work that will be performed in each
30
30
  # thread.
31
31
  #
32
- def initialize(min, max, *extra, &block)
32
+ def initialize(name, min, max, *extra, &block)
33
33
  @not_empty = ConditionVariable.new
34
34
  @not_full = ConditionVariable.new
35
35
  @mutex = Mutex.new
@@ -39,6 +39,7 @@ module Puma
39
39
  @spawned = 0
40
40
  @waiting = 0
41
41
 
42
+ @name = name
42
43
  @min = Integer(min)
43
44
  @max = Integer(max)
44
45
  @block = block
@@ -101,7 +102,7 @@ module Puma
101
102
  @spawned += 1
102
103
 
103
104
  th = Thread.new(@spawned) do |spawned|
104
- Puma.set_thread_name 'threadpool %03i' % spawned
105
+ Puma.set_thread_name '%s threadpool %03i' % [@name, spawned]
105
106
  todo = @todo
106
107
  block = @block
107
108
  mutex = @mutex
@@ -119,6 +120,7 @@ module Puma
119
120
  @trim_requested -= 1
120
121
  @spawned -= 1
121
122
  @workers.delete th
123
+ not_full.signal
122
124
  Thread.exit
123
125
  end
124
126
 
@@ -318,12 +320,12 @@ module Puma
318
320
  end
319
321
 
320
322
  def auto_trim!(timeout=30)
321
- @auto_trim = Automaton.new(self, timeout, "threadpool trimmer", :trim)
323
+ @auto_trim = Automaton.new(self, timeout, "#{@name} threadpool trimmer", :trim)
322
324
  @auto_trim.start!
323
325
  end
324
326
 
325
327
  def auto_reap!(timeout=5)
326
- @reaper = Automaton.new(self, timeout, "threadpool reaper", :reap)
328
+ @reaper = Automaton.new(self, timeout, "#{@name} threadpool reaper", :reap)
327
329
  @reaper.start!
328
330
  end
329
331
 
data/lib/puma/util.rb CHANGED
@@ -61,7 +61,7 @@ module Puma
61
61
  end
62
62
  end
63
63
 
64
- return params
64
+ params
65
65
  end
66
66
 
67
67
  # A case-insensitive Hash that preserves the original case of a
data/lib/puma.rb CHANGED
@@ -12,7 +12,7 @@ require 'thread'
12
12
 
13
13
  require 'puma/puma_http11'
14
14
  require 'puma/detect'
15
- require 'puma/json'
15
+ require 'puma/json_serialization'
16
16
 
17
17
  module Puma
18
18
  autoload :Const, 'puma/const'
@@ -60,7 +60,7 @@ module Puma
60
60
 
61
61
  # @!attribute [rw] stats_object
62
62
  def self.stats
63
- Puma::JSON.generate @get_stats.stats
63
+ Puma::JSONSerialization.generate @get_stats.stats
64
64
  end
65
65
 
66
66
  # @!attribute [r] stats_hash
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.3.1
4
+ version: 5.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Phoenix
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-11 00:00:00.000000000 Z
11
+ date: 2021-07-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nio4r
@@ -94,7 +94,7 @@ files:
94
94
  - lib/puma/events.rb
95
95
  - lib/puma/io_buffer.rb
96
96
  - lib/puma/jruby_restart.rb
97
- - lib/puma/json.rb
97
+ - lib/puma/json_serialization.rb
98
98
  - lib/puma/launcher.rb
99
99
  - lib/puma/minissl.rb
100
100
  - lib/puma/minissl/context_builder.rb