puma 6.6.0 → 7.0.3
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 +131 -5
- data/README.md +18 -31
- data/docs/fork_worker.md +5 -5
- data/docs/kubernetes.md +6 -4
- data/docs/restart.md +2 -2
- data/docs/signals.md +11 -11
- data/docs/stats.md +2 -1
- data/docs/systemd.md +1 -1
- data/ext/puma_http11/extconf.rb +2 -17
- data/ext/puma_http11/mini_ssl.c +0 -5
- data/ext/puma_http11/org/jruby/puma/Http11.java +1 -1
- data/lib/puma/binder.rb +10 -8
- data/lib/puma/cli.rb +3 -5
- data/lib/puma/client.rb +74 -36
- data/lib/puma/cluster/worker.rb +9 -10
- data/lib/puma/cluster/worker_handle.rb +36 -5
- data/lib/puma/cluster.rb +35 -22
- data/lib/puma/commonlogger.rb +3 -3
- data/lib/puma/configuration.rb +88 -43
- data/lib/puma/const.rb +9 -10
- data/lib/puma/control_cli.rb +6 -2
- data/lib/puma/detect.rb +2 -0
- data/lib/puma/dsl.rb +110 -90
- data/lib/puma/error_logger.rb +3 -1
- data/lib/puma/events.rb +25 -10
- data/lib/puma/io_buffer.rb +8 -4
- data/lib/puma/launcher/bundle_pruner.rb +1 -1
- data/lib/puma/launcher.rb +28 -29
- data/lib/puma/minissl.rb +0 -1
- data/lib/puma/plugin/systemd.rb +3 -3
- data/lib/puma/rack/urlmap.rb +1 -1
- data/lib/puma/reactor.rb +19 -4
- data/lib/puma/request.rb +45 -32
- data/lib/puma/runner.rb +8 -17
- data/lib/puma/server.rb +80 -48
- data/lib/puma/single.rb +5 -2
- data/lib/puma/thread_pool.rb +37 -81
- data/lib/puma/util.rb +0 -7
- data/lib/puma.rb +10 -0
- data/lib/rack/handler/puma.rb +2 -2
- data/tools/Dockerfile +3 -1
- metadata +4 -4
data/lib/puma/configuration.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require_relative 'plugin'
|
4
4
|
require_relative 'const'
|
5
5
|
require_relative 'dsl'
|
6
|
+
require_relative 'events'
|
6
7
|
|
7
8
|
module Puma
|
8
9
|
# A class used for storing "leveled" configuration options.
|
@@ -112,7 +113,7 @@ module Puma
|
|
112
113
|
# config = Configuration.new({}) do |user_config, file_config, default_config|
|
113
114
|
# user_config.port 3003
|
114
115
|
# end
|
115
|
-
# config.
|
116
|
+
# config.clamp
|
116
117
|
# puts config.options[:port]
|
117
118
|
# # => 3003
|
118
119
|
#
|
@@ -125,10 +126,13 @@ module Puma
|
|
125
126
|
# is done because an environment variable may have been modified while loading
|
126
127
|
# configuration files.
|
127
128
|
class Configuration
|
129
|
+
class NotLoadedError < StandardError; end
|
130
|
+
class NotClampedError < StandardError; end
|
131
|
+
|
128
132
|
DEFAULTS = {
|
129
133
|
auto_trim_time: 30,
|
130
134
|
binds: ['tcp://0.0.0.0:9292'.freeze],
|
131
|
-
|
135
|
+
fiber_per_request: !!ENV.fetch("PUMA_FIBER_PER_REQUEST", false),
|
132
136
|
debug: false,
|
133
137
|
enable_keep_alives: true,
|
134
138
|
early_hints: nil,
|
@@ -140,19 +144,17 @@ module Puma
|
|
140
144
|
io_selector_backend: :auto,
|
141
145
|
log_requests: false,
|
142
146
|
logger: STDOUT,
|
143
|
-
#
|
144
|
-
#
|
145
|
-
#
|
146
|
-
|
147
|
-
# well behaved client from monopolizing the thread forever.
|
148
|
-
max_fast_inline: 10,
|
147
|
+
# Limits how many requests a keep alive connection can make.
|
148
|
+
# The connection will be closed after it reaches `max_keep_alive`
|
149
|
+
# requests.
|
150
|
+
max_keep_alive: 999,
|
149
151
|
max_threads: Puma.mri? ? 5 : 16,
|
150
152
|
min_threads: 0,
|
151
153
|
mode: :http,
|
152
154
|
mutate_stdout_and_stderr_to_sync_on_write: true,
|
153
155
|
out_of_band: [],
|
154
156
|
# Number of seconds for another request within a persistent session.
|
155
|
-
persistent_timeout:
|
157
|
+
persistent_timeout: 65, # PUMA_PERSISTENT_TIMEOUT
|
156
158
|
queue_requests: true,
|
157
159
|
rackup: 'config.ru'.freeze,
|
158
160
|
raise_exception_on_sigterm: true,
|
@@ -176,24 +178,31 @@ module Puma
|
|
176
178
|
def initialize(user_options={}, default_options = {}, env = ENV, &block)
|
177
179
|
default_options = self.puma_default_options(env).merge(default_options)
|
178
180
|
|
179
|
-
@
|
181
|
+
@_options = UserFileDefaultOptions.new(user_options, default_options)
|
180
182
|
@plugins = PluginLoader.new
|
181
|
-
@
|
182
|
-
@
|
183
|
-
@
|
184
|
-
|
185
|
-
|
186
|
-
default_options[:preload_app] = (@options[:workers] > 1) && Puma.forkable?
|
187
|
-
end
|
183
|
+
@events = @_options[:events] || Events.new
|
184
|
+
@hooks = {}
|
185
|
+
@user_dsl = DSL.new(@_options.user_options, self)
|
186
|
+
@file_dsl = DSL.new(@_options.file_options, self)
|
187
|
+
@default_dsl = DSL.new(@_options.default_options, self)
|
188
188
|
|
189
189
|
@puma_bundler_pruned = env.key? 'PUMA_BUNDLER_PRUNED'
|
190
190
|
|
191
191
|
if block
|
192
192
|
configure(&block)
|
193
193
|
end
|
194
|
+
|
195
|
+
@loaded = false
|
196
|
+
@clamped = false
|
194
197
|
end
|
195
198
|
|
196
|
-
attr_reader :
|
199
|
+
attr_reader :plugins, :events, :hooks
|
200
|
+
|
201
|
+
def options
|
202
|
+
raise NotClampedError, "ensure clamp is called before accessing options" unless @clamped
|
203
|
+
|
204
|
+
@_options
|
205
|
+
end
|
197
206
|
|
198
207
|
def configure
|
199
208
|
yield @user_dsl, @file_dsl, @default_dsl
|
@@ -206,7 +215,7 @@ module Puma
|
|
206
215
|
def initialize_copy(other)
|
207
216
|
@conf = nil
|
208
217
|
@cli_options = nil
|
209
|
-
@
|
218
|
+
@_options = @_options.dup
|
210
219
|
end
|
211
220
|
|
212
221
|
def flatten
|
@@ -214,7 +223,7 @@ module Puma
|
|
214
223
|
end
|
215
224
|
|
216
225
|
def flatten!
|
217
|
-
@
|
226
|
+
@_options = @_options.flatten
|
218
227
|
self
|
219
228
|
end
|
220
229
|
|
@@ -227,6 +236,7 @@ module Puma
|
|
227
236
|
def puma_options_from_env(env = ENV)
|
228
237
|
min = env['PUMA_MIN_THREADS'] || env['MIN_THREADS']
|
229
238
|
max = env['PUMA_MAX_THREADS'] || env['MAX_THREADS']
|
239
|
+
persistent_timeout = env['PUMA_PERSISTENT_TIMEOUT']
|
230
240
|
workers = if env['WEB_CONCURRENCY'] == 'auto'
|
231
241
|
require_processor_counter
|
232
242
|
::Concurrent.available_processor_count
|
@@ -237,24 +247,27 @@ module Puma
|
|
237
247
|
{
|
238
248
|
min_threads: min && min != "" && Integer(min),
|
239
249
|
max_threads: max && max != "" && Integer(max),
|
250
|
+
persistent_timeout: persistent_timeout && persistent_timeout != "" && Integer(persistent_timeout),
|
240
251
|
workers: workers && workers != "" && Integer(workers),
|
241
252
|
environment: env['APP_ENV'] || env['RACK_ENV'] || env['RAILS_ENV'],
|
242
253
|
}
|
243
254
|
end
|
244
255
|
|
245
256
|
def load
|
257
|
+
@loaded = true
|
246
258
|
config_files.each { |config_file| @file_dsl._load_from(config_file) }
|
247
|
-
|
248
|
-
@options
|
259
|
+
@_options
|
249
260
|
end
|
250
261
|
|
251
262
|
def config_files
|
252
|
-
|
263
|
+
raise NotLoadedError, "ensure load is called before accessing config_files" unless @loaded
|
264
|
+
|
265
|
+
files = @_options.all_of(:config_files)
|
253
266
|
|
254
267
|
return [] if files == ['-']
|
255
268
|
return files if files.any?
|
256
269
|
|
257
|
-
first_default_file = %W(config/puma/#{@
|
270
|
+
first_default_file = %W(config/puma/#{@_options[:environment]}.rb config/puma.rb).find do |f|
|
258
271
|
File.exist?(f)
|
259
272
|
end
|
260
273
|
|
@@ -262,9 +275,16 @@ module Puma
|
|
262
275
|
end
|
263
276
|
|
264
277
|
# Call once all configuration (included from rackup files)
|
265
|
-
# is loaded to
|
278
|
+
# is loaded to finalize defaults and lock in the configuration.
|
279
|
+
#
|
280
|
+
# This also calls load if it hasn't been called yet.
|
266
281
|
def clamp
|
267
|
-
@
|
282
|
+
load unless @loaded
|
283
|
+
set_conditional_default_options
|
284
|
+
@_options.finalize_values
|
285
|
+
@clamped = true
|
286
|
+
warn_hooks
|
287
|
+
options
|
268
288
|
end
|
269
289
|
|
270
290
|
# Injects the Configuration object into the env
|
@@ -283,11 +303,11 @@ module Puma
|
|
283
303
|
# Indicate if there is a properly configured app
|
284
304
|
#
|
285
305
|
def app_configured?
|
286
|
-
|
306
|
+
options[:app] || File.exist?(rackup)
|
287
307
|
end
|
288
308
|
|
289
309
|
def rackup
|
290
|
-
|
310
|
+
options[:rackup]
|
291
311
|
end
|
292
312
|
|
293
313
|
# Load the specified rackup file, pull options from
|
@@ -296,9 +316,9 @@ module Puma
|
|
296
316
|
def app
|
297
317
|
found = options[:app] || load_rackup
|
298
318
|
|
299
|
-
if
|
319
|
+
if options[:log_requests]
|
300
320
|
require_relative 'commonlogger'
|
301
|
-
logger =
|
321
|
+
logger = options[:custom_logger] ? options[:custom_logger] : options[:logger]
|
302
322
|
found = CommonLogger.new(found, logger)
|
303
323
|
end
|
304
324
|
|
@@ -307,7 +327,7 @@ module Puma
|
|
307
327
|
|
308
328
|
# Return which environment we're running in
|
309
329
|
def environment
|
310
|
-
|
330
|
+
options[:environment]
|
311
331
|
end
|
312
332
|
|
313
333
|
def load_plugin(name)
|
@@ -315,18 +335,19 @@ module Puma
|
|
315
335
|
end
|
316
336
|
|
317
337
|
# @param key [:Symbol] hook to run
|
318
|
-
# @param arg [Launcher, Int] `:
|
338
|
+
# @param arg [Launcher, Int] `:before_restart` passes Launcher
|
319
339
|
#
|
320
340
|
def run_hooks(key, arg, log_writer, hook_data = nil)
|
321
341
|
log_writer.debug "Running #{key} hooks"
|
322
342
|
|
323
|
-
|
343
|
+
options.all_of(key).each do |hook_options|
|
324
344
|
begin
|
325
|
-
|
326
|
-
|
327
|
-
|
345
|
+
block = hook_options[:block]
|
346
|
+
if id = hook_options[:id]
|
347
|
+
hook_data[id] ||= Hash.new
|
348
|
+
block.call arg, hook_data[id]
|
328
349
|
else
|
329
|
-
|
350
|
+
block.call arg
|
330
351
|
end
|
331
352
|
rescue => e
|
332
353
|
log_writer.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
|
@@ -336,7 +357,7 @@ module Puma
|
|
336
357
|
end
|
337
358
|
|
338
359
|
def final_options
|
339
|
-
|
360
|
+
options.final_options
|
340
361
|
end
|
341
362
|
|
342
363
|
def self.temp_path
|
@@ -346,6 +367,12 @@ module Puma
|
|
346
367
|
"#{Dir.tmpdir}/puma-status-#{t}-#{$$}"
|
347
368
|
end
|
348
369
|
|
370
|
+
def self.random_token
|
371
|
+
require 'securerandom' unless defined?(SecureRandom)
|
372
|
+
|
373
|
+
SecureRandom.hex(16)
|
374
|
+
end
|
375
|
+
|
349
376
|
private
|
350
377
|
|
351
378
|
def require_processor_counter
|
@@ -386,22 +413,40 @@ module Puma
|
|
386
413
|
rack_app, rack_options = rack_builder.parse_file(rackup)
|
387
414
|
rack_options = rack_options || {}
|
388
415
|
|
389
|
-
|
416
|
+
options.file_options.merge!(rack_options)
|
390
417
|
|
391
418
|
config_ru_binds = []
|
392
419
|
rack_options.each do |k, v|
|
393
420
|
config_ru_binds << v if k.to_s.start_with?("bind")
|
394
421
|
end
|
395
422
|
|
396
|
-
|
423
|
+
options.file_options[:binds] = config_ru_binds unless config_ru_binds.empty?
|
397
424
|
|
398
425
|
rack_app
|
399
426
|
end
|
400
427
|
|
401
|
-
def
|
402
|
-
|
428
|
+
def set_conditional_default_options
|
429
|
+
@_options.default_options[:preload_app] = !@_options[:prune_bundler] &&
|
430
|
+
(@_options[:workers] > 1) && Puma.forkable?
|
431
|
+
end
|
403
432
|
|
404
|
-
|
433
|
+
def warn_hooks
|
434
|
+
return if options[:workers] > 0
|
435
|
+
return if options[:silence_fork_callback_warning]
|
436
|
+
|
437
|
+
log_writer = LogWriter.stdio
|
438
|
+
@hooks.each_key do |hook|
|
439
|
+
options.all_of(hook).each do |hook_options|
|
440
|
+
next unless hook_options[:cluster_only]
|
441
|
+
|
442
|
+
log_writer.log(<<~MSG.tr("\n", " "))
|
443
|
+
Warning: The code in the `#{hook}` block will not execute
|
444
|
+
in the current Puma configuration. The `#{hook}` block only
|
445
|
+
executes in Puma's cluster mode. To fix this, either remove the
|
446
|
+
`#{hook}` call or increase Puma's worker count above zero.
|
447
|
+
MSG
|
448
|
+
end
|
449
|
+
end
|
405
450
|
end
|
406
451
|
end
|
407
452
|
end
|
data/lib/puma/const.rb
CHANGED
@@ -100,13 +100,11 @@ module Puma
|
|
100
100
|
# too taxing on performance.
|
101
101
|
module Const
|
102
102
|
|
103
|
-
PUMA_VERSION = VERSION = "
|
104
|
-
CODE_NAME = "
|
103
|
+
PUMA_VERSION = VERSION = "7.0.3"
|
104
|
+
CODE_NAME = "Romantic Warrior"
|
105
105
|
|
106
106
|
PUMA_SERVER_STRING = ["puma", PUMA_VERSION, CODE_NAME].join(" ").freeze
|
107
107
|
|
108
|
-
FAST_TRACK_KA_TIMEOUT = 0.2
|
109
|
-
|
110
108
|
# How long to wait when getting some write blocking on the socket when
|
111
109
|
# sending data back
|
112
110
|
WRITE_TIMEOUT = 10
|
@@ -125,9 +123,9 @@ module Puma
|
|
125
123
|
# Indicate that we couldn't parse the request
|
126
124
|
400 => "HTTP/1.1 400 Bad Request\r\n\r\n",
|
127
125
|
# The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
|
128
|
-
404 => "HTTP/1.1 404 Not Found\r\
|
126
|
+
404 => "HTTP/1.1 404 Not Found\r\nconnection: close\r\n\r\n",
|
129
127
|
# The standard empty 408 response for requests that timed out.
|
130
|
-
408 => "HTTP/1.1 408 Request Timeout\r\
|
128
|
+
408 => "HTTP/1.1 408 Request Timeout\r\nconnection: close\r\n\r\n",
|
131
129
|
# Indicate that there was an internal error, obviously.
|
132
130
|
500 => "HTTP/1.1 500 Internal Server Error\r\n\r\n",
|
133
131
|
# Incorrect or invalid header value
|
@@ -230,6 +228,7 @@ module Puma
|
|
230
228
|
RACK_INPUT = "rack.input"
|
231
229
|
RACK_URL_SCHEME = "rack.url_scheme"
|
232
230
|
RACK_AFTER_REPLY = "rack.after_reply"
|
231
|
+
RACK_RESPONSE_FINISHED = "rack.response_finished"
|
233
232
|
PUMA_SOCKET = "puma.socket"
|
234
233
|
PUMA_CONFIG = "puma.config"
|
235
234
|
PUMA_PEERCERT = "puma.peercert"
|
@@ -252,14 +251,14 @@ module Puma
|
|
252
251
|
KEEP_ALIVE = "keep-alive"
|
253
252
|
|
254
253
|
CONTENT_LENGTH2 = "content-length"
|
255
|
-
CONTENT_LENGTH_S = "
|
254
|
+
CONTENT_LENGTH_S = "content-length: "
|
256
255
|
TRANSFER_ENCODING = "transfer-encoding"
|
257
256
|
TRANSFER_ENCODING2 = "HTTP_TRANSFER_ENCODING"
|
258
257
|
|
259
|
-
CONNECTION_CLOSE = "
|
260
|
-
CONNECTION_KEEP_ALIVE = "
|
258
|
+
CONNECTION_CLOSE = "connection: close\r\n"
|
259
|
+
CONNECTION_KEEP_ALIVE = "connection: keep-alive\r\n"
|
261
260
|
|
262
|
-
TRANSFER_ENCODING_CHUNKED = "
|
261
|
+
TRANSFER_ENCODING_CHUNKED = "transfer-encoding: chunked\r\n"
|
263
262
|
CLOSE_CHUNKED = "0\r\n\r\n"
|
264
263
|
|
265
264
|
CHUNKED = "chunked"
|
data/lib/puma/control_cli.rb
CHANGED
@@ -124,11 +124,15 @@ module Puma
|
|
124
124
|
end
|
125
125
|
|
126
126
|
if @config_file
|
127
|
+
# needed because neither `Puma::CLI` or `Puma::Server` are loaded
|
128
|
+
require_relative '../puma'
|
129
|
+
|
127
130
|
require_relative 'configuration'
|
128
131
|
require_relative 'log_writer'
|
129
132
|
|
130
133
|
config = Puma::Configuration.new({ config_files: [@config_file] }, {} , env)
|
131
|
-
config.
|
134
|
+
config.clamp
|
135
|
+
|
132
136
|
@state ||= config.options[:state]
|
133
137
|
@control_url ||= config.options[:control_url]
|
134
138
|
@control_auth_token ||= config.options[:control_auth_token]
|
@@ -248,7 +252,7 @@ module Puma
|
|
248
252
|
@stdout.flush unless @stdout.sync
|
249
253
|
return
|
250
254
|
elsif sig.start_with? 'SIG'
|
251
|
-
if Signal.list.key? sig.
|
255
|
+
if Signal.list.key? sig.delete_prefix('SIG')
|
252
256
|
Process.kill sig, @pid
|
253
257
|
else
|
254
258
|
raise "Signal '#{sig}' not available'"
|