puma 5.3.1 → 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.
- checksums.yaml +4 -4
- data/History.md +152 -5
- data/LICENSE +0 -0
- data/README.md +47 -6
- data/bin/puma-wild +0 -0
- data/docs/architecture.md +49 -16
- data/docs/compile_options.md +4 -2
- data/docs/deployment.md +53 -67
- data/docs/fork_worker.md +0 -0
- data/docs/images/puma-connection-flow-no-reactor.png +0 -0
- data/docs/images/puma-connection-flow.png +0 -0
- data/docs/images/puma-general-arch.png +0 -0
- data/docs/jungle/README.md +0 -0
- data/docs/jungle/rc.d/README.md +0 -0
- data/docs/jungle/rc.d/puma.conf +0 -0
- data/docs/kubernetes.md +0 -0
- data/docs/nginx.md +0 -0
- 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/PumaHttp11Service.java +0 -0
- data/ext/puma_http11/ext_help.h +0 -0
- data/ext/puma_http11/extconf.rb +28 -5
- data/ext/puma_http11/http11_parser.c +23 -10
- data/ext/puma_http11/http11_parser.h +0 -0
- data/ext/puma_http11/http11_parser.java.rl +0 -0
- data/ext/puma_http11/http11_parser.rl +0 -0
- data/ext/puma_http11/http11_parser_common.rl +1 -1
- data/ext/puma_http11/mini_ssl.c +69 -9
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +0 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +0 -0
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +49 -47
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +28 -43
- data/ext/puma_http11/puma_http11.c +1 -1
- data/lib/puma/app/status.rb +4 -4
- data/lib/puma/binder.rb +51 -6
- data/lib/puma/cli.rb +14 -4
- data/lib/puma/client.rb +106 -21
- data/lib/puma/cluster/worker.rb +14 -17
- data/lib/puma/cluster/worker_handle.rb +4 -0
- data/lib/puma/cluster.rb +30 -24
- data/lib/puma/commonlogger.rb +0 -0
- data/lib/puma/configuration.rb +6 -1
- data/lib/puma/const.rb +9 -8
- data/lib/puma/control_cli.rb +1 -1
- data/lib/puma/detect.rb +8 -2
- data/lib/puma/dsl.rb +106 -12
- data/lib/puma/error_logger.rb +0 -0
- data/lib/puma/events.rb +0 -0
- data/lib/puma/io_buffer.rb +0 -0
- data/lib/puma/jruby_restart.rb +0 -0
- data/lib/puma/{json.rb → json_serialization.rb} +1 -1
- data/lib/puma/launcher.rb +4 -1
- data/lib/puma/minissl/context_builder.rb +8 -6
- data/lib/puma/minissl.rb +24 -23
- data/lib/puma/null_io.rb +0 -0
- data/lib/puma/plugin/tmp_restart.rb +0 -0
- data/lib/puma/plugin.rb +2 -2
- data/lib/puma/queue_close.rb +0 -0
- data/lib/puma/rack/builder.rb +1 -1
- data/lib/puma/rack/urlmap.rb +0 -0
- data/lib/puma/rack_default.rb +0 -0
- data/lib/puma/reactor.rb +0 -0
- data/lib/puma/request.rb +27 -11
- data/lib/puma/runner.rb +22 -8
- data/lib/puma/server.rb +45 -44
- data/lib/puma/single.rb +0 -0
- data/lib/puma/state_file.rb +41 -7
- data/lib/puma/systemd.rb +0 -0
- data/lib/puma/thread_pool.rb +7 -5
- data/lib/puma/util.rb +8 -1
- data/lib/puma.rb +2 -2
- data/lib/rack/handler/puma.rb +0 -0
- data/tools/Dockerfile +1 -1
- data/tools/trickletest.rb +0 -0
- 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
|
-
|
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
|
-
|
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
|
-
|
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: '
|
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
|
-
|
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
|
-
|
320
|
+
[:peerip=, @options[:remote_address_value]]
|
322
321
|
when :header
|
323
|
-
remote_addr_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,16 +343,16 @@ module Puma
|
|
340
343
|
next
|
341
344
|
end
|
342
345
|
drain += 1 if shutting_down?
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
client.remote_addr_header = remote_addr_header
|
348
|
-
end
|
349
|
-
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
|
+
}
|
350
350
|
end
|
351
351
|
end
|
352
|
-
rescue
|
352
|
+
rescue IOError, Errno::EBADF
|
353
|
+
# In the case that any of the sockets are unexpectedly close.
|
354
|
+
raise
|
355
|
+
rescue StandardError => e
|
353
356
|
@events.unknown_error e, nil, "Listen loop"
|
354
357
|
end
|
355
358
|
end
|
@@ -365,13 +368,14 @@ module Puma
|
|
365
368
|
rescue Exception => e
|
366
369
|
@events.unknown_error e, nil, "Exception handling servers"
|
367
370
|
ensure
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
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
|
373
378
|
end
|
374
|
-
@notify.close
|
375
379
|
@notify = nil
|
376
380
|
@check = nil
|
377
381
|
end
|
@@ -395,7 +399,7 @@ module Puma
|
|
395
399
|
return true
|
396
400
|
end
|
397
401
|
|
398
|
-
|
402
|
+
false
|
399
403
|
end
|
400
404
|
|
401
405
|
# Given a connection on +client+, handle the incoming requests,
|
@@ -434,7 +438,7 @@ module Puma
|
|
434
438
|
|
435
439
|
while true
|
436
440
|
@requests_count += 1
|
437
|
-
case handle_request(client, buffer)
|
441
|
+
case handle_request(client, buffer, requests + 1)
|
438
442
|
when false
|
439
443
|
break
|
440
444
|
when :async
|
@@ -447,23 +451,17 @@ module Puma
|
|
447
451
|
|
448
452
|
requests += 1
|
449
453
|
|
450
|
-
#
|
451
|
-
#
|
452
|
-
|
453
|
-
|
454
|
-
#
|
455
|
-
#
|
456
|
-
|
457
|
-
|
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
|
454
|
+
# As an optimization, try to read the next request from the
|
455
|
+
# socket for a short time before returning to the reactor.
|
456
|
+
fast_check = @status == :run
|
457
|
+
|
458
|
+
# Always pass the client back to the reactor after a reasonable
|
459
|
+
# number of inline requests if there are other requests pending.
|
460
|
+
fast_check = false if requests >= @max_fast_inline &&
|
461
|
+
@thread_pool.backlog > 0
|
464
462
|
|
465
463
|
next_request_ready = with_force_shutdown(client) do
|
466
|
-
client.reset(
|
464
|
+
client.reset(fast_check)
|
467
465
|
end
|
468
466
|
|
469
467
|
unless next_request_ready
|
@@ -487,7 +485,7 @@ module Puma
|
|
487
485
|
begin
|
488
486
|
client.close if close_socket
|
489
487
|
rescue IOError, SystemCallError
|
490
|
-
|
488
|
+
Puma::Util.purge_interrupt_queue
|
491
489
|
# Already closed
|
492
490
|
rescue StandardError => e
|
493
491
|
@events.unknown_error e, nil, "Client"
|
@@ -517,6 +515,9 @@ module Puma
|
|
517
515
|
when HttpParserError
|
518
516
|
client.write_error(400)
|
519
517
|
@events.parse_error e, client
|
518
|
+
when HttpParserError501
|
519
|
+
client.write_error(501)
|
520
|
+
@events.parse_error e, client
|
520
521
|
else
|
521
522
|
client.write_error(500)
|
522
523
|
@events.unknown_error e, nil, "Read"
|
@@ -579,11 +580,11 @@ module Puma
|
|
579
580
|
@notify << message
|
580
581
|
rescue IOError, NoMethodError, Errno::EPIPE
|
581
582
|
# The server, in another thread, is shutting down
|
582
|
-
|
583
|
+
Puma::Util.purge_interrupt_queue
|
583
584
|
rescue RuntimeError => e
|
584
585
|
# Temporary workaround for https://bugs.ruby-lang.org/issues/13239
|
585
586
|
if e.message.include?('IOError')
|
586
|
-
|
587
|
+
Puma::Util.purge_interrupt_queue
|
587
588
|
else
|
588
589
|
raise e
|
589
590
|
end
|
data/lib/puma/single.rb
CHANGED
File without changes
|
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,21 @@ 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 /\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
|
-
|
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
|
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,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
|
-
|
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/
|
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::
|
63
|
+
Puma::JSONSerialization.generate @get_stats.stats
|
64
64
|
end
|
65
65
|
|
66
66
|
# @!attribute [r] stats_hash
|
data/lib/rack/handler/puma.rb
CHANGED
File without changes
|
data/tools/Dockerfile
CHANGED
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.
|
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:
|
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
|
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
|
@@ -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.
|
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
|
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: []
|