puma 7.0.3 → 7.2.0
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.
- checksums.yaml +4 -4
- data/History.md +111 -1
- data/README.md +24 -11
- data/docs/deployment.md +58 -23
- data/docs/jungle/README.md +1 -1
- data/docs/kubernetes.md +5 -12
- data/docs/plugins.md +2 -2
- data/docs/signals.md +10 -10
- data/docs/stats.md +2 -2
- data/docs/systemd.md +3 -3
- data/ext/puma_http11/mini_ssl.c +18 -3
- data/ext/puma_http11/org/jruby/puma/Http11.java +9 -1
- data/ext/puma_http11/puma_http11.c +122 -118
- data/lib/puma/app/status.rb +10 -2
- data/lib/puma/client.rb +21 -25
- data/lib/puma/cluster/worker.rb +10 -9
- data/lib/puma/cluster/worker_handle.rb +2 -2
- data/lib/puma/cluster.rb +11 -10
- data/lib/puma/cluster_accept_loop_delay.rb +91 -0
- data/lib/puma/configuration.rb +17 -9
- data/lib/puma/const.rb +2 -2
- data/lib/puma/dsl.rb +41 -10
- data/lib/puma/launcher.rb +32 -26
- data/lib/puma/reactor.rb +3 -12
- data/lib/puma/request.rb +10 -8
- data/lib/puma/runner.rb +1 -1
- data/lib/puma/server.rb +79 -61
- data/lib/puma/single.rb +2 -2
- data/lib/puma/state_file.rb +3 -2
- data/lib/puma/thread_pool.rb +10 -1
- data/tools/Dockerfile +13 -5
- metadata +5 -4
- data/ext/puma_http11/ext_help.h +0 -15
data/lib/puma/server.rb
CHANGED
|
@@ -13,14 +13,12 @@ require_relative 'binder'
|
|
|
13
13
|
require_relative 'util'
|
|
14
14
|
require_relative 'request'
|
|
15
15
|
require_relative 'configuration'
|
|
16
|
+
require_relative 'cluster_accept_loop_delay'
|
|
16
17
|
|
|
17
18
|
require 'socket'
|
|
18
19
|
require 'io/wait' unless Puma::HAS_NATIVE_IO_WAIT
|
|
19
20
|
|
|
20
21
|
module Puma
|
|
21
|
-
# Add `Thread#puma_server` and `Thread#puma_server=`
|
|
22
|
-
Thread.attr_accessor(:puma_server)
|
|
23
|
-
|
|
24
22
|
# The HTTP Server itself. Serves out a single Rack app.
|
|
25
23
|
#
|
|
26
24
|
# This class is used by the `Puma::Single` and `Puma::Cluster` classes
|
|
@@ -58,7 +56,6 @@ module Puma
|
|
|
58
56
|
attr_accessor :app
|
|
59
57
|
attr_accessor :binder
|
|
60
58
|
|
|
61
|
-
|
|
62
59
|
# Create a server for the rack app +app+.
|
|
63
60
|
#
|
|
64
61
|
# +log_writer+ is a Puma::LogWriter object used to log info and error messages.
|
|
@@ -110,6 +107,10 @@ module Puma
|
|
|
110
107
|
@enable_keep_alives &&= @queue_requests
|
|
111
108
|
@io_selector_backend = @options[:io_selector_backend]
|
|
112
109
|
@http_content_length_limit = @options[:http_content_length_limit]
|
|
110
|
+
@cluster_accept_loop_delay = ClusterAcceptLoopDelay.new(
|
|
111
|
+
workers: @options[:workers],
|
|
112
|
+
max_delay: @options[:wait_for_less_busy_worker] || 0 # Real default is in Configuration::DEFAULTS, this is for unit testing
|
|
113
|
+
)
|
|
113
114
|
|
|
114
115
|
if @options[:fiber_per_request]
|
|
115
116
|
singleton_class.prepend(FiberPerRequest)
|
|
@@ -245,11 +246,6 @@ module Puma
|
|
|
245
246
|
@thread_pool&.pool_capacity
|
|
246
247
|
end
|
|
247
248
|
|
|
248
|
-
# @!attribute [r] busy_threads
|
|
249
|
-
def busy_threads
|
|
250
|
-
@thread_pool&.busy_threads
|
|
251
|
-
end
|
|
252
|
-
|
|
253
249
|
# Runs the server.
|
|
254
250
|
#
|
|
255
251
|
# If +background+ is true (the default) then a thread is spun
|
|
@@ -263,10 +259,14 @@ module Puma
|
|
|
263
259
|
|
|
264
260
|
@status = :run
|
|
265
261
|
|
|
266
|
-
@thread_pool = ThreadPool.new(thread_name, options) { |client| process_client client }
|
|
262
|
+
@thread_pool = ThreadPool.new(thread_name, options, server: self) { |client| process_client client }
|
|
267
263
|
|
|
268
264
|
if @queue_requests
|
|
269
|
-
@reactor = Reactor.new(@io_selector_backend) { |c|
|
|
265
|
+
@reactor = Reactor.new(@io_selector_backend) { |c|
|
|
266
|
+
# Inversion of control, the reactor is calling a method on the server when it
|
|
267
|
+
# is done buffering a request or receives a new request from a keepalive connection.
|
|
268
|
+
self.reactor_wakeup(c)
|
|
269
|
+
}
|
|
270
270
|
@reactor.run
|
|
271
271
|
end
|
|
272
272
|
|
|
@@ -291,12 +291,15 @@ module Puma
|
|
|
291
291
|
# This method is called from the Reactor thread when a queued Client receives data,
|
|
292
292
|
# times out, or when the Reactor is shutting down.
|
|
293
293
|
#
|
|
294
|
+
# While the code lives in the Server, the logic is executed on the reactor thread, independently
|
|
295
|
+
# from the server.
|
|
296
|
+
#
|
|
294
297
|
# It is responsible for ensuring that a request has been completely received
|
|
295
298
|
# before it starts to be processed by the ThreadPool. This may be known as read buffering.
|
|
296
299
|
# If read buffering is not done, and no other read buffering is performed (such as by an application server
|
|
297
300
|
# such as nginx) then the application would be subject to a slow client attack.
|
|
298
301
|
#
|
|
299
|
-
# For a graphical representation of how the request buffer works see [architecture.md](https://github.com/puma/puma/blob/
|
|
302
|
+
# For a graphical representation of how the request buffer works see [architecture.md](https://github.com/puma/puma/blob/main/docs/architecture.md).
|
|
300
303
|
#
|
|
301
304
|
# The method checks to see if it has the full header and body with
|
|
302
305
|
# the `Puma::Client#try_to_finish` method. If the full request has been sent,
|
|
@@ -325,7 +328,7 @@ module Puma
|
|
|
325
328
|
end
|
|
326
329
|
rescue StandardError => e
|
|
327
330
|
client_error(e, client)
|
|
328
|
-
client
|
|
331
|
+
close_client_safely(client)
|
|
329
332
|
true
|
|
330
333
|
end
|
|
331
334
|
|
|
@@ -339,7 +342,6 @@ module Puma
|
|
|
339
342
|
pool = @thread_pool
|
|
340
343
|
queue_requests = @queue_requests
|
|
341
344
|
drain = options[:drain_on_shutdown] ? 0 : nil
|
|
342
|
-
max_flt = @max_threads.to_f
|
|
343
345
|
|
|
344
346
|
addr_send_name, addr_value = case options[:remote_address]
|
|
345
347
|
when :value
|
|
@@ -384,15 +386,13 @@ module Puma
|
|
|
384
386
|
# clients until the code is finished.
|
|
385
387
|
pool.wait_while_out_of_band_running
|
|
386
388
|
|
|
387
|
-
#
|
|
388
|
-
if pool.busy_threads
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
sleep 0.0001
|
|
395
|
-
end
|
|
389
|
+
# A well rested herd (cluster) runs faster
|
|
390
|
+
if @cluster_accept_loop_delay.on? && (busy_threads_plus_todo = pool.busy_threads) > 0
|
|
391
|
+
delay = @cluster_accept_loop_delay.calculate(
|
|
392
|
+
max_threads: @max_threads,
|
|
393
|
+
busy_threads_plus_todo: busy_threads_plus_todo
|
|
394
|
+
)
|
|
395
|
+
sleep(delay)
|
|
396
396
|
end
|
|
397
397
|
|
|
398
398
|
io = begin
|
|
@@ -401,11 +401,9 @@ module Puma
|
|
|
401
401
|
next
|
|
402
402
|
end
|
|
403
403
|
drain += 1 if shutting_down?
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
c.send(addr_send_name, addr_value) if addr_value
|
|
408
|
-
}
|
|
404
|
+
client = new_client(io, sock)
|
|
405
|
+
client.send(addr_send_name, addr_value) if addr_value
|
|
406
|
+
pool << client
|
|
409
407
|
end
|
|
410
408
|
end
|
|
411
409
|
rescue IOError, Errno::EBADF
|
|
@@ -442,6 +440,14 @@ module Puma
|
|
|
442
440
|
@events.fire :state, :done
|
|
443
441
|
end
|
|
444
442
|
|
|
443
|
+
# :nodoc:
|
|
444
|
+
def new_client(io, sock)
|
|
445
|
+
client = Client.new(io, @binder.env(sock))
|
|
446
|
+
client.listener = sock
|
|
447
|
+
client.http_content_length_limit = @http_content_length_limit
|
|
448
|
+
client
|
|
449
|
+
end
|
|
450
|
+
|
|
445
451
|
# :nodoc:
|
|
446
452
|
def handle_check
|
|
447
453
|
cmd = @check.read(1)
|
|
@@ -472,9 +478,6 @@ module Puma
|
|
|
472
478
|
#
|
|
473
479
|
# Return true if one or more requests were processed.
|
|
474
480
|
def process_client(client)
|
|
475
|
-
# Advertise this server into the thread
|
|
476
|
-
Thread.current.puma_server = self
|
|
477
|
-
|
|
478
481
|
close_socket = true
|
|
479
482
|
|
|
480
483
|
requests = 0
|
|
@@ -493,31 +496,41 @@ module Puma
|
|
|
493
496
|
client.finish(@first_data_timeout)
|
|
494
497
|
end
|
|
495
498
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
499
|
+
can_loop = true
|
|
500
|
+
while can_loop
|
|
501
|
+
can_loop = false
|
|
502
|
+
@requests_count += 1
|
|
503
|
+
case handle_request(client, requests + 1)
|
|
504
|
+
when :close
|
|
505
|
+
when :async
|
|
506
|
+
close_socket = false
|
|
507
|
+
when :keep_alive
|
|
508
|
+
requests += 1
|
|
503
509
|
|
|
504
|
-
|
|
510
|
+
client.reset
|
|
505
511
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
512
|
+
# This indicates data exists in the client read buffer and there may be
|
|
513
|
+
# additional requests on it, so process them
|
|
514
|
+
next_request_ready = if client.has_back_to_back_requests?
|
|
515
|
+
with_force_shutdown(client) { client.process_back_to_back_requests }
|
|
516
|
+
else
|
|
517
|
+
with_force_shutdown(client) { client.eagerly_finish }
|
|
518
|
+
end
|
|
513
519
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
520
|
+
if next_request_ready
|
|
521
|
+
# When Puma has spare threads, allow this one to be monopolized
|
|
522
|
+
# Perf optimization for https://github.com/puma/puma/issues/3788
|
|
523
|
+
if @thread_pool.waiting > 0
|
|
524
|
+
can_loop = true
|
|
525
|
+
else
|
|
526
|
+
@thread_pool << client
|
|
527
|
+
close_socket = false
|
|
528
|
+
end
|
|
529
|
+
elsif @queue_requests
|
|
530
|
+
client.set_timeout @persistent_timeout
|
|
531
|
+
if @reactor.add client
|
|
532
|
+
close_socket = false
|
|
533
|
+
end
|
|
521
534
|
end
|
|
522
535
|
end
|
|
523
536
|
end
|
|
@@ -529,16 +542,21 @@ module Puma
|
|
|
529
542
|
ensure
|
|
530
543
|
client.io_buffer.reset
|
|
531
544
|
|
|
532
|
-
|
|
533
|
-
client.close if close_socket
|
|
534
|
-
rescue IOError, SystemCallError
|
|
535
|
-
# Already closed
|
|
536
|
-
rescue StandardError => e
|
|
537
|
-
@log_writer.unknown_error e, nil, "Client"
|
|
538
|
-
end
|
|
545
|
+
close_client_safely(client) if close_socket
|
|
539
546
|
end
|
|
540
547
|
end
|
|
541
548
|
|
|
549
|
+
# :nodoc:
|
|
550
|
+
def close_client_safely(client)
|
|
551
|
+
client.close
|
|
552
|
+
rescue IOError, SystemCallError
|
|
553
|
+
# Already closed
|
|
554
|
+
rescue MiniSSL::SSLError => e
|
|
555
|
+
@log_writer.ssl_error e, client.io
|
|
556
|
+
rescue StandardError => e
|
|
557
|
+
@log_writer.unknown_error e, nil, "Client"
|
|
558
|
+
end
|
|
559
|
+
|
|
542
560
|
# Triggers a client timeout if the thread-pool shuts down
|
|
543
561
|
# during execution of the provided block.
|
|
544
562
|
def with_force_shutdown(client, &block)
|
|
@@ -692,7 +710,7 @@ module Puma
|
|
|
692
710
|
|
|
693
711
|
def reset_max
|
|
694
712
|
@reactor.reactor_max = 0 if @reactor
|
|
695
|
-
@thread_pool
|
|
713
|
+
@thread_pool&.reset_max
|
|
696
714
|
end
|
|
697
715
|
|
|
698
716
|
# below are 'delegations' to binder
|
data/lib/puma/single.rb
CHANGED
data/lib/puma/state_file.rb
CHANGED
|
@@ -32,10 +32,11 @@ module Puma
|
|
|
32
32
|
"#{k}: \"#{v}\"\n" : "#{k}: #{v}\n")
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
|
+
|
|
35
36
|
if permission
|
|
36
|
-
File.write path, contents, mode: 'wb:UTF-8'
|
|
37
|
-
else
|
|
38
37
|
File.write path, contents, mode: 'wb:UTF-8', perm: permission
|
|
38
|
+
else
|
|
39
|
+
File.write path, contents, mode: 'wb:UTF-8'
|
|
39
40
|
end
|
|
40
41
|
end
|
|
41
42
|
|
data/lib/puma/thread_pool.rb
CHANGED
|
@@ -5,6 +5,10 @@ require 'thread'
|
|
|
5
5
|
require_relative 'io_buffer'
|
|
6
6
|
|
|
7
7
|
module Puma
|
|
8
|
+
|
|
9
|
+
# Add `Thread#puma_server` and `Thread#puma_server=`
|
|
10
|
+
Thread.attr_accessor(:puma_server)
|
|
11
|
+
|
|
8
12
|
# Internal Docs for A simple thread pool management object.
|
|
9
13
|
#
|
|
10
14
|
# Each Puma "worker" has a thread pool to process requests.
|
|
@@ -33,7 +37,9 @@ module Puma
|
|
|
33
37
|
# The block passed is the work that will be performed in each
|
|
34
38
|
# thread.
|
|
35
39
|
#
|
|
36
|
-
def initialize(name, options = {}, &block)
|
|
40
|
+
def initialize(name, options = {}, server: nil, &block)
|
|
41
|
+
@server = server
|
|
42
|
+
|
|
37
43
|
@not_empty = ConditionVariable.new
|
|
38
44
|
@not_full = ConditionVariable.new
|
|
39
45
|
@mutex = Mutex.new
|
|
@@ -134,6 +140,9 @@ module Puma
|
|
|
134
140
|
trigger_before_thread_start_hooks
|
|
135
141
|
th = Thread.new(@spawned) do |spawned|
|
|
136
142
|
Puma.set_thread_name '%s tp %03i' % [@name, spawned]
|
|
143
|
+
# Advertise server into the thread
|
|
144
|
+
Thread.current.puma_server = @server
|
|
145
|
+
|
|
137
146
|
todo = @todo
|
|
138
147
|
block = @block
|
|
139
148
|
mutex = @mutex
|
data/tools/Dockerfile
CHANGED
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
# Use this Dockerfile to create minimal reproductions of issues
|
|
2
|
+
# Build (MRI): docker build -f tools/Dockerfile .
|
|
3
|
+
# Build (JRuby): docker build -f tools/Dockerfile --build-arg RUBY_IMAGE=jruby:9.4 .
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
ARG RUBY_IMAGE=ruby:latest
|
|
6
|
+
FROM ${RUBY_IMAGE}
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
# Set BUNDLE_FROZEN=false if you need to update Gemfile.lock during a build.
|
|
9
|
+
ARG BUNDLE_FROZEN=true
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
RUN apt-get update \
|
|
12
|
+
&& apt-get install -y --no-install-recommends ragel procps git \
|
|
13
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
14
|
+
|
|
15
|
+
# Only freeze Bundler and compile native extensions when using MRI.
|
|
16
|
+
RUN if [ "$(ruby -e 'print RUBY_ENGINE')" = "ruby" ] && [ "${BUNDLE_FROZEN}" = "true" ]; then bundle config --global frozen 1; fi
|
|
9
17
|
|
|
10
18
|
WORKDIR /usr/src/app
|
|
11
19
|
|
|
12
20
|
COPY . .
|
|
13
21
|
|
|
14
22
|
RUN bundle install
|
|
15
|
-
RUN bundle exec rake compile
|
|
23
|
+
RUN if [ "$(ruby -e 'print RUBY_ENGINE')" = "ruby" ]; then bundle exec rake compile; fi
|
|
16
24
|
|
|
17
25
|
EXPOSE 9292
|
|
18
26
|
CMD ["bundle", "exec", "bin/puma", "test/rackup/hello.ru"]
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: puma
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 7.0
|
|
4
|
+
version: 7.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Evan Phoenix
|
|
@@ -66,7 +66,6 @@ files:
|
|
|
66
66
|
- docs/testing_benchmarks_local_files.md
|
|
67
67
|
- docs/testing_test_rackup_ci_files.md
|
|
68
68
|
- ext/puma_http11/PumaHttp11Service.java
|
|
69
|
-
- ext/puma_http11/ext_help.h
|
|
70
69
|
- ext/puma_http11/extconf.rb
|
|
71
70
|
- ext/puma_http11/http11_parser.c
|
|
72
71
|
- ext/puma_http11/http11_parser.h
|
|
@@ -87,6 +86,7 @@ files:
|
|
|
87
86
|
- lib/puma/cluster.rb
|
|
88
87
|
- lib/puma/cluster/worker.rb
|
|
89
88
|
- lib/puma/cluster/worker_handle.rb
|
|
89
|
+
- lib/puma/cluster_accept_loop_delay.rb
|
|
90
90
|
- lib/puma/commonlogger.rb
|
|
91
91
|
- lib/puma/configuration.rb
|
|
92
92
|
- lib/puma/const.rb
|
|
@@ -127,10 +127,11 @@ licenses:
|
|
|
127
127
|
- BSD-3-Clause
|
|
128
128
|
metadata:
|
|
129
129
|
bug_tracker_uri: https://github.com/puma/puma/issues
|
|
130
|
-
changelog_uri: https://github.com/puma/puma/blob/
|
|
130
|
+
changelog_uri: https://github.com/puma/puma/blob/main/History.md
|
|
131
131
|
homepage_uri: https://puma.io
|
|
132
132
|
source_code_uri: https://github.com/puma/puma
|
|
133
133
|
rubygems_mfa_required: 'true'
|
|
134
|
+
msys2_mingw_dependencies: openssl
|
|
134
135
|
rdoc_options: []
|
|
135
136
|
require_paths:
|
|
136
137
|
- lib
|
|
@@ -145,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
145
146
|
- !ruby/object:Gem::Version
|
|
146
147
|
version: '0'
|
|
147
148
|
requirements: []
|
|
148
|
-
rubygems_version:
|
|
149
|
+
rubygems_version: 4.0.3
|
|
149
150
|
specification_version: 4
|
|
150
151
|
summary: A Ruby/Rack web server built for parallelism.
|
|
151
152
|
test_files: []
|
data/ext/puma_http11/ext_help.h
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
#ifndef ext_help_h
|
|
2
|
-
#define ext_help_h
|
|
3
|
-
|
|
4
|
-
#define RAISE_NOT_NULL(T) if(T == NULL) rb_raise(rb_eArgError, "%s", "NULL found for " # T " when shouldn't be.");
|
|
5
|
-
#define DATA_GET(from,type,data_type,name) TypedData_Get_Struct(from,type,data_type,name); RAISE_NOT_NULL(name);
|
|
6
|
-
#define REQUIRE_TYPE(V, T) if(TYPE(V) != T) rb_raise(rb_eTypeError, "%s", "Wrong argument type for " # V " required " # T);
|
|
7
|
-
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
|
|
8
|
-
|
|
9
|
-
#ifdef DEBUG
|
|
10
|
-
#define TRACE() fprintf(stderr, "> %s:%d:%s\n", __FILE__, __LINE__, __FUNCTION__)
|
|
11
|
-
#else
|
|
12
|
-
#define TRACE()
|
|
13
|
-
#endif
|
|
14
|
-
|
|
15
|
-
#endif
|