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/cluster.rb
CHANGED
|
@@ -23,7 +23,7 @@ module Puma
|
|
|
23
23
|
@next_check = Time.now
|
|
24
24
|
|
|
25
25
|
@worker_max = [] # keeps track of 'max' stat values
|
|
26
|
-
@
|
|
26
|
+
@pending_phased_restart = false
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
# Returns the list of cluster worker handles.
|
|
@@ -186,7 +186,7 @@ module Puma
|
|
|
186
186
|
# we need to phase any workers out (which will restart
|
|
187
187
|
# in the right phase).
|
|
188
188
|
#
|
|
189
|
-
w = @workers.find { |x| x.phase
|
|
189
|
+
w = @workers.find { |x| x.phase < @phase }
|
|
190
190
|
|
|
191
191
|
if w
|
|
192
192
|
if refork
|
|
@@ -221,12 +221,11 @@ module Puma
|
|
|
221
221
|
pipes[:wakeup] = @wakeup
|
|
222
222
|
end
|
|
223
223
|
|
|
224
|
-
server = start_server if preload?
|
|
225
224
|
new_worker = Worker.new index: index,
|
|
226
225
|
master: master,
|
|
227
226
|
launcher: @launcher,
|
|
228
227
|
pipes: pipes,
|
|
229
|
-
|
|
228
|
+
app: (app if preload?)
|
|
230
229
|
new_worker.run
|
|
231
230
|
end
|
|
232
231
|
|
|
@@ -238,7 +237,7 @@ module Puma
|
|
|
238
237
|
def phased_restart(refork = false)
|
|
239
238
|
return false if @options[:preload_app] && !refork
|
|
240
239
|
|
|
241
|
-
@
|
|
240
|
+
@pending_phased_restart = refork ? :refork : true
|
|
242
241
|
wakeup!
|
|
243
242
|
|
|
244
243
|
true
|
|
@@ -456,11 +455,11 @@ module Puma
|
|
|
456
455
|
break
|
|
457
456
|
end
|
|
458
457
|
|
|
459
|
-
if @
|
|
460
|
-
start_phased_restart(@
|
|
458
|
+
if @pending_phased_restart
|
|
459
|
+
start_phased_restart(@pending_phased_restart == :refork)
|
|
461
460
|
|
|
462
|
-
in_phased_restart = @
|
|
463
|
-
@
|
|
461
|
+
in_phased_restart = @pending_phased_restart
|
|
462
|
+
@pending_phased_restart = false
|
|
464
463
|
|
|
465
464
|
workers_not_booted = @options[:workers]
|
|
466
465
|
# worker 0 is not restarted on refork
|
|
@@ -583,7 +582,9 @@ module Puma
|
|
|
583
582
|
# `Process.wait2(-1)` from detecting a terminated process: https://bugs.ruby-lang.org/issues/19837.
|
|
584
583
|
# 2. When `fork_worker` is enabled, some worker may not be direct children,
|
|
585
584
|
# but grand children. Because of this they won't be reaped by `Process.wait2(-1)`.
|
|
586
|
-
if reaped_children.delete(w.pid) || Process.
|
|
585
|
+
if (status = reaped_children.delete(w.pid) || Process.wait2(w.pid, Process::WNOHANG)&.last)
|
|
586
|
+
w.process_status = status
|
|
587
|
+
@config.run_hooks(:after_worker_shutdown, w, @log_writer)
|
|
587
588
|
true
|
|
588
589
|
else
|
|
589
590
|
w.term if w.term?
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Puma
|
|
4
|
+
# Calculate a delay value for sleeping when running in clustered mode
|
|
5
|
+
#
|
|
6
|
+
# The main reason this is a class is so it can be unit tested independently.
|
|
7
|
+
# This makes modification easier in the future if we can encode properties of the
|
|
8
|
+
# delay into a test instead of relying on end-to-end testing only.
|
|
9
|
+
#
|
|
10
|
+
# This is an imprecise mechanism to address specific goals:
|
|
11
|
+
#
|
|
12
|
+
# - Evenly distribute requests across all workers at start
|
|
13
|
+
# - Evenly distribute CPU resources across all workers
|
|
14
|
+
#
|
|
15
|
+
# ## Goal: Distribute requests across workers at start
|
|
16
|
+
#
|
|
17
|
+
# There was a perf bug in Puma where one worker would wake up slightly before the rest and accept
|
|
18
|
+
# all the requests on the socket even though it didn't have enough resources to process all of them.
|
|
19
|
+
# This was originally fixed by never calling accept when a worker had more requests than threads
|
|
20
|
+
# already https://github.com/puma/puma/pull/3678/files/2736ebddb3fc8528e5150b5913fba251c37a8bf7#diff-a95f46e7ce116caddc9b9a9aa81004246d5210d5da5f4df90a818c780630166bL251-L291
|
|
21
|
+
#
|
|
22
|
+
# With the introduction of true keepalive support, there are two ways a request can come in:
|
|
23
|
+
# - A new request from a new client comes into the socket and it must be "accept"-ed
|
|
24
|
+
# - A keepalive request is served and the connection is retained. Another request is then accepted
|
|
25
|
+
#
|
|
26
|
+
# Ideally the server handles requests in the order they come in, and ideally it doesn't accept more requests than it can handle.
|
|
27
|
+
# These goals are contradictory, because when the server is at maximum capacity due to keepalive connections, it could mean we
|
|
28
|
+
# block all new requests, even if those came in before the new request on the older keepalive connection.
|
|
29
|
+
#
|
|
30
|
+
# ## Goal: Distribute CPU resources across all workers
|
|
31
|
+
#
|
|
32
|
+
# - This issue was opened https://github.com/puma/puma/issues/2078
|
|
33
|
+
#
|
|
34
|
+
# There are several entangled issues and it's not exactly clear what the root cause is, but the observable outcome
|
|
35
|
+
# was that performance was better with a small sleep, and that eventually became the default.
|
|
36
|
+
#
|
|
37
|
+
# An attempt to describe why this works is here: https://github.com/puma/puma/issues/2078#issuecomment-3287032470.
|
|
38
|
+
#
|
|
39
|
+
# Summarizing: The delay is for tuning the rate at which "accept" is called on the socket.
|
|
40
|
+
# Puma works by calling "accept" nonblock on the socket in a loop. When there are multiple workers
|
|
41
|
+
# (processes), they will "race" to accept a request at roughly the same rate. However, if one
|
|
42
|
+
# worker has all threads busy processing requests, then accepting a new request might "steal" it from
|
|
43
|
+
# a less busy worker. If a worker has no work to do, it should loop as fast as possible.
|
|
44
|
+
#
|
|
45
|
+
# ## Solution: Distribute requests across workers at start
|
|
46
|
+
#
|
|
47
|
+
# For now, both goals are framed as "load balancing" across workers (processes) and achieved through
|
|
48
|
+
# the same mechanism of sleeping longer to delay busier workers. Rather than the prior Puma 6.x
|
|
49
|
+
# and earlier behavior of using a binary on/off sleep value, we increase it an amount proportional
|
|
50
|
+
# to the load the server is under, capping the maximum delay to the scenario where all threads are busy
|
|
51
|
+
# and the todo list has reached a multiplier of the maximum number of threads.
|
|
52
|
+
#
|
|
53
|
+
# Private: API may change unexpectedly
|
|
54
|
+
class ClusterAcceptLoopDelay
|
|
55
|
+
attr_reader :max_delay
|
|
56
|
+
|
|
57
|
+
# Initialize happens once, `call` happens often. Perform global calculations here.
|
|
58
|
+
def initialize(
|
|
59
|
+
# Number of workers in the cluster
|
|
60
|
+
workers: ,
|
|
61
|
+
# Maximum delay in seconds i.e. 0.005 is 5 milliseconds
|
|
62
|
+
max_delay:
|
|
63
|
+
)
|
|
64
|
+
@on = max_delay > 0 && workers >= 2
|
|
65
|
+
@max_delay = max_delay.to_f
|
|
66
|
+
|
|
67
|
+
# Reach maximum delay when `max_threads * overload_multiplier` is reached in the system
|
|
68
|
+
@overload_multiplier = 25.0
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def on?
|
|
72
|
+
@on
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# We want the extreme values of this delay to be known (minimum and maximum) as well as
|
|
76
|
+
# a predictable curve between the two. i.e. no step functions or hard cliffs.
|
|
77
|
+
#
|
|
78
|
+
# Return value is always numeric. Returns 0 if there should be no delay.
|
|
79
|
+
def calculate(
|
|
80
|
+
# Number of threads working right now, plus number of requests in the todo list
|
|
81
|
+
busy_threads_plus_todo:,
|
|
82
|
+
# Maximum number of threads in the pool, note that the busy threads (alone) may go over this value at times
|
|
83
|
+
# if the pool needs to be reaped. The busy thread plus todo count may go over this value by a large amount.
|
|
84
|
+
max_threads:
|
|
85
|
+
)
|
|
86
|
+
max_value = @overload_multiplier * max_threads
|
|
87
|
+
# Approaches max delay when `busy_threads_plus_todo` approaches `max_value`
|
|
88
|
+
return max_delay * busy_threads_plus_todo.clamp(0, max_value) / max_value
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
data/lib/puma/configuration.rb
CHANGED
|
@@ -155,6 +155,7 @@ module Puma
|
|
|
155
155
|
out_of_band: [],
|
|
156
156
|
# Number of seconds for another request within a persistent session.
|
|
157
157
|
persistent_timeout: 65, # PUMA_PERSISTENT_TIMEOUT
|
|
158
|
+
prune_bundler: false,
|
|
158
159
|
queue_requests: true,
|
|
159
160
|
rackup: 'config.ru'.freeze,
|
|
160
161
|
raise_exception_on_sigterm: true,
|
|
@@ -196,7 +197,7 @@ module Puma
|
|
|
196
197
|
@clamped = false
|
|
197
198
|
end
|
|
198
199
|
|
|
199
|
-
attr_reader :plugins, :events, :hooks
|
|
200
|
+
attr_reader :plugins, :events, :hooks, :_options
|
|
200
201
|
|
|
201
202
|
def options
|
|
202
203
|
raise NotClampedError, "ensure clamp is called before accessing options" unless @clamped
|
|
@@ -237,18 +238,14 @@ module Puma
|
|
|
237
238
|
min = env['PUMA_MIN_THREADS'] || env['MIN_THREADS']
|
|
238
239
|
max = env['PUMA_MAX_THREADS'] || env['MAX_THREADS']
|
|
239
240
|
persistent_timeout = env['PUMA_PERSISTENT_TIMEOUT']
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
::Concurrent.available_processor_count
|
|
243
|
-
else
|
|
244
|
-
env['WEB_CONCURRENCY']
|
|
245
|
-
end
|
|
241
|
+
workers_env = env['WEB_CONCURRENCY']
|
|
242
|
+
workers = workers_env && workers_env.strip != "" ? parse_workers(workers_env.strip) : nil
|
|
246
243
|
|
|
247
244
|
{
|
|
248
245
|
min_threads: min && min != "" && Integer(min),
|
|
249
246
|
max_threads: max && max != "" && Integer(max),
|
|
250
247
|
persistent_timeout: persistent_timeout && persistent_timeout != "" && Integer(persistent_timeout),
|
|
251
|
-
workers: workers
|
|
248
|
+
workers: workers,
|
|
252
249
|
environment: env['APP_ENV'] || env['RACK_ENV'] || env['RAILS_ENV'],
|
|
253
250
|
}
|
|
254
251
|
end
|
|
@@ -379,12 +376,23 @@ module Puma
|
|
|
379
376
|
require 'concurrent/utility/processor_counter'
|
|
380
377
|
rescue LoadError
|
|
381
378
|
warn <<~MESSAGE
|
|
382
|
-
WEB_CONCURRENCY=auto requires the "concurrent-ruby" gem to be installed.
|
|
379
|
+
WEB_CONCURRENCY=auto or workers(:auto) requires the "concurrent-ruby" gem to be installed.
|
|
383
380
|
Please add "concurrent-ruby" to your Gemfile.
|
|
384
381
|
MESSAGE
|
|
385
382
|
raise
|
|
386
383
|
end
|
|
387
384
|
|
|
385
|
+
def parse_workers(value)
|
|
386
|
+
if value == :auto || value == 'auto'
|
|
387
|
+
require_processor_counter
|
|
388
|
+
Integer(::Concurrent.available_processor_count)
|
|
389
|
+
else
|
|
390
|
+
Integer(value)
|
|
391
|
+
end
|
|
392
|
+
rescue ArgumentError, TypeError
|
|
393
|
+
raise ArgumentError, "workers must be an Integer or :auto"
|
|
394
|
+
end
|
|
395
|
+
|
|
388
396
|
# Load and use the normal Rack builder if we can, otherwise
|
|
389
397
|
# fallback to our minimal version.
|
|
390
398
|
def rack_builder
|
data/lib/puma/const.rb
CHANGED
|
@@ -100,8 +100,8 @@ module Puma
|
|
|
100
100
|
# too taxing on performance.
|
|
101
101
|
module Const
|
|
102
102
|
|
|
103
|
-
PUMA_VERSION = VERSION = "7.0
|
|
104
|
-
CODE_NAME = "
|
|
103
|
+
PUMA_VERSION = VERSION = "7.2.0"
|
|
104
|
+
CODE_NAME = "On The Corner"
|
|
105
105
|
|
|
106
106
|
PUMA_SERVER_STRING = ["puma", PUMA_VERSION, CODE_NAME].join(" ").freeze
|
|
107
107
|
|
data/lib/puma/dsl.rb
CHANGED
|
@@ -216,6 +216,8 @@ module Puma
|
|
|
216
216
|
# activate_control_app 'unix:///var/run/pumactl.sock', { auth_token: '12345' }
|
|
217
217
|
# @example
|
|
218
218
|
# activate_control_app 'unix:///var/run/pumactl.sock', { no_token: true }
|
|
219
|
+
# @example
|
|
220
|
+
# activate_control_app 'unix:///var/run/pumactl.sock', { no_token: true, data_only: true}
|
|
219
221
|
#
|
|
220
222
|
def activate_control_app(url="auto", opts={})
|
|
221
223
|
if url == "auto"
|
|
@@ -240,6 +242,7 @@ module Puma
|
|
|
240
242
|
|
|
241
243
|
@options[:control_auth_token] = auth_token
|
|
242
244
|
@options[:control_url_umask] = opts[:umask] if opts[:umask]
|
|
245
|
+
@options[:control_data_only] = opts[:data_only] if opts[:data_only]
|
|
243
246
|
end
|
|
244
247
|
|
|
245
248
|
# Load additional configuration from a file.
|
|
@@ -656,7 +659,8 @@ module Puma
|
|
|
656
659
|
@options[:state] = path.to_s
|
|
657
660
|
end
|
|
658
661
|
|
|
659
|
-
# Use +permission+ to restrict permissions for the state file.
|
|
662
|
+
# Use +permission+ to restrict permissions for the state file. By convention,
|
|
663
|
+
# +permission+ is an octal number (e.g. `0640` or `0o640`).
|
|
660
664
|
#
|
|
661
665
|
# @example
|
|
662
666
|
# state_permission 0600
|
|
@@ -665,21 +669,27 @@ module Puma
|
|
|
665
669
|
@options[:state_permission] = permission
|
|
666
670
|
end
|
|
667
671
|
|
|
668
|
-
# How many worker processes to run.
|
|
669
|
-
#
|
|
672
|
+
# How many worker processes to run. Typically this is set to the number of
|
|
673
|
+
# available cores.
|
|
670
674
|
#
|
|
671
675
|
# The default is the value of the environment variable +WEB_CONCURRENCY+ if
|
|
672
|
-
# set, otherwise 0.
|
|
676
|
+
# set, otherwise 0. Passing +:auto+ will set the value to
|
|
677
|
+
# +Concurrent.available_processor_count+ (requires the concurrent-ruby gem).
|
|
678
|
+
# On some platforms (e.g. under CPU quotas) this may be fractional, and Puma
|
|
679
|
+
# will round down. If it rounds down to 0, Puma will run in single mode and
|
|
680
|
+
# cluster-only hooks like +before_worker_boot+ will not execute.
|
|
681
|
+
# If you rely on cluster-only hooks, set an explicit worker count.
|
|
673
682
|
#
|
|
674
|
-
#
|
|
683
|
+
# A value of 0 or nil means run in single mode.
|
|
675
684
|
#
|
|
676
685
|
# @example
|
|
677
686
|
# workers 2
|
|
687
|
+
# workers :auto
|
|
678
688
|
#
|
|
679
689
|
# @see Puma::Cluster
|
|
680
690
|
#
|
|
681
691
|
def workers(count)
|
|
682
|
-
@options[:workers] = count.
|
|
692
|
+
@options[:workers] = count.nil? ? 0 : @config.send(:parse_workers, count)
|
|
683
693
|
end
|
|
684
694
|
|
|
685
695
|
# Disable warning message when running in cluster mode with a single worker.
|
|
@@ -818,6 +828,20 @@ module Puma
|
|
|
818
828
|
|
|
819
829
|
alias_method :after_worker_boot, :after_worker_fork
|
|
820
830
|
|
|
831
|
+
# Code to run in the master right after a worker has stopped. The worker's
|
|
832
|
+
# index and Process::Status are passed as arguments.
|
|
833
|
+
#
|
|
834
|
+
# @note Cluster mode only.
|
|
835
|
+
#
|
|
836
|
+
# @example
|
|
837
|
+
# after_worker_shutdown do |worker_handle|
|
|
838
|
+
# puts 'Worker crashed' unless worker_handle.process_status.success?
|
|
839
|
+
# end
|
|
840
|
+
#
|
|
841
|
+
def after_worker_shutdown(&block)
|
|
842
|
+
process_hook :after_worker_shutdown, nil, block, cluster_only: true
|
|
843
|
+
end
|
|
844
|
+
|
|
821
845
|
# Code to run after puma is booted (works for both single and cluster modes).
|
|
822
846
|
#
|
|
823
847
|
# @example
|
|
@@ -980,6 +1004,7 @@ module Puma
|
|
|
980
1004
|
# The default is +true+ if your app uses more than 1 worker.
|
|
981
1005
|
#
|
|
982
1006
|
# @note Cluster mode only.
|
|
1007
|
+
# @note When using `fork_worker`, this only applies to worker 0.
|
|
983
1008
|
#
|
|
984
1009
|
# @example
|
|
985
1010
|
# preload_app!
|
|
@@ -1207,13 +1232,19 @@ module Puma
|
|
|
1207
1232
|
end
|
|
1208
1233
|
|
|
1209
1234
|
|
|
1210
|
-
#
|
|
1211
|
-
#
|
|
1235
|
+
# Maximum delay of worker accept loop.
|
|
1236
|
+
#
|
|
1237
|
+
# Attempts to route traffic to less-busy workers by causing a busy worker to delay
|
|
1238
|
+
# listening on the socket, allowing workers which are not processing as many
|
|
1212
1239
|
# requests to pick up new requests first.
|
|
1213
1240
|
#
|
|
1214
1241
|
# The default is 0.005 seconds.
|
|
1215
1242
|
#
|
|
1216
|
-
#
|
|
1243
|
+
# To turn off this feature, set the value to 0.
|
|
1244
|
+
#
|
|
1245
|
+
# @note Cluster mode with >= 2 workers only.
|
|
1246
|
+
#
|
|
1247
|
+
# @note Interpreters with forking support only.
|
|
1217
1248
|
#
|
|
1218
1249
|
# @see Puma::Server#handle_servers
|
|
1219
1250
|
# @see Puma::ThreadPool#wait_for_less_busy_worker
|
|
@@ -1357,7 +1388,7 @@ module Puma
|
|
|
1357
1388
|
#
|
|
1358
1389
|
# The default is +:auto+.
|
|
1359
1390
|
#
|
|
1360
|
-
# @see https://github.com/socketry/nio4r/blob/
|
|
1391
|
+
# @see https://github.com/socketry/nio4r/blob/main/lib/nio/selector.rb
|
|
1361
1392
|
#
|
|
1362
1393
|
def io_selector_backend(backend)
|
|
1363
1394
|
@options[:io_selector_backend] = backend.to_sym
|
data/lib/puma/launcher.rb
CHANGED
|
@@ -42,26 +42,40 @@ module Puma
|
|
|
42
42
|
# end
|
|
43
43
|
# Puma::Launcher.new(conf, log_writer: Puma::LogWriter.stdio).run
|
|
44
44
|
def initialize(conf, launcher_args={})
|
|
45
|
-
|
|
46
|
-
@log_writer = launcher_args[:log_writer] || LogWriter::DEFAULT
|
|
47
|
-
@events = launcher_args[:events] || Events.new
|
|
48
|
-
@argv = launcher_args[:argv] || []
|
|
49
|
-
@original_argv = @argv.dup
|
|
50
|
-
@config = conf
|
|
51
|
-
|
|
52
|
-
env = launcher_args.delete(:env) || ENV
|
|
45
|
+
## Minimal initialization before potential early restart (e.g. from bundle pruning)
|
|
53
46
|
|
|
47
|
+
@config = conf
|
|
48
|
+
# Advertise the CLI Configuration before config files are loaded
|
|
49
|
+
Puma.cli_config = @config if defined?(Puma.cli_config)
|
|
54
50
|
@config.clamp
|
|
51
|
+
|
|
55
52
|
@options = @config.options
|
|
56
53
|
|
|
54
|
+
@log_writer = launcher_args[:log_writer] || LogWriter::DEFAULT
|
|
55
|
+
@log_writer.formatter = LogWriter::PidFormatter.new if clustered?
|
|
56
|
+
@log_writer.formatter = @options[:log_formatter] if @options[:log_formatter]
|
|
57
|
+
@log_writer.custom_logger = @options[:custom_logger] if @options[:custom_logger]
|
|
57
58
|
@options[:log_writer] = @log_writer
|
|
58
59
|
@options[:logger] = @log_writer if clustered?
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
@events = launcher_args[:events] || Events.new
|
|
62
|
+
|
|
63
|
+
@argv = launcher_args[:argv] || []
|
|
64
|
+
@original_argv = @argv.dup
|
|
65
|
+
|
|
66
|
+
## End minimal initialization
|
|
67
|
+
|
|
68
|
+
generate_restart_data
|
|
69
|
+
Dir.chdir(@restart_dir)
|
|
70
|
+
|
|
71
|
+
prune_bundler!
|
|
72
|
+
|
|
73
|
+
env = launcher_args.delete(:env) || ENV
|
|
74
|
+
|
|
75
|
+
# Log after prune_bundler! to avoid duplicate logging if a restart occurs
|
|
62
76
|
log_config if env['PUMA_LOG_CONFIG']
|
|
63
77
|
|
|
64
|
-
@binder
|
|
78
|
+
@binder = Binder.new(@log_writer, @options)
|
|
65
79
|
@binder.create_inherited_fds(env).each { |k| env.delete k }
|
|
66
80
|
@binder.create_activated_fds(env).each { |k| env.delete k }
|
|
67
81
|
|
|
@@ -81,21 +95,10 @@ module Puma
|
|
|
81
95
|
)
|
|
82
96
|
end
|
|
83
97
|
|
|
84
|
-
@log_writer.formatter = LogWriter::PidFormatter.new if clustered?
|
|
85
|
-
@log_writer.formatter = @options[:log_formatter] if @options[:log_formatter]
|
|
86
|
-
|
|
87
|
-
@log_writer.custom_logger = @options[:custom_logger] if @options[:custom_logger]
|
|
88
|
-
|
|
89
|
-
generate_restart_data
|
|
90
|
-
|
|
91
98
|
if clustered? && !Puma.forkable?
|
|
92
99
|
unsupported "worker mode not supported on #{RUBY_ENGINE} on this platform"
|
|
93
100
|
end
|
|
94
101
|
|
|
95
|
-
Dir.chdir(@restart_dir)
|
|
96
|
-
|
|
97
|
-
prune_bundler!
|
|
98
|
-
|
|
99
102
|
@environment = @options[:environment] if @options[:environment]
|
|
100
103
|
set_rack_environment
|
|
101
104
|
|
|
@@ -139,7 +142,10 @@ module Puma
|
|
|
139
142
|
# Delete the configured pidfile
|
|
140
143
|
def delete_pidfile
|
|
141
144
|
path = @options[:pidfile]
|
|
142
|
-
|
|
145
|
+
begin
|
|
146
|
+
File.unlink(path) if path
|
|
147
|
+
rescue Errno::ENOENT
|
|
148
|
+
end
|
|
143
149
|
end
|
|
144
150
|
|
|
145
151
|
# Begin async shutdown of the server
|
|
@@ -381,9 +387,9 @@ module Puma
|
|
|
381
387
|
# using it.
|
|
382
388
|
@restart_dir = Dir.pwd
|
|
383
389
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
390
|
+
# Use the same trick as unicorn, namely favor PWD because
|
|
391
|
+
# it will contain an unresolved symlink, useful for when
|
|
392
|
+
# the pwd is /data/releases/current.
|
|
387
393
|
elsif dir = ENV['PWD']
|
|
388
394
|
s_env = File.stat(dir)
|
|
389
395
|
s_pwd = File.stat(Dir.pwd)
|
data/lib/puma/reactor.rb
CHANGED
|
@@ -75,15 +75,12 @@ module Puma
|
|
|
75
75
|
private
|
|
76
76
|
|
|
77
77
|
def select_loop
|
|
78
|
-
close_selector = true
|
|
79
78
|
begin
|
|
80
79
|
until @input.closed? && @input.empty?
|
|
81
80
|
# Wakeup any registered object that receives incoming data.
|
|
82
81
|
# Block until the earliest timeout or Selector#wakeup is called.
|
|
83
82
|
timeout = (earliest = @timeouts.first) && earliest.timeout
|
|
84
|
-
monitor_wake_up = false
|
|
85
83
|
@selector.select(timeout) do |monitor|
|
|
86
|
-
monitor_wake_up = true
|
|
87
84
|
wakeup!(monitor.value)
|
|
88
85
|
end
|
|
89
86
|
|
|
@@ -103,18 +100,12 @@ module Puma
|
|
|
103
100
|
STDERR.puts "Error in reactor loop escaped: #{e.message} (#{e.class})"
|
|
104
101
|
STDERR.puts e.backtrace
|
|
105
102
|
|
|
106
|
-
|
|
107
|
-
# is odd. Regardless, it may continue for thousands of calls if retried.
|
|
108
|
-
# Also, when it raises, @selector.close also raises an error.
|
|
109
|
-
if !monitor_wake_up && NoMethodError === e
|
|
110
|
-
close_selector = false
|
|
111
|
-
else
|
|
112
|
-
retry
|
|
113
|
-
end
|
|
103
|
+
retry
|
|
114
104
|
end
|
|
105
|
+
|
|
115
106
|
# Wakeup all remaining objects on shutdown.
|
|
116
107
|
@timeouts.each(&@block)
|
|
117
|
-
@selector.close
|
|
108
|
+
@selector.close
|
|
118
109
|
end
|
|
119
110
|
|
|
120
111
|
# Start monitoring the object.
|
data/lib/puma/request.rb
CHANGED
|
@@ -36,17 +36,19 @@ module Puma
|
|
|
36
36
|
# Takes the request contained in +client+, invokes the Rack application to construct
|
|
37
37
|
# the response and writes it back to +client.io+.
|
|
38
38
|
#
|
|
39
|
-
# It'll return +
|
|
39
|
+
# It'll return +:close+ when the connection is closed, this doesn't mean
|
|
40
40
|
# that the response wasn't successful.
|
|
41
41
|
#
|
|
42
|
+
# It'll return +:keep_alive+ if the connection is a pipeline or keep-alive connection.
|
|
43
|
+
# Which may contain additional requests.
|
|
44
|
+
#
|
|
42
45
|
# It'll return +:async+ if the connection remains open but will be handled
|
|
43
46
|
# elsewhere, i.e. the connection has been hijacked by the Rack application.
|
|
44
47
|
#
|
|
45
48
|
# Finally, it'll return +true+ on keep-alive connections.
|
|
46
49
|
# @param client [Puma::Client]
|
|
47
50
|
# @param requests [Integer]
|
|
48
|
-
# @return [
|
|
49
|
-
#
|
|
51
|
+
# @return [:close, :keep_alive, :async]
|
|
50
52
|
def handle_request(client, requests)
|
|
51
53
|
env = client.env
|
|
52
54
|
io_buffer = client.io_buffer
|
|
@@ -54,7 +56,7 @@ module Puma
|
|
|
54
56
|
app_body = nil
|
|
55
57
|
error = nil
|
|
56
58
|
|
|
57
|
-
return
|
|
59
|
+
return :close if closed_socket?(socket)
|
|
58
60
|
|
|
59
61
|
if client.http_content_length_limit_exceeded
|
|
60
62
|
return prepare_response(413, {}, ["Payload Too Large"], requests, client)
|
|
@@ -167,13 +169,13 @@ module Puma
|
|
|
167
169
|
# a call to `Server#lowlevel_error`
|
|
168
170
|
# @param requests [Integer] number of inline requests handled
|
|
169
171
|
# @param client [Puma::Client]
|
|
170
|
-
# @return [
|
|
172
|
+
# @return [:close, :keep_alive, :async]
|
|
171
173
|
def prepare_response(status, headers, res_body, requests, client)
|
|
172
174
|
env = client.env
|
|
173
175
|
socket = client.io
|
|
174
176
|
io_buffer = client.io_buffer
|
|
175
177
|
|
|
176
|
-
return
|
|
178
|
+
return :close if closed_socket?(socket)
|
|
177
179
|
|
|
178
180
|
# Close the connection after a reasonable number of inline requests
|
|
179
181
|
force_keep_alive = @enable_keep_alives && client.requests_served < @max_keep_alive
|
|
@@ -244,7 +246,7 @@ module Puma
|
|
|
244
246
|
io_buffer << LINE_END
|
|
245
247
|
fast_write_str socket, io_buffer.read_and_reset
|
|
246
248
|
socket.flush
|
|
247
|
-
return keep_alive
|
|
249
|
+
return keep_alive ? :keep_alive : :close
|
|
248
250
|
end
|
|
249
251
|
else
|
|
250
252
|
if content_length
|
|
@@ -270,7 +272,7 @@ module Puma
|
|
|
270
272
|
fast_write_response socket, body, io_buffer, chunked, content_length.to_i
|
|
271
273
|
body.close if close_body
|
|
272
274
|
# if we're shutting down, close keep_alive connections
|
|
273
|
-
!shutting_down? && keep_alive
|
|
275
|
+
!shutting_down? && keep_alive ? :keep_alive : :close
|
|
274
276
|
end
|
|
275
277
|
|
|
276
278
|
HTTP_ON_VALUES = { "on" => true, HTTPS => true }
|
data/lib/puma/runner.rb
CHANGED
|
@@ -70,7 +70,7 @@ module Puma
|
|
|
70
70
|
token = nil if token.empty? || token == 'none'
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
-
app = Puma::App::Status.new @launcher, token
|
|
73
|
+
app = Puma::App::Status.new @launcher, token: token, data_only: @options[:control_data_only]
|
|
74
74
|
|
|
75
75
|
# A Reactor is not created and nio4r is not loaded when 'queue_requests: false'
|
|
76
76
|
# Use `nil` for events, no hooks in control server
|