puma 5.3.2 → 5.6.4

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +148 -8
  3. data/LICENSE +0 -0
  4. data/README.md +47 -6
  5. data/bin/puma-wild +0 -0
  6. data/docs/architecture.md +49 -16
  7. data/docs/compile_options.md +4 -2
  8. data/docs/deployment.md +53 -67
  9. data/docs/fork_worker.md +0 -0
  10. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  11. data/docs/images/puma-connection-flow.png +0 -0
  12. data/docs/images/puma-general-arch.png +0 -0
  13. data/docs/jungle/README.md +0 -0
  14. data/docs/jungle/rc.d/README.md +0 -0
  15. data/docs/jungle/rc.d/puma.conf +0 -0
  16. data/docs/kubernetes.md +0 -0
  17. data/docs/nginx.md +0 -0
  18. data/docs/plugins.md +15 -15
  19. data/docs/rails_dev_mode.md +2 -3
  20. data/docs/restart.md +6 -6
  21. data/docs/signals.md +11 -10
  22. data/docs/stats.md +8 -8
  23. data/docs/systemd.md +64 -67
  24. data/ext/puma_http11/PumaHttp11Service.java +0 -0
  25. data/ext/puma_http11/ext_help.h +0 -0
  26. data/ext/puma_http11/extconf.rb +28 -5
  27. data/ext/puma_http11/http11_parser.c +23 -10
  28. data/ext/puma_http11/http11_parser.h +0 -0
  29. data/ext/puma_http11/http11_parser.java.rl +0 -0
  30. data/ext/puma_http11/http11_parser.rl +0 -0
  31. data/ext/puma_http11/http11_parser_common.rl +1 -1
  32. data/ext/puma_http11/mini_ssl.c +69 -9
  33. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +0 -0
  34. data/ext/puma_http11/org/jruby/puma/Http11.java +0 -0
  35. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +49 -47
  36. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +28 -43
  37. data/ext/puma_http11/puma_http11.c +1 -1
  38. data/lib/puma/app/status.rb +4 -4
  39. data/lib/puma/binder.rb +50 -5
  40. data/lib/puma/cli.rb +14 -4
  41. data/lib/puma/client.rb +104 -20
  42. data/lib/puma/cluster/worker.rb +8 -18
  43. data/lib/puma/cluster/worker_handle.rb +4 -0
  44. data/lib/puma/cluster.rb +30 -24
  45. data/lib/puma/commonlogger.rb +0 -0
  46. data/lib/puma/configuration.rb +4 -1
  47. data/lib/puma/const.rb +9 -8
  48. data/lib/puma/control_cli.rb +1 -1
  49. data/lib/puma/detect.rb +8 -2
  50. data/lib/puma/dsl.rb +105 -11
  51. data/lib/puma/error_logger.rb +0 -0
  52. data/lib/puma/events.rb +0 -0
  53. data/lib/puma/io_buffer.rb +0 -0
  54. data/lib/puma/jruby_restart.rb +0 -0
  55. data/lib/puma/{json.rb → json_serialization.rb} +1 -1
  56. data/lib/puma/launcher.rb +4 -1
  57. data/lib/puma/minissl/context_builder.rb +8 -6
  58. data/lib/puma/minissl.rb +24 -23
  59. data/lib/puma/null_io.rb +0 -0
  60. data/lib/puma/plugin/tmp_restart.rb +0 -0
  61. data/lib/puma/plugin.rb +2 -2
  62. data/lib/puma/queue_close.rb +0 -0
  63. data/lib/puma/rack/builder.rb +1 -1
  64. data/lib/puma/rack/urlmap.rb +0 -0
  65. data/lib/puma/rack_default.rb +0 -0
  66. data/lib/puma/reactor.rb +0 -0
  67. data/lib/puma/request.rb +14 -9
  68. data/lib/puma/runner.rb +22 -8
  69. data/lib/puma/server.rb +35 -29
  70. data/lib/puma/single.rb +0 -0
  71. data/lib/puma/state_file.rb +41 -7
  72. data/lib/puma/systemd.rb +0 -0
  73. data/lib/puma/thread_pool.rb +7 -5
  74. data/lib/puma/util.rb +8 -1
  75. data/lib/puma.rb +2 -2
  76. data/lib/rack/handler/puma.rb +0 -0
  77. data/tools/Dockerfile +1 -1
  78. data/tools/trickletest.rb +0 -0
  79. metadata +7 -7
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
@@ -145,7 +146,7 @@ module Puma
145
146
  begin
