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/cluster.rb
CHANGED
@@ -85,9 +85,7 @@ module Puma
|
|
85
85
|
@workers << WorkerHandle.new(idx, pid, @phase, @options)
|
86
86
|
end
|
87
87
|
|
88
|
-
if @options[:fork_worker] &&
|
89
|
-
@workers.all? {|x| x.phase == @phase}
|
90
|
-
|
88
|
+
if @options[:fork_worker] && all_workers_in_phase?
|
91
89
|
@fork_writer << "0\n"
|
92
90
|
end
|
93
91
|
end
|
@@ -148,10 +146,22 @@ module Puma
|
|
148
146
|
idx
|
149
147
|
end
|
150
148
|
|
149
|
+
def worker_at(idx)
|
150
|
+
@workers.find { |w| w.index == idx }
|
151
|
+
end
|
152
|
+
|
151
153
|
def all_workers_booted?
|
152
154
|
@workers.count { |w| !w.booted? } == 0
|
153
155
|
end
|
154
156
|
|
157
|
+
def all_workers_in_phase?
|
158
|
+
@workers.all? { |w| w.phase == @phase }
|
159
|
+
end
|
160
|
+
|
161
|
+
def all_workers_idle_timed_out?
|
162
|
+
(@workers.map(&:pid) - idle_timed_out_worker_pids).empty?
|
163
|
+
end
|
164
|
+
|
155
165
|
def check_workers
|
156
166
|
return if @next_check >= Time.now
|
157
167
|
|
@@ -276,7 +286,7 @@ module Puma
|
|
276
286
|
|
277
287
|
# @version 5.0.0
|
278
288
|
def fork_worker!
|
279
|
-
if (worker =
|
289
|
+
if (worker = worker_at 0)
|
280
290
|
worker.phase += 1
|
281
291
|
end
|
282
292
|
phased_restart(true)
|
@@ -338,6 +348,8 @@ module Puma
|
|
338
348
|
def run
|
339
349
|
@status = :run
|
340
350
|
|
351
|
+
@idle_workers = {}
|
352
|
+
|
341
353
|
output_header "cluster"
|
342
354
|
|
343
355
|
# This is aligned with the output from Runner, see Runner#output_header
|
@@ -411,6 +423,8 @@ module Puma
|
|
411
423
|
|
412
424
|
@master_read, @worker_write = read, @wakeup
|
413
425
|
|
426
|
+
@options[:worker_write] = @worker_write
|
427
|
+
|
414
428
|
@config.run_hooks(:before_fork, nil, @log_writer)
|
415
429
|
|
416
430
|
spawn_workers
|
@@ -426,6 +440,11 @@ module Puma
|
|
426
440
|
|
427
441
|
while @status == :run
|
428
442
|
begin
|
443
|
+
if all_workers_idle_timed_out?
|
444
|
+
log "- All workers reached idle timeout"
|
445
|
+
break
|
446
|
+
end
|
447
|
+
|
429
448
|
if @phased_restart
|
430
449
|
start_phased_restart
|
431
450
|
@phased_restart = false
|
@@ -446,7 +465,7 @@ module Puma
|
|
446
465
|
|
447
466
|
if req == "b" || req == "f"
|
448
467
|
pid, idx = result.split(':').map(&:to_i)
|
449
|
-
w =
|
468
|
+
w = worker_at idx
|
450
469
|
w.pid = pid if w.pid.nil?
|
451
470
|
end
|
452
471
|
|
@@ -463,24 +482,37 @@ module Puma
|
|
463
482
|
when "t"
|
464
483
|
w.term unless w.term?
|
465
484
|
when "p"
|
466
|
-
|
485
|
+
status = result.sub(/^\d+/,'').chomp
|
486
|
+
w.ping!(status)
|
467
487
|
@events.fire(:ping!, w)
|
488
|
+
|
489
|
+
if in_phased_restart && workers_not_booted.positive? && w0 = worker_at(0)
|
490
|
+
w0.ping!(status)
|
491
|
+
@events.fire(:ping!, w0)
|
492
|
+
end
|
493
|
+
|
468
494
|
if !booted && @workers.none? {|worker| worker.last_status.empty?}
|
469
495
|
@events.fire_on_booted!
|
470
496
|
debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
|
471
497
|
booted = true
|
472
498
|
end
|
499
|
+
when "i"
|
500
|
+
if @idle_workers[pid]
|
501
|
+
@idle_workers.delete pid
|
502
|
+
else
|
503
|
+
@idle_workers[pid] = true
|
504
|
+
end
|
473
505
|
end
|
474
506
|
else
|
475
507
|
log "! Out-of-sync worker list, no #{pid} worker"
|
476
508
|
end
|
477
509
|
end
|
510
|
+
|
478
511
|
if in_phased_restart && workers_not_booted.zero?
|
479
512
|
@events.fire_on_booted!
|
480
513
|
debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
|
481
514
|
in_phased_restart = false
|
482
515
|
end
|
483
|
-
|
484
516
|
rescue Interrupt
|
485
517
|
@status = :stop
|
486
518
|
end
|
@@ -509,10 +541,28 @@ module Puma
|
|
509
541
|
# loops thru @workers, removing workers that exited, and calling
|
510
542
|
# `#term` if needed
|
511
543
|
def wait_workers
|
544
|
+
# Reap all children, known workers or otherwise.
|
545
|
+
# If puma has PID 1, as it's common in containerized environments,
|
546
|
+
# then it's responsible for reaping orphaned processes, so we must reap
|
547
|
+
# all our dead children, regardless of whether they are workers we spawned
|
548
|
+
# or some reattached processes.
|
549
|
+
reaped_children = {}
|
550
|
+
loop do
|
551
|
+
begin
|
552
|
+
pid, status = Process.wait2(-1, Process::WNOHANG)
|
553
|
+
break unless pid
|
554
|
+
reaped_children[pid] = status
|
555
|
+
rescue Errno::ECHILD
|
556
|
+
break
|
557
|
+
end
|
558
|
+
end
|
559
|
+
|
512
560
|
@workers.reject! do |w|
|
513
561
|
next false if w.pid.nil?
|
514
562
|
begin
|
515
|
-
|
563
|
+
# When `fork_worker` is enabled, some worker may not be direct children, but grand children.
|
564
|
+
# Because of this they won't be reaped by `Process.wait2(-1)`, so we need to check them individually)
|
565
|
+
if reaped_children.delete(w.pid) || (@options[:fork_worker] && Process.wait(w.pid, Process::WNOHANG))
|
516
566
|
true
|
517
567
|
else
|
518
568
|
w.term if w.term?
|
@@ -529,6 +579,11 @@ module Puma
|
|
529
579
|
end
|
530
580
|
end
|
531
581
|
end
|
582
|
+
|
583
|
+
# Log unknown children
|
584
|
+
reaped_children.each do |pid, status|
|
585
|
+
log "! reaped unknown child process pid=#{pid} status=#{status}"
|
586
|
+
end
|
532
587
|
end
|
533
588
|
|
534
589
|
# @version 5.0.0
|
@@ -536,14 +591,18 @@ module Puma
|
|
536
591
|
@workers.each do |w|
|
537
592
|
if !w.term? && w.ping_timeout <= Time.now
|
538
593
|
details = if w.booted?
|
539
|
-
"(
|
594
|
+
"(Worker #{w.index} failed to check in within #{@options[:worker_timeout]} seconds)"
|
540
595
|
else
|
541
|
-
"(
|
596
|
+
"(Worker #{w.index} failed to boot within #{@options[:worker_boot_timeout]} seconds)"
|
542
597
|
end
|
543
598
|
log "! Terminating timed out worker #{details}: #{w.pid}"
|
544
599
|
w.kill
|
545
600
|
end
|
546
601
|
end
|
547
602
|
end
|
603
|
+
|
604
|
+
def idle_timed_out_worker_pids
|
605
|
+
@idle_workers.keys
|
606
|
+
end
|
548
607
|
end
|
549
608
|
end
|
data/lib/puma/configuration.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
require_relative 'rack/builder'
|
4
4
|
require_relative 'plugin'
|
5
5
|
require_relative 'const'
|
6
|
-
|
6
|
+
require_relative 'dsl'
|
7
7
|
|
8
8
|
module Puma
|
9
9
|
# A class used for storing "leveled" configuration options.
|
@@ -133,8 +133,10 @@ module Puma
|
|
133
133
|
debug: false,
|
134
134
|
early_hints: nil,
|
135
135
|
environment: 'development'.freeze,
|
136
|
-
# Number of seconds to wait until we get the first data for the request
|
136
|
+
# Number of seconds to wait until we get the first data for the request.
|
137
137
|
first_data_timeout: 30,
|
138
|
+
# Number of seconds to wait until the next request before shutting down.
|
139
|
+
idle_timeout: nil,
|
138
140
|
io_selector_backend: :auto,
|
139
141
|
log_requests: false,
|
140
142
|
logger: STDOUT,
|
@@ -385,5 +387,3 @@ module Puma
|
|
385
387
|
end
|
386
388
|
end
|
387
389
|
end
|
388
|
-
|
389
|
-
require_relative 'dsl'
|
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 = "6.3
|
104
|
-
CODE_NAME = "
|
103
|
+
PUMA_VERSION = VERSION = "6.4.3"
|
104
|
+
CODE_NAME = "The Eagle of Durango"
|
105
105
|
|
106
106
|
PUMA_SERVER_STRING = ["puma", PUMA_VERSION, CODE_NAME].join(" ").freeze
|
107
107
|
|
@@ -281,6 +281,14 @@ module Puma
|
|
281
281
|
# header values can contain HTAB?
|
282
282
|
ILLEGAL_HEADER_VALUE_REGEX = /[\x00-\x08\x0A-\x1F]/.freeze
|
283
283
|
|
284
|
+
# The keys of headers that should not be convert to underscore
|
285
|
+
# normalized versions. These headers are ignored at the request reading layer,
|
286
|
+
# but if we normalize them after reading, it's just confusing for the application.
|
287
|
+
UNMASKABLE_HEADERS = {
|
288
|
+
"HTTP_TRANSFER,ENCODING" => true,
|
289
|
+
"HTTP_CONTENT,LENGTH" => true,
|
290
|
+
}
|
291
|
+
|
284
292
|
# Banned keys of response header
|
285
293
|
BANNED_HEADER_KEY = /\A(rack\.|status\z)/.freeze
|
286
294
|
|
data/lib/puma/control_cli.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'optparse'
|
4
|
-
require_relative 'state_file'
|
5
4
|
require_relative 'const'
|
6
5
|
require_relative 'detect'
|
7
|
-
require_relative 'configuration'
|
8
6
|
require 'uri'
|
9
7
|
require 'socket'
|
10
8
|
|
@@ -126,6 +124,9 @@ module Puma
|
|
126
124
|
end
|
127
125
|
|
128
126
|
if @config_file
|
127
|
+
require_relative 'configuration'
|
128
|
+
require_relative 'log_writer'
|
129
|
+
|
129
130
|
config = Puma::Configuration.new({ config_files: [@config_file] }, {})
|
130
131
|
config.load
|
131
132
|
@state ||= config.options[:state]
|
@@ -149,6 +150,8 @@ module Puma
|
|
149
150
|
raise "State file not found: #{@state}"
|
150
151
|
end
|
151
152
|
|
153
|
+
require_relative 'state_file'
|
154
|
+
|
152
155
|
sf = Puma::StateFile.new
|
153
156
|
sf.load @state
|
154
157
|
|
@@ -164,22 +167,26 @@ module Puma
|
|
164
167
|
def send_request
|
165
168
|
uri = URI.parse @control_url
|
166
169
|
|
170
|
+
host = uri.host
|
171
|
+
|
167
172
|
# create server object by scheme
|
168
173
|
server =
|
169
174
|
case uri.scheme
|
170
175
|
when 'ssl'
|
171
176
|
require 'openssl'
|
177
|
+
host = host[1..-2] if host&.start_with? '['
|
172
178
|
OpenSSL::SSL::SSLSocket.new(
|
173
|
-
TCPSocket.new(
|
179
|
+
TCPSocket.new(host, uri.port),
|
174
180
|
OpenSSL::SSL::SSLContext.new)
|
175
181
|
.tap { |ssl| ssl.sync_close = true } # default is false
|
176
182
|
.tap(&:connect)
|
177
183
|
when 'tcp'
|
178
|
-
|
184
|
+
host = host[1..-2] if host&.start_with? '['
|
185
|
+
TCPSocket.new host, uri.port
|
179
186
|
when 'unix'
|
180
187
|
# check for abstract UNIXSocket
|
181
188
|
UNIXSocket.new(@control_url.start_with?('unix://@') ?
|
182
|
-
"\0#{
|
189
|
+
"\0#{host}#{uri.path}" : "#{host}#{uri.path}")
|
183
190
|
else
|
184
191
|
raise "Invalid scheme: #{uri.scheme}"
|
185
192
|
end
|
data/lib/puma/detect.rb
CHANGED
@@ -12,15 +12,14 @@ module Puma
|
|
12
12
|
|
13
13
|
IS_JRUBY = Object.const_defined? :JRUBY_VERSION
|
14
14
|
|
15
|
-
IS_OSX =
|
15
|
+
IS_OSX = RUBY_DESCRIPTION.include? 'darwin'
|
16
16
|
|
17
|
-
IS_WINDOWS =
|
18
|
-
IS_JRUBY && RUBY_DESCRIPTION.include?('mswin')
|
17
|
+
IS_WINDOWS = RUBY_DESCRIPTION.match?(/mswin|ming|cygwin/)
|
19
18
|
|
20
19
|
IS_LINUX = !(IS_OSX || IS_WINDOWS)
|
21
20
|
|
22
21
|
# @version 5.2.0
|
23
|
-
IS_MRI =
|
22
|
+
IS_MRI = RUBY_ENGINE == 'ruby'
|
24
23
|
|
25
24
|
def self.jruby?
|
26
25
|
IS_JRUBY
|
data/lib/puma/dsl.rb
CHANGED
@@ -315,16 +315,22 @@ module Puma
|
|
315
315
|
bind URI::Generic.build(scheme: 'tcp', host: host, port: Integer(port)).to_s
|
316
316
|
end
|
317
317
|
|
318
|
+
# Define how long the tcp socket stays open, if no data has been received.
|
319
|
+
# @see Puma::Server.new
|
320
|
+
def first_data_timeout(seconds)
|
321
|
+
@options[:first_data_timeout] = Integer(seconds)
|
322
|
+
end
|
323
|
+
|
318
324
|
# Define how long persistent connections can be idle before Puma closes them.
|
319
325
|
# @see Puma::Server.new
|
320
326
|
def persistent_timeout(seconds)
|
321
327
|
@options[:persistent_timeout] = Integer(seconds)
|
322
328
|
end
|
323
329
|
|
324
|
-
#
|
330
|
+
# If a new request is not received within this number of seconds, begin shutting down.
|
325
331
|
# @see Puma::Server.new
|
326
|
-
def
|
327
|
-
@options[:
|
332
|
+
def idle_timeout(seconds)
|
333
|
+
@options[:idle_timeout] = Integer(seconds)
|
328
334
|
end
|
329
335
|
|
330
336
|
# Work around leaky apps that leave garbage in Thread locals
|
@@ -510,6 +516,12 @@ module Puma
|
|
510
516
|
# `true`, which sets reuse 'on' with default values, or a hash, with `:size`
|
511
517
|
# and/or `:timeout` keys, each with integer values.
|
512
518
|
#
|
519
|
+
# The `cert:` options hash parameter can be the path to a certificate
|
520
|
+
# file including all intermediate certificates in PEM format.
|
521
|
+
#
|
522
|
+
# The `cert_pem:` options hash parameter can be String containing the
|
523
|
+
# cerificate and all intermediate certificates in PEM format.
|
524
|
+
#
|
513
525
|
# @example
|
514
526
|
# ssl_bind '127.0.0.1', '9292', {
|
515
527
|
# cert: path_to_cert,
|
@@ -717,6 +729,51 @@ module Puma
|
|
717
729
|
process_hook :before_refork, key, block, 'on_refork'
|
718
730
|
end
|
719
731
|
|
732
|
+
# Provide a block to be executed just before a thread is added to the thread
|
733
|
+
# pool. Be careful: while the block executes, thread creation is delayed, and
|
734
|
+
# probably a request will have to wait too! The new thread will not be added to
|
735
|
+
# the threadpool until the provided block returns.
|
736
|
+
#
|
737
|
+
# Return values are ignored.
|
738
|
+
# Raising an exception will log a warning.
|
739
|
+
#
|
740
|
+
# This hook is useful for doing something when the thread pool grows.
|
741
|
+
#
|
742
|
+
# This can be called multiple times to add several hooks.
|
743
|
+
#
|
744
|
+
# @example
|
745
|
+
# on_thread_start do
|
746
|
+
# puts 'On thread start...'
|
747
|
+
# end
|
748
|
+
def on_thread_start(&block)
|
749
|
+
@options[:before_thread_start] ||= []
|
750
|
+
@options[:before_thread_start] << block
|
751
|
+
end
|
752
|
+
|
753
|
+
# Provide a block to be executed after a thread is trimmed from the thread
|
754
|
+
# pool. Be careful: while this block executes, Puma's main loop is
|
755
|
+
# blocked, so no new requests will be picked up.
|
756
|
+
#
|
757
|
+
# This hook only runs when a thread in the threadpool is trimmed by Puma.
|
758
|
+
# It does not run when a thread dies due to exceptions or any other cause.
|
759
|
+
#
|
760
|
+
# Return values are ignored.
|
761
|
+
# Raising an exception will log a warning.
|
762
|
+
#
|
763
|
+
# This hook is useful for cleaning up thread local resources when a thread
|
764
|
+
# is trimmed.
|
765
|
+
#
|
766
|
+
# This can be called multiple times to add several hooks.
|
767
|
+
#
|
768
|
+
# @example
|
769
|
+
# on_thread_exit do
|
770
|
+
# puts 'On thread exit...'
|
771
|
+
# end
|
772
|
+
def on_thread_exit(&block)
|
773
|
+
@options[:before_thread_exit] ||= []
|
774
|
+
@options[:before_thread_exit] << block
|
775
|
+
end
|
776
|
+
|
720
777
|
# Code to run out-of-band when the worker is idle.
|
721
778
|
# These hooks run immediately after a request has finished
|
722
779
|
# processing and there are no busy threads on the worker.
|
@@ -848,7 +905,8 @@ module Puma
|
|
848
905
|
# not a request timeout, it is to protect against a hung or dead process.
|
849
906
|
# Setting this value will not protect against slow requests.
|
850
907
|
#
|
851
|
-
#
|
908
|
+
# This value must be greater than worker_check_interval.
|
909
|
+
# The default value is 60 seconds.
|
852
910
|
#
|
853
911
|
# @note Cluster mode only.
|
854
912
|
# @example
|
@@ -1132,8 +1190,10 @@ module Puma
|
|
1132
1190
|
|
1133
1191
|
def warn_if_in_single_mode(hook_name)
|
1134
1192
|
return if @options[:silence_fork_callback_warning]
|
1135
|
-
|
1136
|
-
|
1193
|
+
# user_options (CLI) have precedence over config file
|
1194
|
+
workers_val = @config.options.user_options[:workers] || @options[:workers] ||
|
1195
|
+
@config.puma_default_options[:workers] || 0
|
1196
|
+
if workers_val == 0
|
1137
1197
|
log_string =
|
1138
1198
|
"Warning: You specified code to run in a `#{hook_name}` block, " \
|
1139
1199
|
"but Puma is not configured to run in cluster mode (worker count > 0 ), " \
|
data/lib/puma/minissl.rb
CHANGED
@@ -184,6 +184,11 @@ module Puma
|
|
184
184
|
@socket.peeraddr
|
185
185
|
end
|
186
186
|
|
187
|
+
# OpenSSL is loaded in `MiniSSL::ContextBuilder` when
|
188
|
+
# `MiniSSL::Context#verify_mode` is not `VERIFY_NONE`.
|
189
|
+
# When `VERIFY_NONE`, `MiniSSL::Engine#peercert` is nil, regardless of
|
190
|
+
# whether the client sends a cert.
|
191
|
+
# @return [OpenSSL::X509::Certificate, nil]
|
187
192
|
# @!attribute [r] peercert
|
188
193
|
def peercert
|
189
194
|
return @peercert if @peercert
|
data/lib/puma/null_io.rb
CHANGED
@@ -18,8 +18,22 @@ module Puma
|
|
18
18
|
|
19
19
|
# Mimics IO#read with no data.
|
20
20
|
#
|
21
|
-
def read(
|
22
|
-
|
21
|
+
def read(length = nil, buffer = nil)
|
22
|
+
if length.to_i < 0
|
23
|
+
raise ArgumentError, "(negative length #{length} given)"
|
24
|
+
end
|
25
|
+
|
26
|
+
buffer = if buffer.nil?
|
27
|
+
"".b
|
28
|
+
else
|
29
|
+
String.try_convert(buffer) or raise TypeError, "no implicit conversion of #{buffer.class} into String"
|
30
|
+
end
|
31
|
+
buffer.clear
|
32
|
+
if length.to_i > 0
|
33
|
+
nil
|
34
|
+
else
|
35
|
+
buffer
|
36
|
+
end
|
23
37
|
end
|
24
38
|
|
25
39
|
def rewind
|
data/lib/puma/rack/urlmap.rb
CHANGED
@@ -34,7 +34,7 @@ module Puma::Rack
|
|
34
34
|
end
|
35
35
|
|
36
36
|
location = location.chomp('/')
|
37
|
-
match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)",
|
37
|
+
match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)", Regexp::NOENCODING)
|
38
38
|
|
39
39
|
[host, location, match, app]
|
40
40
|
}.sort_by do |(host, location, _, _)|
|
data/lib/puma/request.rb
CHANGED
@@ -495,6 +495,11 @@ module Puma
|
|
495
495
|
# compatibility, we'll convert them back. This code is written to
|
496
496
|
# avoid allocation in the common case (ie there are no headers
|
497
497
|
# with `,` in their names), that's why it has the extra conditionals.
|
498
|
+
#
|
499
|
+
# @note If a normalized version of a `,` header already exists, we ignore
|
500
|
+
# the `,` version. This prevents clobbering headers managed by proxies
|
501
|
+
# but not by clients (Like X-Forwarded-For).
|
502
|
+
#
|
498
503
|
# @param env [Hash] see Puma::Client#env, from request, modifies in place
|
499
504
|
# @version 5.0.3
|
500
505
|
#
|
@@ -503,23 +508,31 @@ module Puma
|
|
503
508
|
to_add = nil
|
504
509
|
|
505
510
|
env.each do |k,v|
|
506
|
-
if k.start_with?("HTTP_") && k.include?(",") && k
|
511
|
+
if k.start_with?("HTTP_") && k.include?(",") && !UNMASKABLE_HEADERS.key?(k)
|
507
512
|
if to_delete
|
508
513
|
to_delete << k
|
509
514
|
else
|
510
515
|
to_delete = [k]
|
511
516
|
end
|
512
517
|
|
518
|
+
new_k = k.tr(",", "_")
|
519
|
+
if env.key?(new_k)
|
520
|
+
next
|
521
|
+
end
|
522
|
+
|
513
523
|
unless to_add
|
514
524
|
to_add = {}
|
515
525
|
end
|
516
526
|
|
517
|
-
to_add[
|
527
|
+
to_add[new_k] = v
|
518
528
|
end
|
519
529
|
end
|
520
530
|
|
521
|
-
if to_delete
|
531
|
+
if to_delete # rubocop:disable Style/SafeNavigation
|
522
532
|
to_delete.each { |k| env.delete(k) }
|
533
|
+
end
|
534
|
+
|
535
|
+
if to_add
|
523
536
|
env.merge! to_add
|
524
537
|
end
|
525
538
|
end
|
data/lib/puma/runner.rb
CHANGED
@@ -70,12 +70,16 @@ module Puma
|
|
70
70
|
|
71
71
|
app = Puma::App::Status.new @launcher, token
|
72
72
|
|
73
|
-
# A Reactor is not created
|
73
|
+
# A Reactor is not created and nio4r is not loaded when 'queue_requests: false'
|
74
74
|
# Use `nil` for events, no hooks in control server
|
75
75
|
control = Puma::Server.new app, nil,
|
76
76
|
{ min_threads: 0, max_threads: 1, queue_requests: false, log_writer: @log_writer }
|
77
77
|
|
78
|
-
|
78
|
+
begin
|
79
|
+
control.binder.parse [str], nil, 'Starting control server'
|
80
|
+
rescue Errno::EADDRINUSE, Errno::EACCES => e
|
81
|
+
raise e, "Error: Control server address '#{str}' is already in use. Original error: #{e.message}"
|
82
|
+
end
|
79
83
|
|
80
84
|
control.run thread_name: 'ctl'
|
81
85
|
@control = control
|