puma 5.0.4 → 5.5.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/History.md +250 -48
- data/README.md +90 -24
- data/docs/architecture.md +57 -20
- data/docs/compile_options.md +21 -0
- data/docs/deployment.md +53 -67
- data/docs/fork_worker.md +2 -0
- data/docs/jungle/rc.d/README.md +1 -1
- data/docs/kubernetes.md +66 -0
- data/docs/plugins.md +15 -15
- data/docs/rails_dev_mode.md +28 -0
- data/docs/restart.md +7 -7
- data/docs/signals.md +10 -10
- data/docs/stats.md +142 -0
- data/docs/systemd.md +85 -66
- data/ext/puma_http11/extconf.rb +36 -6
- data/ext/puma_http11/http11_parser.c +64 -59
- data/ext/puma_http11/http11_parser.h +1 -1
- data/ext/puma_http11/http11_parser.java.rl +1 -1
- data/ext/puma_http11/http11_parser.rl +1 -1
- data/ext/puma_http11/http11_parser_common.rl +1 -1
- data/ext/puma_http11/mini_ssl.c +177 -84
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +39 -41
- data/ext/puma_http11/puma_http11.c +8 -2
- data/lib/puma/app/status.rb +4 -7
- data/lib/puma/binder.rb +121 -46
- data/lib/puma/cli.rb +9 -0
- data/lib/puma/client.rb +58 -19
- data/lib/puma/cluster/worker.rb +19 -16
- data/lib/puma/cluster/worker_handle.rb +9 -2
- data/lib/puma/cluster.rb +46 -22
- data/lib/puma/configuration.rb +18 -2
- data/lib/puma/const.rb +14 -4
- data/lib/puma/control_cli.rb +76 -71
- data/lib/puma/detect.rb +14 -10
- data/lib/puma/dsl.rb +143 -26
- data/lib/puma/error_logger.rb +12 -5
- data/lib/puma/events.rb +18 -3
- data/lib/puma/json_serialization.rb +96 -0
- data/lib/puma/launcher.rb +54 -6
- data/lib/puma/minissl/context_builder.rb +6 -0
- data/lib/puma/minissl.rb +54 -38
- data/lib/puma/null_io.rb +12 -0
- data/lib/puma/plugin.rb +1 -1
- data/lib/puma/queue_close.rb +7 -7
- data/lib/puma/rack/builder.rb +1 -1
- data/lib/puma/reactor.rb +19 -12
- data/lib/puma/request.rb +45 -16
- data/lib/puma/runner.rb +38 -13
- data/lib/puma/server.rb +62 -123
- data/lib/puma/state_file.rb +5 -3
- data/lib/puma/systemd.rb +46 -0
- data/lib/puma/thread_pool.rb +10 -7
- data/lib/puma/util.rb +8 -1
- data/lib/puma.rb +36 -10
- data/lib/rack/handler/puma.rb +1 -0
- metadata +15 -9
data/lib/puma/dsl.rb
CHANGED
@@ -34,6 +34,40 @@ module Puma
|
|
34
34
|
class DSL
|
35
35
|
include ConfigDefault
|
36
36
|
|
37
|
+
# convenience method so logic can be used in CI
|
38
|
+
# @see ssl_bind
|
39
|
+
#
|
40
|
+
def self.ssl_bind_str(host, port, opts)
|
41
|
+
verify = opts.fetch(:verify_mode, 'none').to_s
|
42
|
+
|
43
|
+
tls_str =
|
44
|
+
if opts[:no_tlsv1_1] then '&no_tlsv1_1=true'
|
45
|
+
elsif opts[:no_tlsv1] then '&no_tlsv1=true'
|
46
|
+
else ''
|
47
|
+
end
|
48
|
+
|
49
|
+
ca_additions = "&ca=#{opts[:ca]}" if ['peer', 'force_peer'].include?(verify)
|
50
|
+
|
51
|
+
if defined?(JRUBY_VERSION)
|
52
|
+
ssl_cipher_list = opts[:ssl_cipher_list] ?
|
53
|
+
"&ssl_cipher_list=#{opts[:ssl_cipher_list]}" : nil
|
54
|
+
|
55
|
+
keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
|
56
|
+
|
57
|
+
"ssl://#{host}:#{port}?#{keystore_additions}#{ssl_cipher_list}" \
|
58
|
+
"&verify_mode=#{verify}#{tls_str}#{ca_additions}"
|
59
|
+
else
|
60
|
+
ssl_cipher_filter = opts[:ssl_cipher_filter] ?
|
61
|
+
"&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" : nil
|
62
|
+
|
63
|
+
v_flags = (ary = opts[:verification_flags]) ?
|
64
|
+
"&verification_flags=#{Array(ary).join ','}" : nil
|
65
|
+
|
66
|
+
"ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}" \
|
67
|
+
"#{ssl_cipher_filter}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
37
71
|
def initialize(options, config)
|
38
72
|
@config = config
|
39
73
|
@options = options
|
@@ -167,7 +201,7 @@ module Puma
|
|
167
201
|
# * Set the socket backlog depth with +backlog+, default is 1024.
|
168
202
|
# * Set up an SSL certificate with +key+ & +cert+.
|
169
203
|
# * Set whether to optimize for low latency instead of throughput with
|
170
|
-
# +low_latency+, default is to optimize for low latency. This is done
|
204
|
+
# +low_latency+, default is to not optimize for low latency. This is done
|
171
205
|
# via +Socket::TCP_NODELAY+.
|
172
206
|
# * Set socket permissions with +umask+.
|
173
207
|
#
|
@@ -191,13 +225,39 @@ module Puma
|
|
191
225
|
@options[:binds] = []
|
192
226
|
end
|
193
227
|
|
228
|
+
# Bind to (systemd) activated sockets, regardless of configured binds.
|
229
|
+
#
|
230
|
+
# Systemd can present sockets as file descriptors that are already opened.
|
231
|
+
# By default Puma will use these but only if it was explicitly told to bind
|
232
|
+
# to the socket. If not, it will close the activated sockets. This means
|
233
|
+
# all configuration is duplicated.
|
234
|
+
#
|
235
|
+
# Binds can contain additional configuration, but only SSL config is really
|
236
|
+
# relevant since the unix and TCP socket options are ignored.
|
237
|
+
#
|
238
|
+
# This means there is a lot of duplicated configuration for no additional
|
239
|
+
# value in most setups. This method tells the launcher to bind to all
|
240
|
+
# activated sockets, regardless of existing bind.
|
241
|
+
#
|
242
|
+
# To clear configured binds, the value only can be passed. This will clear
|
243
|
+
# out any binds that may have been configured.
|
244
|
+
#
|
245
|
+
# @example Use any systemd activated sockets as well as configured binds
|
246
|
+
# bind_to_activated_sockets
|
247
|
+
#
|
248
|
+
# @example Only bind to systemd activated sockets, ignoring other binds
|
249
|
+
# bind_to_activated_sockets 'only'
|
250
|
+
def bind_to_activated_sockets(bind=true)
|
251
|
+
@options[:bind_to_activated_sockets] = bind
|
252
|
+
end
|
253
|
+
|
194
254
|
# Define the TCP port to bind to. Use +bind+ for more advanced options.
|
195
255
|
#
|
196
256
|
# @example
|
197
257
|
# port 9292
|
198
258
|
def port(port, host=nil)
|
199
259
|
host ||= default_host
|
200
|
-
bind
|
260
|
+
bind URI::Generic.build(scheme: 'tcp', host: host, port: Integer(port)).to_s
|
201
261
|
end
|
202
262
|
|
203
263
|
# Define how long persistent connections can be idle before Puma closes them.
|
@@ -321,6 +381,13 @@ module Puma
|
|
321
381
|
@options[:rackup] ||= path.to_s
|
322
382
|
end
|
323
383
|
|
384
|
+
# Allows setting `env['rack.url_scheme']`.
|
385
|
+
# Only necessary if X-Forwarded-Proto is not being set by your proxy
|
386
|
+
# Normal values are 'http' or 'https'.
|
387
|
+
def rack_url_scheme(scheme=nil)
|
388
|
+
@options[:rack_url_scheme] = scheme
|
389
|
+
end
|
390
|
+
|
324
391
|
def early_hints(answer=true)
|
325
392
|
@options[:early_hints] = answer
|
326
393
|
end
|
@@ -345,7 +412,10 @@ module Puma
|
|
345
412
|
# Configure +min+ to be the minimum number of threads to use to answer
|
346
413
|
# requests and +max+ the maximum.
|
347
414
|
#
|
348
|
-
# The default is
|
415
|
+
# The default is the environment variables +PUMA_MIN_THREADS+ / +PUMA_MAX_THREADS+
|
416
|
+
# (or +MIN_THREADS+ / +MAX_THREADS+ if the +PUMA_+ variables aren't set).
|
417
|
+
#
|
418
|
+
# If these environment variables aren't set, the default is "0, 5" in MRI or "0, 16" for other interpreters.
|
349
419
|
#
|
350
420
|
# @example
|
351
421
|
# threads 0, 16
|
@@ -375,29 +445,17 @@ module Puma
|
|
375
445
|
# key: path_to_key,
|
376
446
|
# ssl_cipher_filter: cipher_filter, # optional
|
377
447
|
# verify_mode: verify_mode, # default 'none'
|
448
|
+
# verification_flags: flags, # optional, not supported by JRuby
|
378
449
|
# }
|
379
|
-
# @example For JRuby
|
450
|
+
# @example For JRuby, two keys are required: keystore & keystore_pass.
|
380
451
|
# ssl_bind '127.0.0.1', '9292', {
|
381
|
-
# cert: path_to_cert,
|
382
|
-
# key: path_to_key,
|
383
|
-
# ssl_cipher_filter: cipher_filter, # optional
|
384
|
-
# verify_mode: verify_mode, # default 'none'
|
385
452
|
# keystore: path_to_keystore,
|
386
|
-
# keystore_pass: password
|
453
|
+
# keystore_pass: password,
|
454
|
+
# ssl_cipher_list: cipher_list, # optional
|
455
|
+
# verify_mode: verify_mode # default 'none'
|
387
456
|
# }
|
388
457
|
def ssl_bind(host, port, opts)
|
389
|
-
|
390
|
-
no_tlsv1 = opts.fetch(:no_tlsv1, 'false')
|
391
|
-
no_tlsv1_1 = opts.fetch(:no_tlsv1_1, 'false')
|
392
|
-
ca_additions = "&ca=#{opts[:ca]}" if ['peer', 'force_peer'].include?(verify)
|
393
|
-
|
394
|
-
if defined?(JRUBY_VERSION)
|
395
|
-
keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
|
396
|
-
bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}&#{keystore_additions}&verify_mode=#{verify}&no_tlsv1=#{no_tlsv1}&no_tlsv1_1=#{no_tlsv1_1}#{ca_additions}"
|
397
|
-
else
|
398
|
-
ssl_cipher_filter = "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" if opts[:ssl_cipher_filter]
|
399
|
-
bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}#{ssl_cipher_filter}&verify_mode=#{verify}&no_tlsv1=#{no_tlsv1}&no_tlsv1_1=#{no_tlsv1_1}#{ca_additions}"
|
400
|
-
end
|
458
|
+
bind self.class.ssl_bind_str(host, port, opts)
|
401
459
|
end
|
402
460
|
|
403
461
|
# Use +path+ as the file to store the server info state. This is
|
@@ -422,7 +480,8 @@ module Puma
|
|
422
480
|
# How many worker processes to run. Typically this is set to
|
423
481
|
# the number of available cores.
|
424
482
|
#
|
425
|
-
# The default is
|
483
|
+
# The default is the value of the environment variable +WEB_CONCURRENCY+ if
|
484
|
+
# set, otherwise 0.
|
426
485
|
#
|
427
486
|
# @note Cluster mode only.
|
428
487
|
# @see Puma::Cluster
|
@@ -430,6 +489,24 @@ module Puma
|
|
430
489
|
@options[:workers] = count.to_i
|
431
490
|
end
|
432
491
|
|
492
|
+
# Disable warning message when running in cluster mode with a single worker.
|
493
|
+
#
|
494
|
+
# Cluster mode has some overhead of running an additional 'control' process
|
495
|
+
# in order to manage the cluster. If only running a single worker it is
|
496
|
+
# likely not worth paying that overhead vs running in single mode with
|
497
|
+
# additional threads instead.
|
498
|
+
#
|
499
|
+
# There are some scenarios where running cluster mode with a single worker
|
500
|
+
# may still be warranted and valid under certain deployment scenarios, see
|
501
|
+
# https://github.com/puma/puma/issues/2534
|
502
|
+
#
|
503
|
+
# Moving from workers = 1 to workers = 0 will save 10-30% of memory use.
|
504
|
+
#
|
505
|
+
# @note Cluster mode only.
|
506
|
+
def silence_single_worker_warning
|
507
|
+
@options[:silence_single_worker_warning] = true
|
508
|
+
end
|
509
|
+
|
433
510
|
# Code to run immediately before master process
|
434
511
|
# forks workers (once on boot). These hooks can block if necessary
|
435
512
|
# to wait for background operations unknown to Puma to finish before
|
@@ -508,7 +585,7 @@ module Puma
|
|
508
585
|
# end
|
509
586
|
def after_worker_fork(&block)
|
510
587
|
@options[:after_worker_fork] ||= []
|
511
|
-
@options[:after_worker_fork]
|
588
|
+
@options[:after_worker_fork] << block
|
512
589
|
end
|
513
590
|
|
514
591
|
alias_method :after_worker_boot, :after_worker_fork
|
@@ -561,7 +638,7 @@ module Puma
|
|
561
638
|
end
|
562
639
|
|
563
640
|
# Preload the application before starting the workers; this conflicts with
|
564
|
-
# phased restart feature.
|
641
|
+
# phased restart feature. On by default if your app uses more than 1 worker.
|
565
642
|
#
|
566
643
|
# @note Cluster mode only.
|
567
644
|
# @example
|
@@ -741,7 +818,7 @@ module Puma
|
|
741
818
|
# a kernel syscall is required which for very fast rack handlers
|
742
819
|
# slows down the handling significantly.
|
743
820
|
#
|
744
|
-
# There are
|
821
|
+
# There are 5 possible values:
|
745
822
|
#
|
746
823
|
# 1. **:socket** (the default) - read the peername from the socket using the
|
747
824
|
# syscall. This is the normal behavior.
|
@@ -751,7 +828,10 @@ module Puma
|
|
751
828
|
# `set_remote_address header: "X-Real-IP"`.
|
752
829
|
# Only the first word (as separated by spaces or comma) is used, allowing
|
753
830
|
# headers such as X-Forwarded-For to be used as well.
|
754
|
-
# 4.
|
831
|
+
# 4. **proxy_protocol: :v1**- set the remote address to the value read from the
|
832
|
+
# HAproxy PROXY protocol, version 1. If the request does not have the PROXY
|
833
|
+
# protocol attached to it, will fall back to :socket
|
834
|
+
# 5. **\<Any string\>** - this allows you to hardcode remote address to any value
|
755
835
|
# you wish. Because Puma never uses this field anyway, it's format is
|
756
836
|
# entirely in your hands.
|
757
837
|
#
|
@@ -769,6 +849,13 @@ module Puma
|
|
769
849
|
if hdr = val[:header]
|
770
850
|
@options[:remote_address] = :header
|
771
851
|
@options[:remote_address_header] = "HTTP_" + hdr.upcase.tr("-", "_")
|
852
|
+
elsif protocol_version = val[:proxy_protocol]
|
853
|
+
@options[:remote_address] = :proxy_protocol
|
854
|
+
protocol_version = protocol_version.downcase.to_sym
|
855
|
+
unless [:v1].include?(protocol_version)
|
856
|
+
raise "Invalid value for proxy_protocol - #{protocol_version.inspect}"
|
857
|
+
end
|
858
|
+
@options[:remote_address_proxy_protocol] = protocol_version
|
772
859
|
else
|
773
860
|
raise "Invalid value for set_remote_address - #{val.inspect}"
|
774
861
|
end
|
@@ -810,5 +897,35 @@ module Puma
|
|
810
897
|
def nakayoshi_fork(enabled=true)
|
811
898
|
@options[:nakayoshi_fork] = enabled
|
812
899
|
end
|
900
|
+
|
901
|
+
# The number of requests to attempt inline before sending a client back to
|
902
|
+
# the reactor to be subject to normal ordering.
|
903
|
+
#
|
904
|
+
def max_fast_inline(num_of_requests)
|
905
|
+
@options[:max_fast_inline] = Float(num_of_requests)
|
906
|
+
end
|
907
|
+
|
908
|
+
# Specify the backend for the IO selector.
|
909
|
+
#
|
910
|
+
# Provided values will be passed directly to +NIO::Selector.new+, with the
|
911
|
+
# exception of +:auto+ which will let nio4r choose the backend.
|
912
|
+
#
|
913
|
+
# Check the documentation of +NIO::Selector.backends+ for the list of valid
|
914
|
+
# options. Note that the available options on your system will depend on the
|
915
|
+
# operating system. If you want to use the pure Ruby backend (not
|
916
|
+
# recommended due to its comparatively low performance), set environment
|
917
|
+
# variable +NIO4R_PURE+ to +true+.
|
918
|
+
#
|
919
|
+
# The default is +:auto+.
|
920
|
+
#
|
921
|
+
# @see https://github.com/socketry/nio4r/blob/master/lib/nio/selector.rb
|
922
|
+
#
|
923
|
+
def io_selector_backend(backend)
|
924
|
+
@options[:io_selector_backend] = backend.to_sym
|
925
|
+
end
|
926
|
+
|
927
|
+
def mutate_stdout_and_stderr_to_sync_on_write(enabled=true)
|
928
|
+
@options[:mutate_stdout_and_stderr_to_sync_on_write] = enabled
|
929
|
+
end
|
813
930
|
end
|
814
931
|
end
|
data/lib/puma/error_logger.rb
CHANGED
@@ -15,7 +15,6 @@ module Puma
|
|
15
15
|
|
16
16
|
def initialize(ioerr)
|
17
17
|
@ioerr = ioerr
|
18
|
-
@ioerr.sync = true
|
19
18
|
|
20
19
|
@debug = ENV.key? 'PUMA_DEBUG'
|
21
20
|
end
|
@@ -24,7 +23,7 @@ module Puma
|
|
24
23
|
new $stderr
|
25
24
|
end
|
26
25
|
|
27
|
-
# Print
|
26
|
+
# Print occurred error details.
|
28
27
|
# +options+ hash with additional options:
|
29
28
|
# - +error+ is an exception object
|
30
29
|
# - +req+ the http request
|
@@ -32,10 +31,10 @@ module Puma
|
|
32
31
|
# and before all remaining info.
|
33
32
|
#
|
34
33
|
def info(options={})
|
35
|
-
|
34
|
+
log title(options)
|
36
35
|
end
|
37
36
|
|
38
|
-
# Print
|
37
|
+
# Print occurred error details only if
|
39
38
|
# environment variable PUMA_DEBUG is defined.
|
40
39
|
# +options+ hash with additional options:
|
41
40
|
# - +error+ is an exception object
|
@@ -54,7 +53,7 @@ module Puma
|
|
54
53
|
string_block << request_dump(req) if request_parsed?(req)
|
55
54
|
string_block << error.backtrace if error
|
56
55
|
|
57
|
-
|
56
|
+
log string_block.join("\n")
|
58
57
|
end
|
59
58
|
|
60
59
|
def title(options={})
|
@@ -93,5 +92,13 @@ module Puma
|
|
93
92
|
def request_parsed?(req)
|
94
93
|
req && req.env[REQUEST_METHOD]
|
95
94
|
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def log(str)
|
99
|
+
ioerr.puts str
|
100
|
+
|
101
|
+
ioerr.flush unless ioerr.sync
|
102
|
+
end
|
96
103
|
end
|
97
104
|
end
|
data/lib/puma/events.rb
CHANGED
@@ -30,9 +30,6 @@ module Puma
|
|
30
30
|
@stdout = stdout
|
31
31
|
@stderr = stderr
|
32
32
|
|
33
|
-
@stdout.sync = true
|
34
|
-
@stderr.sync = true
|
35
|
-
|
36
33
|
@debug = ENV.key? 'PUMA_DEBUG'
|
37
34
|
@error_logger = ErrorLogger.new(@stderr)
|
38
35
|
|
@@ -66,6 +63,8 @@ module Puma
|
|
66
63
|
#
|
67
64
|
def log(str)
|
68
65
|
@stdout.puts format(str) if @stdout.respond_to? :puts
|
66
|
+
|
67
|
+
@stdout.flush unless @stdout.sync
|
69
68
|
rescue Errno::EPIPE
|
70
69
|
end
|
71
70
|
|
@@ -137,10 +136,26 @@ module Puma
|
|
137
136
|
register(:on_booted, &block)
|
138
137
|
end
|
139
138
|
|
139
|
+
def on_restart(&block)
|
140
|
+
register(:on_restart, &block)
|
141
|
+
end
|
142
|
+
|
143
|
+
def on_stopped(&block)
|
144
|
+
register(:on_stopped, &block)
|
145
|
+
end
|
146
|
+
|
140
147
|
def fire_on_booted!
|
141
148
|
fire(:on_booted)
|
142
149
|
end
|
143
150
|
|
151
|
+
def fire_on_restart!
|
152
|
+
fire(:on_restart)
|
153
|
+
end
|
154
|
+
|
155
|
+
def fire_on_stopped!
|
156
|
+
fire(:on_stopped)
|
157
|
+
end
|
158
|
+
|
144
159
|
DEFAULT = new(STDOUT, STDERR)
|
145
160
|
|
146
161
|
# Returns an Events object which writes its status to 2 StringIO
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
module Puma
|
5
|
+
|
6
|
+
# Puma deliberately avoids the use of the json gem and instead performs JSON
|
7
|
+
# serialization without any external dependencies. In a puma cluster, loading
|
8
|
+
# any gem into the puma master process means that operators cannot use a
|
9
|
+
# phased restart to upgrade their application if the new version of that
|
10
|
+
# application uses a different version of that gem. The json gem in
|
11
|
+
# particular is additionally problematic because it leverages native
|
12
|
+
# extensions. If the puma master process relies on a gem with native
|
13
|
+
# extensions and operators remove gems from disk related to old releases,
|
14
|
+
# subsequent phased restarts can fail.
|
15
|
+
#
|
16
|
+
# The implementation of JSON serialization in this module is not designed to
|
17
|
+
# be particularly full-featured or fast. It just has to handle the few places
|
18
|
+
# where Puma relies on JSON serialization internally.
|
19
|
+
|
20
|
+
module JSONSerialization
|
21
|
+
QUOTE = /"/
|
22
|
+
BACKSLASH = /\\/
|
23
|
+
CONTROL_CHAR_TO_ESCAPE = /[\x00-\x1F]/ # As required by ECMA-404
|
24
|
+
CHAR_TO_ESCAPE = Regexp.union QUOTE, BACKSLASH, CONTROL_CHAR_TO_ESCAPE
|
25
|
+
|
26
|
+
class SerializationError < StandardError; end
|
27
|
+
|
28
|
+
class << self
|
29
|
+
def generate(value)
|
30
|
+
StringIO.open do |io|
|
31
|
+
serialize_value io, value
|
32
|
+
io.string
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def serialize_value(output, value)
|
39
|
+
case value
|
40
|
+
when Hash
|
41
|
+
output << '{'
|
42
|
+
value.each_with_index do |(k, v), index|
|
43
|
+
output << ',' if index != 0
|
44
|
+
serialize_object_key output, k
|
45
|
+
output << ':'
|
46
|
+
serialize_value output, v
|
47
|
+
end
|
48
|
+
output << '}'
|
49
|
+
when Array
|
50
|
+
output << '['
|
51
|
+
value.each_with_index do |member, index|
|
52
|
+
output << ',' if index != 0
|
53
|
+
serialize_value output, member
|
54
|
+
end
|
55
|
+
output << ']'
|
56
|
+
when Integer, Float
|
57
|
+
output << value.to_s
|
58
|
+
when String
|
59
|
+
serialize_string output, value
|
60
|
+
when true
|
61
|
+
output << 'true'
|
62
|
+
when false
|
63
|
+
output << 'false'
|
64
|
+
when nil
|
65
|
+
output << 'null'
|
66
|
+
else
|
67
|
+
raise SerializationError, "Unexpected value of type #{value.class}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def serialize_string(output, value)
|
72
|
+
output << '"'
|
73
|
+
output << value.gsub(CHAR_TO_ESCAPE) do |character|
|
74
|
+
case character
|
75
|
+
when BACKSLASH
|
76
|
+
'\\\\'
|
77
|
+
when QUOTE
|
78
|
+
'\\"'
|
79
|
+
when CONTROL_CHAR_TO_ESCAPE
|
80
|
+
'\u%.4X' % character.ord
|
81
|
+
end
|
82
|
+
end
|
83
|
+
output << '"'
|
84
|
+
end
|
85
|
+
|
86
|
+
def serialize_object_key(output, value)
|
87
|
+
case value
|
88
|
+
when Symbol, String
|
89
|
+
serialize_string output, value.to_s
|
90
|
+
else
|
91
|
+
raise SerializationError, "Could not serialize object of type #{value.class} as object key"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/puma/launcher.rb
CHANGED
@@ -58,6 +58,13 @@ module Puma
|
|
58
58
|
|
59
59
|
@config.load
|
60
60
|
|
61
|
+
if @config.options[:bind_to_activated_sockets]
|
62
|
+
@config.options[:binds] = @binder.synthesize_binds_from_activated_fs(
|
63
|
+
@config.options[:binds],
|
64
|
+
@config.options[:bind_to_activated_sockets] == 'only'
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
61
68
|
@options = @config.options
|
62
69
|
@config.clamp
|
63
70
|
|
@@ -87,6 +94,8 @@ module Puma
|
|
87
94
|
Puma.stats_object = @runner
|
88
95
|
|
89
96
|
@status = :run
|
97
|
+
|
98
|
+
log_config if ENV['PUMA_LOG_CONFIG']
|
90
99
|
end
|
91
100
|
|
92
101
|
attr_reader :binder, :events, :config, :options, :restart_dir
|
@@ -168,6 +177,7 @@ module Puma
|
|
168
177
|
|
169
178
|
setup_signals
|
170
179
|
set_process_title
|
180
|
+
integrate_with_systemd
|
171
181
|
@runner.run
|
172
182
|
|
173
183
|
case @status
|
@@ -207,6 +217,10 @@ module Puma
|
|
207
217
|
def close_binder_listeners
|
208
218
|
@runner.close_control_listeners
|
209
219
|
@binder.close_listeners
|
220
|
+
unless @status == :restart
|
221
|
+
log "=== puma shutdown: #{Time.now} ==="
|
222
|
+
log "- Goodbye!"
|
223
|
+
end
|
210
224
|
end
|
211
225
|
|
212
226
|
# @!attribute [r] thread_status
|
@@ -229,11 +243,10 @@ module Puma
|
|
229
243
|
def write_pid
|
230
244
|
path = @options[:pidfile]
|
231
245
|
return unless path
|
232
|
-
|
233
|
-
File.
|
234
|
-
cur = Process.pid
|
246
|
+
cur_pid = Process.pid
|
247
|
+
File.write path, cur_pid, mode: 'wb:UTF-8'
|
235
248
|
at_exit do
|
236
|
-
delete_pidfile if
|
249
|
+
delete_pidfile if cur_pid == Process.pid
|
237
250
|
end
|
238
251
|
end
|
239
252
|
|
@@ -242,6 +255,7 @@ module Puma
|
|
242
255
|
end
|
243
256
|
|
244
257
|
def restart!
|
258
|
+
@events.fire_on_restart!
|
245
259
|
@config.run_hooks :on_restart, self, @events
|
246
260
|
|
247
261
|
if Puma.jruby?
|
@@ -305,10 +319,12 @@ module Puma
|
|
305
319
|
log '* Pruning Bundler environment'
|
306
320
|
home = ENV['GEM_HOME']
|
307
321
|
bundle_gemfile = Bundler.original_env['BUNDLE_GEMFILE']
|
322
|
+
bundle_app_config = Bundler.original_env['BUNDLE_APP_CONFIG']
|
308
323
|
with_unbundled_env do
|
309
324
|
ENV['GEM_HOME'] = home
|
310
325
|
ENV['BUNDLE_GEMFILE'] = bundle_gemfile
|
311
326
|
ENV['PUMA_BUNDLER_PRUNED'] = '1'
|
327
|
+
ENV["BUNDLE_APP_CONFIG"] = bundle_app_config
|
312
328
|
args = [Gem.ruby, puma_wild_location, '-I', dirs.join(':')] + @original_argv
|
313
329
|
# Ruby 2.0+ defaults to true which breaks socket activation
|
314
330
|
args += [{:close_others => false}]
|
@@ -316,6 +332,30 @@ module Puma
|
|
316
332
|
end
|
317
333
|
end
|
318
334
|
|
335
|
+
#
|
336
|
+
# Puma's systemd integration allows Puma to inform systemd:
|
337
|
+
# 1. when it has successfully started
|
338
|
+
# 2. when it is starting shutdown
|
339
|
+
# 3. periodically for a liveness check with a watchdog thread
|
340
|
+
#
|
341
|
+
|
342
|
+
def integrate_with_systemd
|
343
|
+
return unless ENV["NOTIFY_SOCKET"]
|
344
|
+
|
345
|
+
begin
|
346
|
+
require 'puma/systemd'
|
347
|
+
rescue LoadError
|
348
|
+
log "Systemd integration failed. It looks like you're trying to use systemd notify but don't have sd_notify gem installed"
|
349
|
+
return
|
350
|
+
end
|
351
|
+
|
352
|
+
log "* Enabling systemd notification integration"
|
353
|
+
|
354
|
+
systemd = Systemd.new(@events)
|
355
|
+
systemd.hook_events
|
356
|
+
systemd.start_watchdog
|
357
|
+
end
|
358
|
+
|
319
359
|
def spec_for_gem(gem_name)
|
320
360
|
Bundler.rubygems.loaded_specs(gem_name)
|
321
361
|
end
|
@@ -338,9 +378,8 @@ module Puma
|
|
338
378
|
end
|
339
379
|
|
340
380
|
def graceful_stop
|
381
|
+
@events.fire_on_stopped!
|
341
382
|
@runner.stop_blocked
|
342
|
-
log "=== puma shutdown: #{Time.now} ==="
|
343
|
-
log "- Goodbye!"
|
344
383
|
end
|
345
384
|
|
346
385
|
def set_process_title
|
@@ -493,5 +532,14 @@ module Puma
|
|
493
532
|
Bundler.with_unbundled_env { yield }
|
494
533
|
end
|
495
534
|
end
|
535
|
+
|
536
|
+
def log_config
|
537
|
+
log "Configuration:"
|
538
|
+
|
539
|
+
@config.final_options
|
540
|
+
.each { |config_key, value| log "- #{config_key}: #{value}" }
|
541
|
+
|
542
|
+
log "\n"
|
543
|
+
end
|
496
544
|
end
|
497
545
|
end
|
@@ -62,6 +62,12 @@ module Puma
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
+
if params['verification_flags']
|
66
|
+
ctx.verification_flags = params['verification_flags'].split(',').
|
67
|
+
map { |flag| MiniSSL::VERIFICATION_FLAGS.fetch(flag) }.
|
68
|
+
inject { |sum, flag| sum ? sum | flag : flag }
|
69
|
+
end
|
70
|
+
|
65
71
|
ctx
|
66
72
|
end
|
67
73
|
|