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.

Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +250 -48
  3. data/README.md +90 -24
  4. data/docs/architecture.md +57 -20
  5. data/docs/compile_options.md +21 -0
  6. data/docs/deployment.md +53 -67
  7. data/docs/fork_worker.md +2 -0
  8. data/docs/jungle/rc.d/README.md +1 -1
  9. data/docs/kubernetes.md +66 -0
  10. data/docs/plugins.md +15 -15
  11. data/docs/rails_dev_mode.md +28 -0
  12. data/docs/restart.md +7 -7
  13. data/docs/signals.md +10 -10
  14. data/docs/stats.md +142 -0
  15. data/docs/systemd.md +85 -66
  16. data/ext/puma_http11/extconf.rb +36 -6
  17. data/ext/puma_http11/http11_parser.c +64 -59
  18. data/ext/puma_http11/http11_parser.h +1 -1
  19. data/ext/puma_http11/http11_parser.java.rl +1 -1
  20. data/ext/puma_http11/http11_parser.rl +1 -1
  21. data/ext/puma_http11/http11_parser_common.rl +1 -1
  22. data/ext/puma_http11/mini_ssl.c +177 -84
  23. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +39 -41
  24. data/ext/puma_http11/puma_http11.c +8 -2
  25. data/lib/puma/app/status.rb +4 -7
  26. data/lib/puma/binder.rb +121 -46
  27. data/lib/puma/cli.rb +9 -0
  28. data/lib/puma/client.rb +58 -19
  29. data/lib/puma/cluster/worker.rb +19 -16
  30. data/lib/puma/cluster/worker_handle.rb +9 -2
  31. data/lib/puma/cluster.rb +46 -22
  32. data/lib/puma/configuration.rb +18 -2
  33. data/lib/puma/const.rb +14 -4
  34. data/lib/puma/control_cli.rb +76 -71
  35. data/lib/puma/detect.rb +14 -10
  36. data/lib/puma/dsl.rb +143 -26
  37. data/lib/puma/error_logger.rb +12 -5
  38. data/lib/puma/events.rb +18 -3
  39. data/lib/puma/json_serialization.rb +96 -0
  40. data/lib/puma/launcher.rb +54 -6
  41. data/lib/puma/minissl/context_builder.rb +6 -0
  42. data/lib/puma/minissl.rb +54 -38
  43. data/lib/puma/null_io.rb +12 -0
  44. data/lib/puma/plugin.rb +1 -1
  45. data/lib/puma/queue_close.rb +7 -7
  46. data/lib/puma/rack/builder.rb +1 -1
  47. data/lib/puma/reactor.rb +19 -12
  48. data/lib/puma/request.rb +45 -16
  49. data/lib/puma/runner.rb +38 -13
  50. data/lib/puma/server.rb +62 -123
  51. data/lib/puma/state_file.rb +5 -3
  52. data/lib/puma/systemd.rb +46 -0
  53. data/lib/puma/thread_pool.rb +10 -7
  54. data/lib/puma/util.rb +8 -1
  55. data/lib/puma.rb +36 -10
  56. data/lib/rack/handler/puma.rb +1 -0
  57. 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 "tcp://#{host}:#{port}"
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 "0, 16".
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 additional keys are required: keystore & keystore_pass.
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
- verify = opts.fetch(:verify_mode, 'none').to_s
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 0.
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] = block
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. This is off by default.
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 4 possible values:
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. **\<Any string\>** - this allows you to hardcode remote address to any value
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
@@ -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 occured error details.
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
- ioerr.puts title(options)
34
+ log title(options)
36
35
  end
37
36
 
38
- # Print occured error details only if
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
- ioerr.puts string_block.join("\n")
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.open(path, 'w') { |f| f.puts Process.pid }
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 cur == Process.pid
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