146
147
  skt.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 1) if skt.kind_of? TCPSocket
147
148
  rescue IOError, SystemCallError
148
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
149
+ Puma::Util.purge_interrupt_queue
149
150
  end
150
151
  end
151
152
 
@@ -154,7 +155,7 @@ module Puma
154
155
  begin
155
156
  skt.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 0) if skt.kind_of? TCPSocket
156
157
  rescue IOError, SystemCallError
157
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
158
+ Puma::Util.purge_interrupt_queue
158
159
  end
159
160
  end
160
161
  else
@@ -175,7 +176,7 @@ module Puma
175
176
  begin
176
177
  tcp_info = skt.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_INFO)
177
178
  rescue IOError, SystemCallError
178
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
179
+ Puma::Util.purge_interrupt_queue
179
180
  @precheck_closing = false
180
181
  false
181
182
  else
@@ -219,7 +220,7 @@ module Puma
219
220
  # up in the background to handle requests. Otherwise requests
220
221
  # are handled synchronously.
221
222
  #
222
- def run(background=true, thread_name: 'server')
223
+ def run(background=true, thread_name: 'srv')
223
224
  BasicSocket.do_not_reverse_lookup = true
224
225
 
225
226
  @events.fire :state, :booting
@@ -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,
@@ -313,14 +315,15 @@ module Puma
313
315
  queue_requests = @queue_requests
314
316
  drain = @options[:drain_on_shutdown] ? 0 : nil
315
317
 
316
- remote_addr_value = nil
317
- remote_addr_header = nil
318
-
319
- case @options[:remote_address]
318
+ addr_send_name, addr_value = case @options[:remote_address]
320
319
  when :value
321
- remote_addr_value = @options[:remote_address_value]
320
+ [:peerip=, @options[:remote_address_value]]
322
321
  when :header
323
- remote_addr_header = @options[:remote_address_header]
322
+ [:remote_addr_header=, @options[:remote_address_header]]
323
+ when :proxy_protocol
324
+ [:expect_proxy_proto=, @options[:remote_address_proxy_protocol]]
325
+ else
326
+ [nil, nil]
324
327
  end
325
328
 
326
329
  while @status == :run || (drain && shutting_down?)
@@ -340,17 +343,16 @@ module Puma
340
343
  next
341
344
  end
342
345
  drain += 1 if shutting_down?
343
- client = Client.new io, @binder.env(sock)
344
- client.listener = sock
345
- if remote_addr_value
346
- client.peerip = remote_addr_value
347
- elsif remote_addr_header
348
- client.remote_addr_header = remote_addr_header
349
- end
350
- pool << client
346
+ pool << Client.new(io, @binder.env(sock)).tap { |c|
347
+ c.listener = sock
348
+ c.send(addr_send_name, addr_value) if addr_value
349
+ }
351
350
  end
352
351
  end
353
- rescue Object => e
352
+ rescue IOError, Errno::EBADF
353
+ # In the case that any of the sockets are unexpectedly close.
354
+ raise
355
+ rescue StandardError => e
354
356
  @events.unknown_error e, nil, "Listen loop"
355
357
  end
356
358
  end
@@ -366,13 +368,14 @@ module Puma
366
368
  rescue Exception => e
367
369
  @events.unknown_error e, nil, "Exception handling servers"
368
370
  ensure
