puma 5.3.2 → 5.6.8
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.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/History.md +211 -11
- data/README.md +47 -6
- data/docs/architecture.md +49 -16
- data/docs/compile_options.md +4 -2
- data/docs/deployment.md +53 -67
- data/docs/plugins.md +15 -15
- data/docs/rails_dev_mode.md +2 -3
- data/docs/restart.md +6 -6
- data/docs/signals.md +11 -10
- data/docs/stats.md +8 -8
- data/docs/systemd.md +64 -67
- data/ext/puma_http11/extconf.rb +34 -6
- data/ext/puma_http11/http11_parser.c +23 -10
- data/ext/puma_http11/http11_parser_common.rl +1 -1
- data/ext/puma_http11/mini_ssl.c +90 -12
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +49 -47
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +38 -55
- data/ext/puma_http11/puma_http11.c +1 -1
- data/lib/puma/app/status.rb +7 -4
- data/lib/puma/binder.rb +51 -6
- data/lib/puma/cli.rb +14 -4
- data/lib/puma/client.rb +143 -25
- data/lib/puma/cluster/worker.rb +8 -18
- data/lib/puma/cluster/worker_handle.rb +4 -0
- data/lib/puma/cluster.rb +30 -24
- data/lib/puma/configuration.rb +4 -1
- data/lib/puma/const.rb +9 -8
- data/lib/puma/control_cli.rb +19 -13
- data/lib/puma/detect.rb +8 -2
- data/lib/puma/dsl.rb +111 -13
- data/lib/puma/{json.rb → json_serialization.rb} +1 -1
- data/lib/puma/launcher.rb +15 -1
- data/lib/puma/minissl/context_builder.rb +8 -6
- data/lib/puma/minissl.rb +33 -27
- data/lib/puma/null_io.rb +5 -0
- data/lib/puma/plugin.rb +2 -2
- data/lib/puma/rack/builder.rb +1 -1
- data/lib/puma/request.rb +19 -10
- data/lib/puma/runner.rb +22 -8
- data/lib/puma/server.rb +37 -29
- data/lib/puma/state_file.rb +42 -7
- data/lib/puma/thread_pool.rb +7 -5
- data/lib/puma/util.rb +20 -4
- data/lib/puma.rb +6 -4
- data/lib/rack/version_restriction.rb +15 -0
- data/tools/Dockerfile +1 -1
- metadata +8 -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
|
@@ -38,6 +39,7 @@ module Puma
|
|
38
39
|
attr_reader :events
|
39
40
|
attr_reader :min_threads, :max_threads # for #stats
|
40
41
|
attr_reader :requests_count # @version 5.0.0
|
42
|
+
attr_reader :log_writer # to help with backports
|
41
43
|
|
42
44
|
# @todo the following may be deprecated in the future
|
43
45
|
attr_reader :auto_trim_time, :early_hints, :first_data_timeout,
|
@@ -72,6 +74,7 @@ module Puma
|
|
72
74
|
def initialize(app, events=Events.stdio, options={})
|
73
75
|
@app = app
|
74
76
|
@events = events
|
77
|
+
@log_writer = events
|
75
78
|
|
76
79
|
@check, @notify = nil
|
77
80
|
@status = :stop
|
@@ -145,7 +148,7 @@ module Puma
|
|
145
148
|
begin
|
146
149
|
skt.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 1) if skt.kind_of? TCPSocket
|
147
150
|
rescue IOError, SystemCallError
|
148
|
-
|
151
|
+
Puma::Util.purge_interrupt_queue
|
149
152
|
end
|
150
153
|
end
|
151
154
|
|
@@ -154,7 +157,7 @@ module Puma
|
|
154
157
|
begin
|
155
158
|
skt.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 0) if skt.kind_of? TCPSocket
|
156
159
|
rescue IOError, SystemCallError
|
157
|
-
|
160
|
+
Puma::Util.purge_interrupt_queue
|
158
161
|
end
|
159
162
|
end
|
160
163
|
else
|
@@ -175,7 +178,7 @@ module Puma
|
|
175
178
|
begin
|
176
179
|
tcp_info = skt.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_INFO)
|
177
180
|
rescue IOError, SystemCallError
|
178
|
-
|
181
|
+
Puma::Util.purge_interrupt_queue
|
179
182
|
@precheck_closing = false
|
180
183
|
false
|
181
184
|
else
|
@@ -219,7 +222,7 @@ module Puma
|
|
219
222
|
# up in the background to handle requests. Otherwise requests
|
220
223
|
# are handled synchronously.
|
221
224
|
#
|
222
|
-
def run(background=true, thread_name: '
|
225
|
+
def run(background=true, thread_name: 'srv')
|
223
226
|
BasicSocket.do_not_reverse_lookup = true
|
224
227
|
|
225
228
|
@events.fire :state, :booting
|
@@ -227,6 +230,7 @@ module Puma
|
|
227
230
|
@status = :run
|
228
231
|
|
229
232
|
@thread_pool = ThreadPool.new(
|
233
|
+
thread_name,
|
230
234
|
@min_threads,
|
231
235
|
@max_threads,
|
232
236
|
::Puma::IOBuffer,
|
@@ -313,14 +317,15 @@ module Puma
|
|
313
317
|
queue_requests = @queue_requests
|
314
318
|
drain = @options[:drain_on_shutdown] ? 0 : nil
|
315
319
|
|
316
|
-
|
317
|
-
remote_addr_header = nil
|
318
|
-
|
319
|
-
case @options[:remote_address]
|
320
|
+
addr_send_name, addr_value = case @options[:remote_address]
|
320
321
|
when :value
|
321
|
-
|
322
|
+
[:peerip=, @options[:remote_address_value]]
|
322
323
|
when :header
|
323
|
-
remote_addr_header
|
324
|
+
[:remote_addr_header=, @options[:remote_address_header]]
|
325
|
+
when :proxy_protocol
|
326
|
+
[:expect_proxy_proto=, @options[:remote_address_proxy_protocol]]
|
327
|
+
else
|
328
|
+
[nil, nil]
|
324
329
|
end
|
325
330
|
|
326
331
|
while @status == :run || (drain && shutting_down?)
|
@@ -340,17 +345,16 @@ module Puma
|
|
340
345
|
next
|
341
346
|
end
|
342
347
|
drain += 1 if shutting_down?
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
elsif remote_addr_header
|
348
|
-
client.remote_addr_header = remote_addr_header
|
349
|
-
end
|
350
|
-
pool << client
|
348
|
+
pool << Client.new(io, @binder.env(sock)).tap { |c|
|
349
|
+
c.listener = sock
|
350
|
+
c.send(addr_send_name, addr_value) if addr_value
|
351
|
+
}
|
351
352
|
end
|
352
353
|
end
|
353
|
-
rescue
|
354
|
+
rescue IOError, Errno::EBADF
|
355
|
+
# In the case that any of the sockets are unexpectedly close.
|
356
|
+
raise
|
357
|
+
rescue StandardError => e
|
354
358
|
@events.unknown_error e, nil, "Listen loop"
|
355
359
|
end
|
356
360
|
end
|
@@ -366,13 +370,14 @@ module Puma
|
|
366
370
|
rescue Exception => e
|
367
371
|
@events.unknown_error e, nil, "Exception handling servers"
|
368
372
|
ensure
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
373
|
+
# RuntimeError is Ruby 2.2 issue, can't modify frozen IOError
|
374
|
+
# Errno::EBADF is infrequently raised
|
375
|
+
[@check, @notify].each do |io|
|
376
|
+
begin
|
377
|
+
io.close unless io.closed?
|
378
|
+
rescue Errno::EBADF, RuntimeError
|
379
|
+
end
|
374
380
|
end
|
375
|
-
@notify.close
|
376
381
|
@notify = nil
|
377
382
|
@check = nil
|
378
383
|
end
|
@@ -396,7 +401,7 @@ module Puma
|
|
396
401
|
return true
|
397
402
|
end
|
398
403
|
|
399
|
-
|
404
|
+
false
|
400
405
|
end
|
401
406
|
|
402
407
|
# Given a connection on +client+, handle the incoming requests,
|
@@ -482,7 +487,7 @@ module Puma
|
|
482
487
|
begin
|
483
488
|
client.close if close_socket
|
484
489
|
rescue IOError, SystemCallError
|
485
|
-
|
490
|
+
Puma::Util.purge_interrupt_queue
|
486
491
|
# Already closed
|
487
492
|
rescue StandardError => e
|
488
493
|
@events.unknown_error e, nil, "Client"
|
@@ -512,6 +517,9 @@ module Puma
|
|
512
517
|
when HttpParserError
|
513
518
|
client.write_error(400)
|
514
519
|
@events.parse_error e, client
|
520
|
+
when HttpParserError501
|
521
|
+
client.write_error(501)
|
522
|
+
@events.parse_error e, client
|
515
523
|
else
|
516
524
|
client.write_error(500)
|
517
525
|
@events.unknown_error e, nil, "Read"
|
@@ -574,11 +582,11 @@ module Puma
|
|
574
582
|
@notify << message
|
575
583
|
rescue IOError, NoMethodError, Errno::EPIPE
|
576
584
|
# The server, in another thread, is shutting down
|
577
|
-
|
585
|
+
Puma::Util.purge_interrupt_queue
|
578
586
|
rescue RuntimeError => e
|
579
587
|
# Temporary workaround for https://bugs.ruby-lang.org/issues/13239
|
580
588
|
if e.message.include?('IOError')
|
581
|
-
|
589
|
+
Puma::Util.purge_interrupt_queue
|
582
590
|
else
|
583
591
|
raise e
|
584
592
|
end
|
data/lib/puma/state_file.rb
CHANGED
@@ -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 =
|
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,22 @@ module Puma
|
|
18
43
|
end
|
19
44
|
|
20
45
|
def load(path)
|
21
|
-
|
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 '' then nil
|
54
|
+
when /\A\d+\z/ then v.to_i
|
55
|
+
when /\A\d+\.\d+\z/ then v.to_f
|
56
|
+
else v.gsub(/\A"|"\z/, '')
|
57
|
+
end
|
58
|
+
end
|
22
59
|
end
|
23
60
|
|
24
|
-
|
25
|
-
|
26
|
-
FIELDS.each do |f|
|
61
|
+
ALLOWED_FIELDS.each do |f|
|
27
62
|
define_method f do
|
28
63
|
@options[f]
|
29
64
|
end
|
data/lib/puma/thread_pool.rb
CHANGED
@@ -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
|
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 '
|
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,18 +10,34 @@ module Puma
|
|
10
10
|
IO.pipe
|
11
11
|
end
|
12
12
|
|
13
|
-
#
|
14
|
-
#
|
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
|
+
|
20
|
+
# Escapes and unescapes a URI escaped string with
|
21
|
+
# +encoding+. +encoding+ will be the target encoding of the string
|
22
|
+
# returned, and it defaults to UTF-8
|
15
23
|
if defined?(::Encoding)
|
24
|
+
def escape(s, encoding = Encoding::UTF_8)
|
25
|
+
URI.encode_www_form_component(s, encoding)
|
26
|
+
end
|
27
|
+
|
16
28
|
def unescape(s, encoding = Encoding::UTF_8)
|
17
29
|
URI.decode_www_form_component(s, encoding)
|
18
30
|
end
|
19
31
|
else
|
32
|
+
def escape(s, encoding = nil)
|
33
|
+
URI.encode_www_form_component(s, encoding)
|
34
|
+
end
|
35
|
+
|
20
36
|
def unescape(s, encoding = nil)
|
21
37
|
URI.decode_www_form_component(s, encoding)
|
22
38
|
end
|
23
39
|
end
|
24
|
-
module_function :unescape
|
40
|
+
module_function :unescape, :escape
|
25
41
|
|
26
42
|
# @version 5.0.0
|
27
43
|
def nakayoshi_gc(events)
|
@@ -61,7 +77,7 @@ module Puma
|
|
61
77
|
end
|
62
78
|
end
|
63
79
|
|
64
|
-
|
80
|
+
params
|
65
81
|
end
|
66
82
|
|
67
83
|
# A case-insensitive Hash that preserves the original case of a
|
data/lib/puma.rb
CHANGED
@@ -10,9 +10,11 @@ require 'stringio'
|
|
10
10
|
|
11
11
|
require 'thread'
|
12
12
|
|
13
|
+
# extension files should not be loaded with `require_relative`
|
13
14
|
require 'puma/puma_http11'
|
14
|
-
|
15
|
-
|
15
|
+
require_relative 'puma/detect'
|
16
|
+
require_relative 'puma/json_serialization'
|
17
|
+
require_relative 'rack/version_restriction'
|
16
18
|
|
17
19
|
module Puma
|
18
20
|
autoload :Const, 'puma/const'
|
@@ -23,7 +25,7 @@ module Puma
|
|
23
25
|
# not in minissl.rb
|
24
26
|
HAS_SSL = const_defined?(:MiniSSL, false) && MiniSSL.const_defined?(:Engine, false)
|
25
27
|
|
26
|
-
HAS_UNIX_SOCKET = Object.const_defined?
|
28
|
+
HAS_UNIX_SOCKET = Object.const_defined?(:UNIXSocket) && !IS_WINDOWS
|
27
29
|
|
28
30
|
if HAS_SSL
|
29
31
|
require 'puma/minissl'
|
@@ -60,7 +62,7 @@ module Puma
|
|
60
62
|
|
61
63
|
# @!attribute [rw] stats_object
|
62
64
|
def self.stats
|
63
|
-
Puma::
|
65
|
+
Puma::JSONSerialization.generate @get_stats.stats
|
64
66
|
end
|
65
67
|
|
66
68
|
# @!attribute [r] stats_hash
|
@@ -0,0 +1,15 @@
|
|
1
|
+
begin
|
2
|
+
begin
|
3
|
+
# rack/version exists in Rack 2.2.0 and later, compatible with Ruby 2.3 and later
|
4
|
+
# we prefer to not load Rack
|
5
|
+
require 'rack/version'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rack'
|
8
|
+
end
|
9
|
+
|
10
|
+
# Rack.release is needed for Rack v1, Rack::RELEASE was added in v2
|
11
|
+
if Gem::Version.new(Rack.release) >= Gem::Version.new("3.0.0")
|
12
|
+
raise StandardError.new "Puma 5 is not compatible with Rack 3, please upgrade to Puma 6 or higher."
|
13
|
+
end
|
14
|
+
rescue LoadError
|
15
|
+
end
|
data/tools/Dockerfile
CHANGED
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.
|
4
|
+
version: 5.6.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evan Phoenix
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-08 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
|
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
|
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/
|
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
|
@@ -115,6 +115,7 @@ files:
|
|
115
115
|
- lib/puma/thread_pool.rb
|
116
116
|
- lib/puma/util.rb
|
117
117
|
- lib/rack/handler/puma.rb
|
118
|
+
- lib/rack/version_restriction.rb
|
118
119
|
- tools/Dockerfile
|
119
120
|
- tools/trickletest.rb
|
120
121
|
homepage: https://puma.io
|
@@ -140,9 +141,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
140
141
|
- !ruby/object:Gem::Version
|
141
142
|
version: '0'
|
142
143
|
requirements: []
|
143
|
-
rubygems_version: 3.
|
144
|
+
rubygems_version: 3.5.3
|
144
145
|
signing_key:
|
145
146
|
specification_version: 4
|
146
|
-
summary: Puma is a simple, fast, threaded, and highly
|
147
|
+
summary: Puma is a simple, fast, threaded, and highly parallel HTTP 1.1 server for
|
147
148
|
Ruby/Rack applications
|
148
149
|
test_files: []
|