puma 6.6.0 → 7.0.1
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 +110 -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 +34 -5
- data/lib/puma/cluster.rb +35 -22
- data/lib/puma/commonlogger.rb +3 -3
- data/lib/puma/configuration.rb +86 -43
- data/lib/puma/const.rb +9 -10
- data/lib/puma/control_cli.rb +3 -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 +38 -31
- 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 +27 -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: ENV.fetch('PUMA_PERSISTENT_TIMEOUT', 65),
|
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
|
|
@@ -243,18 +252,20 @@ module Puma
|
|
243
252
|
end
|
244
253
|
|
245
254
|
def load
|
255
|
+
@loaded = true
|
246
256
|
config_files.each { |config_file| @file_dsl._load_from(config_file) }
|
247
|
-
|
248
|
-
@options
|
257
|
+
@_options
|
249
258
|
end
|
250
259
|
|
251
260
|
def config_files
|
252
|
-
|
261
|
+
raise NotLoadedError, "ensure load is called before accessing config_files" unless @loaded
|
262
|
+
|
263
|
+
files = @_options.all_of(:config_files)
|
253
264
|
|
254
265
|
return [] if files == ['-']
|
255
266
|
return files if files.any?
|
256
267
|
|
257
|
-
first_default_file = %W(config/puma/#{@
|
268
|
+
first_default_file = %W(config/puma/#{@_options[:environment]}.rb config/puma.rb).find do |f|
|
258
269
|
File.exist?(f)
|
259
270
|
end
|
260
271
|
|
@@ -262,9 +273,16 @@ module Puma
|
|
262
273
|
end
|
263
274
|
|
264
275
|
# Call once all configuration (included from rackup files)
|
265
|
-
# is loaded to
|
276
|
+
# is loaded to finalize defaults and lock in the configuration.
|
277
|
+
#
|
278
|
+
# This also calls load if it hasn't been called yet.
|
266
279
|
def clamp
|
267
|
-
@
|
280
|
+
load unless @loaded
|
281
|
+
set_conditional_default_options
|
282
|
+
@_options.finalize_values
|
283
|
+
@clamped = true
|
284
|
+
warn_hooks
|
285
|
+
options
|
268
286
|
end
|
269
287
|
|
270
288
|
# Injects the Configuration object into the env
|
@@ -283,11 +301,11 @@ module Puma
|
|
283
301
|
# Indicate if there is a properly configured app
|
284
302
|
#
|
285
303
|
def app_configured?
|
286
|
-
|
304
|
+
options[:app] || File.exist?(rackup)
|
287
305
|
end
|
288
306
|
|
289
307
|
def rackup
|
290
|
-
|
308
|
+
options[:rackup]
|
291
309
|
end
|
292
310
|
|
293
311
|
# Load the specified rackup file, pull options from
|
@@ -296,9 +314,9 @@ module Puma
|
|
296
314
|
def app
|
297
315
|
found = options[:app] || load_rackup
|
298
316
|
|
299
|
-
if
|
317
|
+
if options[:log_requests]
|
300
318
|
require_relative 'commonlogger'
|
301
|
-
logger =
|
319
|
+
logger = options[:custom_logger] ? options[:custom_logger] : options[:logger]
|
302
320
|
found = CommonLogger.new(found, logger)
|
303
321
|
end
|
304
322
|
|
@@ -307,7 +325,7 @@ module Puma
|
|
307
325
|
|
308
326
|
# Return which environment we're running in
|
309
327
|
def environment
|
310
|
-
|
328
|
+
options[:environment]
|
311
329
|
end
|
312
330
|
|
313
331
|
def load_plugin(name)
|
@@ -315,18 +333,19 @@ module Puma
|
|
315
333
|
end
|
316
334
|
|
317
335
|
# @param key [:Symbol] hook to run
|
318
|
-
# @param arg [Launcher, Int] `:
|
336
|
+
# @param arg [Launcher, Int] `:before_restart` passes Launcher
|
319
337
|
#
|
320
338
|
def run_hooks(key, arg, log_writer, hook_data = nil)
|
321
339
|
log_writer.debug "Running #{key} hooks"
|
322
340
|
|
323
|
-
|
341
|
+
options.all_of(key).each do |hook_options|
|
324
342
|
begin
|
325
|
-
|
326
|
-
|
327
|
-
|
343
|
+
block = hook_options[:block]
|
344
|
+
if id = hook_options[:id]
|
345
|
+
hook_data[id] ||= Hash.new
|
346
|
+
block.call arg, hook_data[id]
|
328
347
|
else
|
329
|
-
|
348
|
+
block.call arg
|
330
349
|
end
|
331
350
|
rescue => e
|
332
351
|
log_writer.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
|
@@ -336,7 +355,7 @@ module Puma
|
|
336
355
|
end
|
337
356
|
|
338
357
|
def final_options
|
339
|
-
|
358
|
+
options.final_options
|
340
359
|
end
|
341
360
|
|
342
361
|
def self.temp_path
|
@@ -346,6 +365,12 @@ module Puma
|
|
346
365
|
"#{Dir.tmpdir}/puma-status-#{t}-#{$$}"
|
347
366
|
end
|
348
367
|
|
368
|
+
def self.random_token
|
369
|
+
require 'securerandom' unless defined?(SecureRandom)
|
370
|
+
|
371
|
+
SecureRandom.hex(16)
|
372
|
+
end
|
373
|
+
|
349
374
|
private
|
350
375
|
|
351
376
|
def require_processor_counter
|
@@ -386,22 +411,40 @@ module Puma
|
|
386
411
|
rack_app, rack_options = rack_builder.parse_file(rackup)
|
387
412
|
rack_options = rack_options || {}
|
388
413
|
|
389
|
-
|
414
|
+
options.file_options.merge!(rack_options)
|
390
415
|
|
391
416
|
config_ru_binds = []
|
392
417
|
rack_options.each do |k, v|
|
393
418
|
config_ru_binds << v if k.to_s.start_with?("bind")
|
394
419
|
end
|
395
420
|
|
396
|
-
|
421
|
+
options.file_options[:binds] = config_ru_binds unless config_ru_binds.empty?
|
397
422
|
|
398
423
|
rack_app
|
399
424
|
end
|
400
425
|
|
401
|
-
def
|
402
|
-
|
426
|
+
def set_conditional_default_options
|
427
|
+
@_options.default_options[:preload_app] = !@_options[:prune_bundler] &&
|
428
|
+
(@_options[:workers] > 1) && Puma.forkable?
|
429
|
+
end
|
403
430
|
|
404
|
-
|
431
|
+
def warn_hooks
|
432
|
+
return if options[:workers] > 0
|
433
|
+
return if options[:silence_fork_callback_warning]
|
434
|
+
|
435
|
+
log_writer = LogWriter.stdio
|
436
|
+
@hooks.each_key do |hook|
|
437
|
+
options.all_of(hook).each do |hook_options|
|
438
|
+
next unless hook_options[:cluster_only]
|
439
|
+
|
440
|
+
log_writer.log(<<~MSG.tr("\n", " "))
|
441
|
+
Warning: The code in the `#{hook}` block will not execute
|
442
|
+
in the current Puma configuration. The `#{hook}` block only
|
443
|
+
executes in Puma's cluster mode. To fix this, either remove the
|
444
|
+
`#{hook}` call or increase Puma's worker count above zero.
|
445
|
+
MSG
|
446
|
+
end
|
447
|
+
end
|
405
448
|
end
|
406
449
|
end
|
407
450
|
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.1"
|
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
@@ -128,7 +128,8 @@ module Puma
|
|
128
128
|
require_relative 'log_writer'
|
129
129
|
|
130
130
|
config = Puma::Configuration.new({ config_files: [@config_file] }, {} , env)
|
131
|
-
config.
|
131
|
+
config.clamp
|
132
|
+
|
132
133
|
@state ||= config.options[:state]
|
133
134
|
@control_url ||= config.options[:control_url]
|
134
135
|
@control_auth_token ||= config.options[:control_auth_token]
|
@@ -248,7 +249,7 @@ module Puma
|
|
248
249
|
@stdout.flush unless @stdout.sync
|
249
250
|
return
|
250
251
|
elsif sig.start_with? 'SIG'
|
251
|
-
if Signal.list.key? sig.
|
252
|
+
if Signal.list.key? sig.delete_prefix('SIG')
|
252
253
|
Process.kill sig, @pid
|
253
254
|
else
|
254
255
|
raise "Signal '#{sig}' not available'"
|