369
- begin
370
- @check.close unless @check.closed?
371
- rescue Errno::EBADF, RuntimeError
372
- # RuntimeError is Ruby 2.2 issue, can't modify frozen IOError
373
- # Errno::EBADF is infrequently raised
371
+ # RuntimeError is Ruby 2.2 issue, can't modify frozen IOError
372
+ # Errno::EBADF is infrequently raised
373
+ [@check, @notify].each do |io|
374
+ begin
375
+ io.close unless io.closed?
376
+ rescue Errno::EBADF, RuntimeError
377
+ end
374
378
  end
375
- @notify.close
376
379
  @notify = nil
377
380
  @check = nil
378
381
  end
@@ -396,7 +399,7 @@ module Puma
396
399
  return true
397
400
  end
398
401
 
399
- return false
402
+ false
400
403
  end
401
404
 
402
405
  # Given a connection on +client+, handle the incoming requests,
@@ -482,7 +485,7 @@ module Puma
482
485
  begin
483
486
  client.close if close_socket
484
487
  rescue IOError, SystemCallError
485
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
488
+ Puma::Util.purge_interrupt_queue
486
489
  # Already closed
487
490
  rescue StandardError => e
488
491
  @events.unknown_error e, nil, "Client"
@@ -512,6 +515,9 @@ module Puma
512
515
  when HttpParserError
513
516
  client.write_error(400)
514
517
  @events.parse_error e, client
518
+ when HttpParserError501
519
+ client.write_error(501)
520
+ @events.parse_error e, client
515
521
  else
516
522
  client.write_error(500)
517
523
  @events.unknown_error e, nil, "Read"
@@ -574,11 +580,11 @@ module Puma
574
580
  @notify << message
575
581
  rescue IOError, NoMethodError, Errno::EPIPE
576
582
  # The server, in another thread, is shutting down
577
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
583
+ Puma::Util.purge_interrupt_queue
578
584
  rescue RuntimeError => e
579
585
  # Temporary workaround for https://bugs.ruby-lang.org/issues/13239
580
586
  if e.message.include?('IOError')
581
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
587
+ Puma::Util.purge_interrupt_queue
582
588
  else
583
589
  raise e
584
590
  end
data/lib/puma/single.rb CHANGED
File without changes
@@ -1,15 +1,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'yaml'
4
-
5
3
  module Puma
4
+
5
+ # Puma::Launcher uses StateFile to write a yaml file for use with Puma::ControlCLI.
6
+ #
7
+ # In previous versions of Puma, YAML was used to read/write the state file.
8
+ # Since Puma is similar to Bundler/RubyGems in that it may load before one's app
9
+ # does, minimizing the dependencies that may be shared with the app is desired.
10
+ #
11
+ # At present, it only works with numeric and string values. It is still a valid
12
+ # yaml file, and the CI tests parse it with Psych.
13
+ #
6
14
  class StateFile
15
+
16
+ ALLOWED_FIELDS = %w!control_url control_auth_token pid running_from!
17
+
18
+ # @deprecated 6.0.0
19
+ FIELDS = ALLOWED_FIELDS
20
+
7
21
  def initialize
8
22
  @options = {}
9
23
  end
10
24
 
11
25
  def save(path, permission = nil)
12
- contents =YAML.dump @options
26
+ contents = "---\n".dup
27
+ @options.each do |k,v|
28
+ next unless ALLOWED_FIELDS.include? k
29
+ case v
30
+ when Numeric
31
+ contents << "#{k}: #{v}\n"
32
+ when String
33
+ next if v.strip.empty?
34
+ contents << (k == 'running_from' || v.to_s.include?(' ') ?
35
+ "#{k}: \"#{v}\"\n" : "#{k}: #{v}\n")
36
+ end
37
+ end
13
38
  if permission
14
39
  File.write path, contents, mode: 'wb:UTF-8'
15
40
  else
@@ -18,12 +43,21 @@ module Puma
18
43
  end
19
44
 
