puma 6.3.1 → 6.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.md +114 -9
- data/README.md +19 -9
- data/docs/kubernetes.md +12 -0
- data/docs/restart.md +1 -0
- data/docs/systemd.md +2 -4
- data/ext/puma_http11/extconf.rb +5 -1
- data/ext/puma_http11/mini_ssl.c +66 -7
- data/ext/puma_http11/org/jruby/puma/Http11.java +2 -0
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +2 -1
- data/lib/puma/binder.rb +2 -2
- data/lib/puma/cli.rb +4 -0
- data/lib/puma/client.rb +31 -3
- data/lib/puma/cluster.rb +69 -10
- data/lib/puma/configuration.rb +4 -4
- data/lib/puma/const.rb +10 -2
- data/lib/puma/control_cli.rb +12 -5
- data/lib/puma/detect.rb +3 -4
- data/lib/puma/dsl.rb +66 -6
- data/lib/puma/minissl/context_builder.rb +2 -0
- data/lib/puma/minissl.rb +5 -0
- data/lib/puma/null_io.rb +16 -2
- data/lib/puma/rack/urlmap.rb +1 -1
- data/lib/puma/request.rb +16 -3
- data/lib/puma/runner.rb +6 -2
- data/lib/puma/server.rb +73 -23
- data/lib/puma/state_file.rb +2 -2
- data/lib/puma/thread_pool.rb +34 -0
- data/tools/Dockerfile +2 -2
- metadata +3 -3
data/lib/puma/server.rb
CHANGED
@@ -15,7 +15,6 @@ require_relative 'request'
|
|
15
15
|
|
16
16
|
require 'socket'
|
17
17
|
require 'io/wait' unless Puma::HAS_NATIVE_IO_WAIT
|
18
|
-
require 'forwardable'
|
19
18
|
|
20
19
|
module Puma
|
21
20
|
|
@@ -32,7 +31,6 @@ module Puma
|
|
32
31
|
class Server
|
33
32
|
include Puma::Const
|
34
33
|
include Request
|
35
|
-
extend Forwardable
|
36
34
|
|
37
35
|
attr_reader :thread
|
38
36
|
attr_reader :log_writer
|
@@ -48,9 +46,6 @@ module Puma
|
|
48
46
|
attr_accessor :app
|
49
47
|
attr_accessor :binder
|
50
48
|
|
51
|
-
def_delegators :@binder, :add_tcp_listener, :add_ssl_listener,
|
52
|
-
:add_unix_listener, :connected_ports
|
53
|
-
|
54
49
|
THREAD_LOCAL_KEY = :puma_server
|
55
50
|
|
56
51
|
# Create a server for the rack app +app+.
|
@@ -86,15 +81,18 @@ module Puma
|
|
86
81
|
UserFileDefaultOptions.new(options, Configuration::DEFAULTS)
|
87
82
|
end
|
88
83
|
|
89
|
-
@
|
90
|
-
@
|
91
|
-
@
|
92
|
-
@
|
93
|
-
@
|
94
|
-
@persistent_timeout
|
95
|
-
@
|
96
|
-
@
|
97
|
-
@
|
84
|
+
@clustered = (@options.fetch :workers, 0) > 0
|
85
|
+
@worker_write = @options[:worker_write]
|
86
|
+
@log_writer = @options.fetch :log_writer, LogWriter.stdio
|
87
|
+
@early_hints = @options[:early_hints]
|
88
|
+
@first_data_timeout = @options[:first_data_timeout]
|
89
|
+
@persistent_timeout = @options[:persistent_timeout]
|
90
|
+
@idle_timeout = @options[:idle_timeout]
|
91
|
+
@min_threads = @options[:min_threads]
|
92
|
+
@max_threads = @options[:max_threads]
|
93
|
+
@queue_requests = @options[:queue_requests]
|
94
|
+
@max_fast_inline = @options[:max_fast_inline]
|
95
|
+
@io_selector_backend = @options[:io_selector_backend]
|
98
96
|
@http_content_length_limit = @options[:http_content_length_limit]
|
99
97
|
|
100
98
|
# make this a hash, since we prefer `key?` over `include?`
|
@@ -121,6 +119,8 @@ module Puma
|
|
121
119
|
@precheck_closing = true
|
122
120
|
|
123
121
|
@requests_count = 0
|
122
|
+
|
123
|
+
@idle_timeout_reached = false
|
124
124
|
end
|
125
125
|
|
126
126
|
def inherit_binder(bind)
|
@@ -330,8 +330,28 @@ module Puma
|
|
330
330
|
|
331
331
|
while @status == :run || (drain && shutting_down?)
|
332
332
|
begin
|
333
|
-
ios = IO.select sockets, nil, nil, (shutting_down? ? 0 :
|
334
|
-
|
333
|
+
ios = IO.select sockets, nil, nil, (shutting_down? ? 0 : @idle_timeout)
|
334
|
+
unless ios
|
335
|
+
unless shutting_down?
|
336
|
+
@idle_timeout_reached = true
|
337
|
+
|
338
|
+
if @clustered
|
339
|
+
@worker_write << "i#{Process.pid}\n" rescue nil
|
340
|
+
next
|
341
|
+
else
|
342
|
+
@log_writer.log "- Idle timeout reached"
|
343
|
+
@status = :stop
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
break
|
348
|
+
end
|
349
|
+
|
350
|
+
if @idle_timeout_reached && @clustered
|
351
|
+
@idle_timeout_reached = false
|
352
|
+
@worker_write << "i#{Process.pid}\n" rescue nil
|
353
|
+
end
|
354
|
+
|
335
355
|
ios.first.each do |sock|
|
336
356
|
if sock == check
|
337
357
|
break if handle_check
|
@@ -367,6 +387,7 @@ module Puma
|
|
367
387
|
@queue_requests = false
|
368
388
|
@reactor.shutdown
|
369
389
|
end
|
390
|
+
|
370
391
|
graceful_shutdown if @status == :stop || @status == :restart
|
371
392
|
rescue Exception => e
|
372
393
|
@log_writer.unknown_error e, nil, "Exception handling servers"
|
@@ -476,7 +497,7 @@ module Puma
|
|
476
497
|
end
|
477
498
|
true
|
478
499
|
rescue StandardError => e
|
479
|
-
client_error(e, client)
|
500
|
+
client_error(e, client, requests)
|
480
501
|
# The ensure tries to close +client+ down
|
481
502
|
requests > 0
|
482
503
|
ensure
|
@@ -504,22 +525,22 @@ module Puma
|
|
504
525
|
# :nocov:
|
505
526
|
|
506
527
|
# Handle various error types thrown by Client I/O operations.
|
507
|
-
def client_error(e, client)
|
528
|
+
def client_error(e, client, requests = 1)
|
508
529
|
# Swallow, do not log
|
509
530
|
return if [ConnectionError, EOFError].include?(e.class)
|
510
531
|
|
511
|
-
lowlevel_error(e, client.env)
|
512
532
|
case e
|
513
533
|
when MiniSSL::SSLError
|
534
|
+
lowlevel_error(e, client.env)
|
514
535
|
@log_writer.ssl_error e, client.io
|
515
536
|
when HttpParserError
|
516
|
-
client
|
537
|
+
response_to_error(client, requests, e, 400)
|
517
538
|
@log_writer.parse_error e, client
|
518
539
|
when HttpParserError501
|
519
|
-
client
|
540
|
+
response_to_error(client, requests, e, 501)
|
520
541
|
@log_writer.parse_error e, client
|
521
542
|
else
|
522
|
-
client
|
543
|
+
response_to_error(client, requests, e, 500)
|
523
544
|
@log_writer.unknown_error e, nil, "Read"
|
524
545
|
end
|
525
546
|
end
|
@@ -541,10 +562,17 @@ module Puma
|
|
541
562
|
backtrace = e.backtrace.nil? ? '<no backtrace available>' : e.backtrace.join("\n")
|
542
563
|
[status, {}, ["Puma caught this error: #{e.message} (#{e.class})\n#{backtrace}"]]
|
543
564
|
else
|
544
|
-
[status, {}, ["
|
565
|
+
[status, {}, [""]]
|
545
566
|
end
|
546
567
|
end
|
547
568
|
|
569
|
+
def response_to_error(client, requests, err, status_code)
|
570
|
+
status, headers, res_body = lowlevel_error(err, client.env, status_code)
|
571
|
+
prepare_response(status, headers, res_body, requests, client)
|
572
|
+
client.write_error(status_code)
|
573
|
+
end
|
574
|
+
private :response_to_error
|
575
|
+
|
548
576
|
# Wait for all outstanding requests to finish.
|
549
577
|
#
|
550
578
|
def graceful_shutdown
|
@@ -623,5 +651,27 @@ module Puma
|
|
623
651
|
def stats
|
624
652
|
STAT_METHODS.map {|name| [name, send(name) || 0]}.to_h
|
625
653
|
end
|
654
|
+
|
655
|
+
# below are 'delegations' to binder
|
656
|
+
# remove in Puma 7?
|
657
|
+
|
658
|
+
|
659
|
+
def add_tcp_listener(host, port, optimize_for_latency = true, backlog = 1024)
|
660
|
+
@binder.add_tcp_listener host, port, optimize_for_latency, backlog
|
661
|
+
end
|
662
|
+
|
663
|
+
def add_ssl_listener(host, port, ctx, optimize_for_latency = true,
|
664
|
+
backlog = 1024)
|
665
|
+
@binder.add_ssl_listener host, port, ctx, optimize_for_latency, backlog
|
666
|
+
end
|
667
|
+
|
668
|
+
def add_unix_listener(path, umask = nil, mode = nil, backlog = 1024)
|
669
|
+
@binder.add_unix_listener path, umask, mode, backlog
|
670
|
+
end
|
671
|
+
|
672
|
+
# @!attribute [r] connected_ports
|
673
|
+
def connected_ports
|
674
|
+
@binder.connected_ports
|
675
|
+
end
|
626
676
|
end
|
627
677
|
end
|
data/lib/puma/state_file.rb
CHANGED
data/lib/puma/thread_pool.rb
CHANGED
@@ -51,6 +51,8 @@ module Puma
|
|
51
51
|
@block = block
|
52
52
|
@out_of_band = options[:out_of_band]
|
53
53
|
@clean_thread_locals = options[:clean_thread_locals]
|
54
|
+
@before_thread_start = options[:before_thread_start]
|
55
|
+
@before_thread_exit = options[:before_thread_exit]
|
54
56
|
@reaping_time = options[:reaping_time]
|
55
57
|
@auto_trim_time = options[:auto_trim_time]
|
56
58
|
|
@@ -107,6 +109,7 @@ module Puma
|
|
107
109
|
def spawn_thread
|
108
110
|
@spawned += 1
|
109
111
|
|
112
|
+
trigger_before_thread_start_hooks
|
110
113
|
th = Thread.new(@spawned) do |spawned|
|
111
114
|
Puma.set_thread_name '%s tp %03i' % [@name, spawned]
|
112
115
|
todo = @todo
|
@@ -125,6 +128,7 @@ module Puma
|
|
125
128
|
@spawned -= 1
|
126
129
|
@workers.delete th
|
127
130
|
not_full.signal
|
131
|
+
trigger_before_thread_exit_hooks
|
128
132
|
Thread.exit
|
129
133
|
end
|
130
134
|
|
@@ -162,6 +166,36 @@ module Puma
|
|
162
166
|
|
163
167
|
private :spawn_thread
|
164
168
|
|
169
|
+
def trigger_before_thread_start_hooks
|
170
|
+
return unless @before_thread_start&.any?
|
171
|
+
|
172
|
+
@before_thread_start.each do |b|
|
173
|
+
begin
|
174
|
+
b.call
|
175
|
+
rescue Exception => e
|
176
|
+
STDERR.puts "WARNING before_thread_start hook failed with exception (#{e.class}) #{e.message}"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
nil
|
180
|
+
end
|
181
|
+
|
182
|
+
private :trigger_before_thread_start_hooks
|
183
|
+
|
184
|
+
def trigger_before_thread_exit_hooks
|
185
|
+
return unless @before_thread_exit&.any?
|
186
|
+
|
187
|
+
@before_thread_exit.each do |b|
|
188
|
+
begin
|
189
|
+
b.call
|
190
|
+
rescue Exception => e
|
191
|
+
STDERR.puts "WARNING before_thread_exit hook failed with exception (#{e.class}) #{e.message}"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
nil
|
195
|
+
end
|
196
|
+
|
197
|
+
private :trigger_before_thread_exit_hooks
|
198
|
+
|
165
199
|
# @version 5.0.0
|
166
200
|
def trigger_out_of_band_hook
|
167
201
|
return false unless @out_of_band&.any?
|
data/tools/Dockerfile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Use this Dockerfile to create minimal reproductions of issues
|
2
2
|
|
3
|
-
FROM ruby:3.
|
3
|
+
FROM ruby:3.2
|
4
4
|
|
5
5
|
# throw errors if Gemfile has been modified since Gemfile.lock
|
6
6
|
RUN bundle config --global frozen 1
|
@@ -8,7 +8,7 @@ RUN bundle config --global frozen 1
|
|
8
8
|
WORKDIR /usr/src/app
|
9
9
|
|
10
10
|
COPY . .
|
11
|
-
|
11
|
+
|
12
12
|
RUN bundle install
|
13
13
|
RUN bundle exec rake compile
|
14
14
|
|
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: 6.3
|
4
|
+
version: 6.4.3
|
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-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nio4r
|
@@ -145,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
145
145
|
- !ruby/object:Gem::Version
|
146
146
|
version: '0'
|
147
147
|
requirements: []
|
148
|
-
rubygems_version: 3.
|
148
|
+
rubygems_version: 3.5.16
|
149
149
|
signing_key:
|
150
150
|
specification_version: 4
|
151
151
|
summary: Puma is a simple, fast, threaded, and highly parallel HTTP 1.1 server for
|