20
45
  def load(path)
21
- @options = YAML.load File.read(path)
46
+ File.read(path).lines.each do |line|
47
+ next if line.start_with? '#'
48
+ k,v = line.split ':', 2
49
+ next unless v && ALLOWED_FIELDS.include?(k)
50
+ v = v.strip
51
+ @options[k] =
52
+ case v
53
+ when /\A\d+\z/ then v.to_i
54
+ when /\A\d+\.\d+\z/ then v.to_f
55
+ else v.gsub(/\A"|"\z/, '')
56
+ end
57
+ end
22
58
  end
23
59
 
24
- FIELDS = %w!control_url control_auth_token pid running_from!
25
-
26
- FIELDS.each do |f|
60
+ ALLOWED_FIELDS.each do |f|
27
61
  define_method f do
28
62
  @options[f]
29
63
  end
data/lib/puma/systemd.rb CHANGED
File without changes
@@ -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
@@ -71,7 +72,7 @@ module Puma
71
72
  attr_accessor :out_of_band_hook # @version 5.0.0
72
73
 
73
74
  def self.clean_thread_locals
74
- Thread.current.keys.each do |key| # rubocop: disable Performance/HashEachMethods
75
+ Thread.current.keys.each do |key| # rubocop: disable Style/HashEachMethods
75
76
  Thread.current[key] = nil unless key == :__recursive_key__
76
77
  end
77
78
  end
@@ -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 tp %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
@@ -10,6 +10,13 @@ module Puma
10
10
  IO.pipe
11
11
  end
12
12
 
13
+ # An instance method on Thread has been provided to address https://bugs.ruby-lang.org/issues/13632,
14
+ # which currently effects some older versions of Ruby: 2.2.7 2.2.8 2.2.9 2.2.10 2.3.4 2.4.1
15
+ # Additional context: https://github.com/puma/puma/pull/1345
16
+ def purge_interrupt_queue
17
+ Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
18
+ end
19
+
13
20
  # Unescapes a URI escaped string with +encoding+. +encoding+ will be the
14
21
  # target encoding of the string returned, and it defaults to UTF-8
15
22
  if defined?(::Encoding)
@@ -61,7 +68,7 @@ module Puma
61
68
  end
62
69
  end
63
70
 
64
- return params
71
+ params
65
72
  end
66
73
 
67
74
  # 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
File without changes
data/tools/Dockerfile CHANGED
@@ -1,6 +1,6 @@
1
1
  # Use this Dockerfile to create minimal reproductions of issues
2
2
 
3
- FROM ruby:2.6
3
+ FROM ruby:3.1
4
4
 
5
5
  # throw errors if Gemfile has been modified since Gemfile.lock
6
6
  RUN bundle config --global frozen 1
data/tools/trickletest.rb CHANGED
File without changes
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.2
4
+ version: 5.6.4
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-21 00:00:00.000000000 Z
11
+ date: 1980-01-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nio4r
@@ -24,9 +24,9 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.0'
27
- description: Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server
27
+ description: Puma is a simple, fast, threaded, and highly parallel HTTP 1.1 server
28
28
  for Ruby/Rack applications. Puma is intended for use in both development and production
29
- environments. It's great for highly concurrent Ruby implementations such as Rubinius
29
+ environments. It's great for highly parallel Ruby implementations such as Rubinius
30
30
  and JRuby as well as as providing process worker support to support CRuby well.
31
31
  email:
32
32
  - evan@phx.io
@@ -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
@@ -140,9 +140,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
140
140
  - !ruby/object:Gem::Version
141
141
  version: '0'
142
142
  requirements: []
143
- rubygems_version: 3.2.3
143
+ rubygems_version: 3.2.26
144
144
  signing_key:
145
145
  specification_version: 4
146
- summary: Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server for
146
+ summary: Puma is a simple, fast, threaded, and highly parallel HTTP 1.1 server for
147
147
  Ruby/Rack applications
148
148
  test_files: []