puma 5.6.5 → 6.0.0

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +102 -11
  3. data/README.md +21 -17
  4. data/bin/puma-wild +1 -1
  5. data/docs/compile_options.md +34 -0
  6. data/docs/fork_worker.md +1 -3
  7. data/docs/testing_benchmarks_local_files.md +150 -0
  8. data/docs/testing_test_rackup_ci_files.md +36 -0
  9. data/ext/puma_http11/extconf.rb +11 -8
  10. data/ext/puma_http11/http11_parser.c +1 -1
  11. data/ext/puma_http11/http11_parser.h +1 -1
  12. data/ext/puma_http11/http11_parser.java.rl +2 -2
  13. data/ext/puma_http11/http11_parser.rl +2 -2
  14. data/ext/puma_http11/http11_parser_common.rl +2 -2
  15. data/ext/puma_http11/mini_ssl.c +36 -15
  16. data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
  17. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +1 -1
  18. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +156 -53
  19. data/ext/puma_http11/puma_http11.c +17 -9
  20. data/lib/puma/app/status.rb +3 -3
  21. data/lib/puma/binder.rb +36 -42
  22. data/lib/puma/cli.rb +11 -17
  23. data/lib/puma/client.rb +22 -12
  24. data/lib/puma/cluster/worker.rb +13 -11
  25. data/lib/puma/cluster/worker_handle.rb +4 -1
  26. data/lib/puma/cluster.rb +28 -25
  27. data/lib/puma/configuration.rb +74 -58
  28. data/lib/puma/const.rb +14 -18
  29. data/lib/puma/control_cli.rb +3 -6
  30. data/lib/puma/detect.rb +2 -0
  31. data/lib/puma/dsl.rb +93 -52
  32. data/lib/puma/error_logger.rb +17 -9
  33. data/lib/puma/events.rb +6 -126
  34. data/lib/puma/io_buffer.rb +29 -4
  35. data/lib/puma/jruby_restart.rb +2 -1
  36. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  37. data/lib/puma/launcher.rb +96 -156
  38. data/lib/puma/log_writer.rb +137 -0
  39. data/lib/puma/minissl/context_builder.rb +23 -12
  40. data/lib/puma/minissl.rb +82 -11
  41. data/lib/puma/plugin/tmp_restart.rb +1 -1
  42. data/lib/puma/rack/builder.rb +4 -4
  43. data/lib/puma/rack_default.rb +1 -1
  44. data/lib/puma/reactor.rb +3 -3
  45. data/lib/puma/request.rb +292 -161
  46. data/lib/puma/runner.rb +41 -20
  47. data/lib/puma/server.rb +53 -66
  48. data/lib/puma/single.rb +10 -10
  49. data/lib/puma/state_file.rb +1 -4
  50. data/lib/puma/systemd.rb +3 -2
  51. data/lib/puma/thread_pool.rb +16 -13
  52. data/lib/puma/util.rb +0 -11
  53. data/lib/puma.rb +11 -8
  54. data/lib/rack/handler/puma.rb +9 -9
  55. metadata +7 -3
  56. data/lib/puma/queue_close.rb +0 -26
@@ -1,21 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'puma/rack/builder'
4
- require 'puma/plugin'
5
- require 'puma/const'
3
+ require_relative 'rack/builder'
4
+ require_relative 'plugin'
5
+ require_relative 'const'
6
+ # note that dsl is loaded at end of file, requires ConfigDefault constants
6
7
 
7
8
  module Puma
8
-
9
- module ConfigDefault
10
- DefaultRackup = "config.ru"
11
-
12
- DefaultTCPHost = "0.0.0.0"
13
- DefaultTCPPort = 9292
14
- DefaultWorkerCheckInterval = 5
15
- DefaultWorkerTimeout = 60
16
- DefaultWorkerShutdownTimeout = 30
17
- end
18
-
19
9
  # A class used for storing "leveled" configuration options.
20
10
  #
21
11
  # In this class any "user" specified options take precedence over any
@@ -136,7 +126,48 @@ module Puma
136
126
  # is done because an environment variable may have been modified while loading
137
127
  # configuration files.
138
128
  class Configuration
139
- include ConfigDefault
129
+ DEFAULTS = {
130
+ auto_trim_time: 30,
131
+ binds: ['tcp://0.0.0.0:9292'.freeze],
132
+ clean_thread_locals: false,
133
+ debug: false,
134
+ early_hints: nil,
135
+ environment: 'development'.freeze,
136
+ # Number of seconds to wait until we get the first data for the request
137
+ first_data_timeout: 30,
138
+ io_selector_backend: :auto,
139
+ log_requests: false,
140
+ logger: STDOUT,
141
+ # How many requests to attempt inline before sending a client back to
142
+ # the reactor to be subject to normal ordering. The idea here is that
143
+ # we amortize the cost of going back to the reactor for a well behaved
144
+ # but very "greedy" client across 10 requests. This prevents a not
145
+ # well behaved client from monopolizing the thread forever.
146
+ max_fast_inline: 10,
147
+ max_threads: Puma.mri? ? 5 : 16,
148
+ min_threads: 0,
149
+ mode: :http,
150
+ mutate_stdout_and_stderr_to_sync_on_write: true,
151
+ out_of_band: [],
152
+ # Number of seconds for another request within a persistent session.
153
+ persistent_timeout: 20,
154
+ queue_requests: true,
155
+ rackup: 'config.ru'.freeze,
156
+ raise_exception_on_sigterm: true,
157
+ reaping_time: 1,
158
+ remote_address: :socket,
159
+ silence_single_worker_warning: false,
160
+ tag: File.basename(Dir.getwd),
161
+ tcp_host: '0.0.0.0'.freeze,
162
+ tcp_port: 9292,
163
+ wait_for_less_busy_worker: 0.005,
164
+ worker_boot_timeout: 60,
165
+ worker_check_interval: 5,
166
+ worker_culling_strategy: :youngest,
167
+ worker_shutdown_timeout: 30,
168
+ worker_timeout: 60,
169
+ workers: 0,
170
+ }
140
171
 
141
172
  def initialize(user_options={}, default_options = {}, &block)
142
173
  default_options = self.puma_default_options.merge(default_options)
@@ -181,37 +212,22 @@ module Puma
181
212
  self
182
213
  end
183
214
 
184
- # @version 5.0.0
185
- def default_max_threads
186
- Puma.mri? ? 5 : 16
215
+ def puma_default_options
216
+ defaults = DEFAULTS.dup
217
+ puma_options_from_env.each { |k,v| defaults[k] = v if v }
218
+ defaults
187
219
  end
188
220
 
189
- def puma_default_options
221
+ def puma_options_from_env
222
+ min = ENV['PUMA_MIN_THREADS'] || ENV['MIN_THREADS']
223
+ max = ENV['PUMA_MAX_THREADS'] || ENV['MAX_THREADS']
224
+ workers = ENV['WEB_CONCURRENCY']
225
+
190
226
  {
191
- :min_threads => Integer(ENV['PUMA_MIN_THREADS'] || ENV['MIN_THREADS'] || 0),
192
- :max_threads => Integer(ENV['PUMA_MAX_THREADS'] || ENV['MAX_THREADS'] || default_max_threads),
193
- :log_requests => false,
194
- :debug => false,
195
- :binds => ["tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"],
196
- :workers => Integer(ENV['WEB_CONCURRENCY'] || 0),
197
- :silence_single_worker_warning => false,
198
- :mode => :http,
199
- :worker_check_interval => DefaultWorkerCheckInterval,
200
- :worker_timeout => DefaultWorkerTimeout,
201
- :worker_boot_timeout => DefaultWorkerTimeout,
202
- :worker_shutdown_timeout => DefaultWorkerShutdownTimeout,
203
- :worker_culling_strategy => :youngest,
204
- :remote_address => :socket,
205
- :tag => method(:infer_tag),
206
- :environment => -> { ENV['APP_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development' },
207
- :rackup => DefaultRackup,
208
- :logger => STDOUT,
209
- :persistent_timeout => Const::PERSISTENT_TIMEOUT,
210
- :first_data_timeout => Const::FIRST_DATA_TIMEOUT,
211
- :raise_exception_on_sigterm => true,
212
- :max_fast_inline => Const::MAX_FAST_INLINE,
213
- :io_selector_backend => :auto,
214
- :mutate_stdout_and_stderr_to_sync_on_write => true,
227
+ min_threads: min && Integer(min),
228
+ max_threads: max && Integer(max),
229
+ workers: workers && Integer(workers),
230
+ environment: ENV['APP_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV'],
215
231
  }
216
232
  end
217
233
 
@@ -227,7 +243,7 @@ module Puma
227
243
  return [] if files == ['-']
228
244
  return files if files.any?
229
245
 
230
- first_default_file = %W(config/puma/#{environment_str}.rb config/puma.rb).find do |f|
246
+ first_default_file = %W(config/puma/#{@options[:environment]}.rb config/puma.rb).find do |f|
231
247
  File.exist?(f)
232
248
  end
233
249
 
@@ -270,7 +286,7 @@ module Puma
270
286
  found = options[:app] || load_rackup
271
287
 
272
288
  if @options[:log_requests]
273
- require 'puma/commonlogger'
289
+ require_relative 'commonlogger'
274
290
  logger = @options[:logger]
275
291
  found = CommonLogger.new(found, logger)
276
292
  end
@@ -283,21 +299,25 @@ module Puma
283
299
  @options[:environment]
284
300
  end
285
301
 
286
- def environment_str
287
- environment.respond_to?(:call) ? environment.call : environment
288
- end
289
-
290
302
  def load_plugin(name)
291
303
  @plugins.create name
292
304
  end
293
305
 
294
- def run_hooks(key, arg, events)
306
+ # @param key [:Symbol] hook to run
307
+ # @param arg [Launcher, Int] `:on_restart` passes Launcher
308
+ #
309
+ def run_hooks(key, arg, log_writer, hook_data = nil)
295
310
  @options.all_of(key).each do |b|
296
311
  begin
297
- b.call arg
312
+ if Array === b
313
+ hook_data[b[1]] ||= Hash.new
314
+ b[0].call arg, hook_data[b[1]]
315
+ else
316
+ b.call arg
317
+ end
298
318
  rescue => e
299
- events.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
300
- events.debug e.backtrace.join("\n")
319
+ log_writer.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
320
+ log_writer.debug e.backtrace.join("\n")
301
321
  end
302
322
  end
303
323
  end
@@ -315,10 +335,6 @@ module Puma
315
335
 
316
336
  private
317
337
 
318
- def infer_tag
319
- File.basename(Dir.getwd)
320
- end
321
-
322
338
  # Load and use the normal Rack builder if we can, otherwise
323
339
  # fallback to our minimal version.
324
340
  def rack_builder
@@ -368,4 +384,4 @@ module Puma
368
384
  end
369
385
  end
370
386
 
371
- require 'puma/dsl'
387
+ require_relative 'dsl'
data/lib/puma/const.rb CHANGED
@@ -100,32 +100,17 @@ module Puma
100
100
  # too taxing on performance.
101
101
  module Const
102
102
 
103
- PUMA_VERSION = VERSION = "5.6.5".freeze
104
- CODE_NAME = "Birdie's Version".freeze
103
+ PUMA_VERSION = VERSION = "6.0.0".freeze
104
+ CODE_NAME = "Sunflower".freeze
105
105
 
106
106
  PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
107
107
 
108
108
  FAST_TRACK_KA_TIMEOUT = 0.2
109
109
 
110
- # The default number of seconds for another request within a persistent
111
- # session.
112
- PERSISTENT_TIMEOUT = 20
113
-
114
- # The default number of seconds to wait until we get the first data
115
- # for the request
116
- FIRST_DATA_TIMEOUT = 30
117
-
118
110
  # How long to wait when getting some write blocking on the socket when
119
111
  # sending data back
120
112
  WRITE_TIMEOUT = 10
121
113
 
122
- # How many requests to attempt inline before sending a client back to
123
- # the reactor to be subject to normal ordering. The idea here is that
124
- # we amortize the cost of going back to the reactor for a well behaved
125
- # but very "greedy" client across 10 requests. This prevents a not
126
- # well behaved client from monopolizing the thread forever.
127
- MAX_FAST_INLINE = 10
128
-
129
114
  # The original URI requested by the client.
130
115
  REQUEST_URI= 'REQUEST_URI'.freeze
131
116
  REQUEST_PATH = 'REQUEST_PATH'.freeze
@@ -163,6 +148,14 @@ module Puma
163
148
 
164
149
  REQUEST_METHOD = "REQUEST_METHOD".freeze
165
150
  HEAD = "HEAD".freeze
151
+ GET = "GET".freeze
152
+ POST = "POST".freeze
153
+ PUT = "PUT".freeze
154
+ DELETE = "DELETE".freeze
155
+ OPTIONS = "OPTIONS".freeze
156
+ TRACE = "TRACE".freeze
157
+ PATCH = "PATCH".freeze
158
+ SUPPORTED_HTTP_METHODS = [HEAD, GET, POST, PUT, DELETE, OPTIONS, TRACE, PATCH].freeze
166
159
  # ETag is based on the apache standard of hex mtime-size-inode (inode is 0 on win32)
167
160
  LINE_END = "\r\n".freeze
168
161
  REMOTE_ADDR = "REMOTE_ADDR".freeze
@@ -177,7 +170,10 @@ module Puma
177
170
  PORT_80 = "80".freeze
178
171
  PORT_443 = "443".freeze
179
172
  LOCALHOST = "localhost".freeze
180
- LOCALHOST_IP = "127.0.0.1".freeze
173
+ LOCALHOST_IPV4 = "127.0.0.1".freeze
174
+ LOCALHOST_IPV6 = "::1".freeze
175
+ UNSPECIFIED_IPV4 = "0.0.0.0".freeze
176
+ UNSPECIFIED_IPV6 = "::".freeze
181
177
 
182
178
  SERVER_PROTOCOL = "SERVER_PROTOCOL".freeze
183
179
  HTTP_11 = "HTTP/1.1".freeze
@@ -33,9 +33,6 @@ module Puma
33
33
  'worker-count-up' => 'SIGTTIN'
34
34
  }.freeze
35
35
 
36
- # @deprecated 6.0.0
37
- COMMANDS = CMD_PATH_SIG_MAP.keys.freeze
38
-
39
36
  # commands that cannot be used in a request
40
37
  NO_REQ_COMMANDS = %w[info reopen-log worker-count-down worker-count-up].freeze
41
38
 
@@ -287,7 +284,7 @@ module Puma
287
284
 
288
285
  private
289
286
  def start
290
- require 'puma/cli'
287
+ require_relative 'cli'
291
288
 
292
289
  run_args = []
293
290
 
@@ -299,13 +296,13 @@ module Puma
299
296
  run_args += ["-C", @config_file] if @config_file
300
297
  run_args += ["-e", @environment] if @environment
301
298
 
302
- events = Puma::Events.new @stdout, @stderr
299
+ log_writer = Puma::LogWriter.new(@stdout, @stderr)
303
300
 
304
301
  # replace $0 because puma use it to generate restart command
305
302
  puma_cmd = $0.gsub(/pumactl$/, 'puma')
306
303
  $0 = puma_cmd if File.exist?(puma_cmd)
307
304
 
308
- cli = Puma::CLI.new run_args, events
305
+ cli = Puma::CLI.new run_args, log_writer
309
306
  cli.run
310
307
  end
311
308
  end
data/lib/puma/detect.rb CHANGED
@@ -8,6 +8,8 @@ module Puma
8
8
  # @version 5.2.1
9
9
  HAS_FORK = ::Process.respond_to? :fork
10
10
 
11
+ HAS_NATIVE_IO_WAIT = ::IO.public_instance_methods(false).include? :wait_readable
12
+
11
13
  IS_JRUBY = Object.const_defined? :JRUBY_VERSION
12
14
 
13
15
  IS_OSX = RUBY_PLATFORM.include? 'darwin'
data/lib/puma/dsl.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'puma/const'
4
- require 'puma/util'
3
+ require_relative 'const'
4
+ require_relative 'util'
5
5
 
6
6
  module Puma
7
7
  # The methods that are available for use inside the configuration file.
@@ -32,8 +32,24 @@ module Puma
32
32
  # You can also find many examples being used by the test suite in
33
33
  # +test/config+.
34
34
  #
35
+ # Puma v6 adds the option to specify a key name (String or Symbol) to the
36
+ # hooks that run inside the forked workers. All the hooks run inside the
37
+ # {Puma::Cluster::Worker#run} method.
38
+ #
39
+ # Previously, the worker index and the LogWriter instance were passed to the
40
+ # hook blocks/procs. If a key name is specified, a hash is passed as the last
41
+ # parameter. This allows storage of data, typically objects that are created
42
+ # before the worker that need to be passed to the hook when the worker is shutdown.
43
+ #
44
+ # The following hooks have been updated:
45
+ #
46
+ # | DSL Method | Options Key | Fork Block Location |
47
+ # | on_worker_boot | :before_worker_boot | inside, before |
48
+ # | on_worker_shutdown | :before_worker_shutdown | inside, after |
49
+ # | on_refork | :before_refork | inside |
50
+ #
35
51
  class DSL
36
- include ConfigDefault
52
+ ON_WORKER_KEY = [String, Symbol].freeze
37
53
 
38
54
  # convenience method so logic can be used in CI
39
55
  # @see ssl_bind
@@ -52,25 +68,53 @@ module Puma
52
68
  backlog_str = opts[:backlog] ? "&backlog=#{Integer(opts[:backlog])}" : ''
53
69
 
54
70
  if defined?(JRUBY_VERSION)
55
- ssl_cipher_list = opts[:ssl_cipher_list] ?
56
- "&ssl_cipher_list=#{opts[:ssl_cipher_list]}" : nil
71
+ cipher_suites = opts[:ssl_cipher_list] ? "&ssl_cipher_list=#{opts[:ssl_cipher_list]}" : nil # old name
72
+ cipher_suites = "#{cipher_suites}&cipher_suites=#{opts[:cipher_suites]}" if opts[:cipher_suites]
73
+ protocols = opts[:protocols] ? "&protocols=#{opts[:protocols]}" : nil
57
74
 
58
75
  keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
76
+ keystore_additions = "#{keystore_additions}&keystore-type=#{opts[:keystore_type]}" if opts[:keystore_type]
77
+ if opts[:truststore]
78
+ truststore_additions = "&truststore=#{opts[:truststore]}"
79
+ truststore_additions = "#{truststore_additions}&truststore-pass=#{opts[:truststore_pass]}" if opts[:truststore_pass]
80
+ truststore_additions = "#{truststore_additions}&truststore-type=#{opts[:truststore_type]}" if opts[:truststore_type]
81
+ end
59
82
 
60
- "ssl://#{host}:#{port}?#{keystore_additions}#{ssl_cipher_list}" \
83
+ "ssl://#{host}:#{port}?#{keystore_additions}#{truststore_additions}#{cipher_suites}#{protocols}" \
61
84
  "&verify_mode=#{verify}#{tls_str}#{ca_additions}#{backlog_str}"
62
85
  else
63
- ssl_cipher_filter = opts[:ssl_cipher_filter] ?
64
- "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" : nil
65
-
66
- v_flags = (ary = opts[:verification_flags]) ?
67
- "&verification_flags=#{Array(ary).join ','}" : nil
68
-
69
- cert_flags = (cert = opts[:cert]) ? "cert=#{Puma::Util.escape(opts[:cert])}" : nil
70
- key_flags = (cert = opts[:key]) ? "&key=#{Puma::Util.escape(opts[:key])}" : nil
86
+ ssl_cipher_filter = opts[:ssl_cipher_filter] ? "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" : nil
87
+ v_flags = (ary = opts[:verification_flags]) ? "&verification_flags=#{Array(ary).join ','}" : nil
88
+
89
+ cert_flags = (cert = opts[:cert]) ? "cert=#{Puma::Util.escape(cert)}" : nil
90
+ key_flags = (key = opts[:key]) ? "&key=#{Puma::Util.escape(key)}" : nil
91
+
92
+ reuse_flag =
93
+ if (reuse = opts[:reuse])
94
+ if reuse == true
95
+ '&reuse=dflt'
96
+ elsif reuse.is_a?(Hash) && (reuse.key?(:size) || reuse.key?(:timeout))
97
+ val = +''
98
+ if (size = reuse[:size]) && Integer === size
99
+ val << size.to_s
100
+ end
101
+ if (timeout = reuse[:timeout]) && Integer === timeout
102
+ val << ",#{timeout}"
103
+ end
104
+ if val.empty?
105
+ nil
106
+ else
107
+ "&reuse=#{val}"
108
+ end
109
+ else
110
+ nil
111
+ end
112
+ else
113
+ nil
114
+ end
71
115
 
72
- "ssl://#{host}:#{port}?#{cert_flags}#{key_flags}" \
73
- "#{ssl_cipher_filter}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}#{backlog_str}"
116
+ "ssl://#{host}:#{port}?#{cert_flags}#{key_flags}#{ssl_cipher_filter}" \
117
+ "#{reuse_flag}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}#{backlog_str}"
74
118
  end
75
119
  end
76
120
 
@@ -106,7 +150,7 @@ module Puma
106
150
  end
107
151
 
108
152
  def default_host
109
- @options[:default_host] || Configuration::DefaultTCPHost
153
+ @options[:default_host] || Configuration::DEFAULTS[:tcp_host]
110
154
  end
111
155
 
112
156
  def inject(&blk)
@@ -452,6 +496,10 @@ module Puma
452
496
  # Puma will assume you are using the +localhost+ gem and try to load the
453
497
  # appropriate files.
454
498
  #
499
+ # When using the options hash parameter, the `reuse:` value is either
500
+ # `true`, which sets reuse 'on' with default values, or a hash, with `:size`
501
+ # and/or `:timeout` keys, each with integer values.
502
+ #
455
503
  # @example
456
504
  # ssl_bind '127.0.0.1', '9292', {
457
505
  # cert: path_to_cert,
@@ -459,6 +507,7 @@ module Puma
459
507
  # ssl_cipher_filter: cipher_filter, # optional
460
508
  # verify_mode: verify_mode, # default 'none'
461
509
  # verification_flags: flags, # optional, not supported by JRuby
510
+ # reuse: true # optional
462
511
  # }
463
512
  #
464
513
  # @example Using self-signed certificate with the +localhost+ gem:
@@ -468,6 +517,7 @@ module Puma
468
517
  # ssl_bind '127.0.0.1', '9292', {
469
518
  # cert_pem: File.read(path_to_cert),
470
519
  # key_pem: File.read(path_to_key),
520
+ # reuse: {size: 2_000, timeout: 20} # optional
471
521
  # }
472
522
  #
473
523
  # @example For JRuby, two keys are required: +keystore+ & +keystore_pass+
@@ -560,9 +610,8 @@ module Puma
560
610
  # on_worker_boot do
561
611
  # puts 'Before worker boot...'
562
612
  # end
563
- def on_worker_boot(&block)
564
- @options[:before_worker_boot] ||= []
565
- @options[:before_worker_boot] << block
613
+ def on_worker_boot(key = nil, &block)
614
+ process_hook :before_worker_boot, key, block, 'on_worker_boot'
566
615
  end
567
616
 
568
617
  # Code to run immediately before a worker shuts
@@ -577,9 +626,8 @@ module Puma
577
626
  # on_worker_shutdown do
578
627
  # puts 'On worker shutdown...'
579
628
  # end
580
- def on_worker_shutdown(&block)
581
- @options[:before_worker_shutdown] ||= []
582
- @options[:before_worker_shutdown] << block
629
+ def on_worker_shutdown(key = nil, &block)
630
+ process_hook :before_worker_shutdown, key, block, 'on_worker_shutdown'
583
631
  end
584
632
 
585
633
  # Code to run in the master right before a worker is started. The worker's
@@ -593,8 +641,7 @@ module Puma
593
641
  # puts 'Before worker fork...'
594
642
  # end
595
643
  def on_worker_fork(&block)
596
- @options[:before_worker_fork] ||= []
597
- @options[:before_worker_fork] << block
644
+ process_hook :before_worker_fork, nil, block, 'on_worker_fork'
598
645
  end
599
646
 
600
647
  # Code to run in the master after a worker has been started. The worker's
@@ -608,8 +655,7 @@ module Puma
608
655
  # puts 'After worker fork...'
609
656
  # end
610
657
  def after_worker_fork(&block)
611
- @options[:after_worker_fork] ||= []
612
- @options[:after_worker_fork] << block
658
+ process_hook :after_worker_fork, nil, block, 'after_worker_fork'
613
659
  end
614
660
 
615
661
  alias_method :after_worker_boot, :after_worker_fork
@@ -632,9 +678,8 @@ module Puma
632
678
  # end
633
679
  # @version 5.0.0
634
680
  #
635
- def on_refork(&block)
636
- @options[:before_refork] ||= []
637
- @options[:before_refork] << block
681
+ def on_refork(key = nil, &block)
682
+ process_hook :before_refork, key, block, 'on_refork'
638
683
  end
639
684
 
640
685
  # Code to run out-of-band when the worker is idle.
@@ -647,8 +692,7 @@ module Puma
647
692
  #
648
693
  # This can be called multiple times to add several hooks.
649
694
  def out_of_band(&block)
650
- @options[:out_of_band] ||= []
651
- @options[:out_of_band] << block
695
+ process_hook :out_of_band, nil, block, 'out_of_band'
652
696
  end
653
697
 
654
698
  # The directory to operate out of.
@@ -778,7 +822,7 @@ module Puma
778
822
  #
779
823
  def worker_timeout(timeout)
780
824
  timeout = Integer(timeout)
781
- min = @options.fetch(:worker_check_interval, Puma::ConfigDefault::DefaultWorkerCheckInterval)
825
+ min = @options.fetch(:worker_check_interval, Configuration::DEFAULTS[:worker_check_interval])
782
826
 
783
827
  if timeout <= min
784
828
  raise "The minimum worker_timeout must be greater than the worker reporting interval (#{min})"
@@ -882,13 +926,16 @@ module Puma
882
926
  # There are 5 possible values:
883
927
  #
884
928
  # 1. **:socket** (the default) - read the peername from the socket using the
885
- # syscall. This is the normal behavior.
929
+ # syscall. This is the normal behavior. If this fails for any reason (e.g.,
930
+ # if the peer disconnects between the connection being accepted and the getpeername
931
+ # system call), Puma will return "0.0.0.0"
886
932
  # 2. **:localhost** - set the remote address to "127.0.0.1"
887
933
  # 3. **header: <http_header>**- set the remote address to the value of the
888
934
  # provided http header. For instance:
889
935
  # `set_remote_address header: "X-Real-IP"`.
890
936
  # Only the first word (as separated by spaces or comma) is used, allowing
891
- # headers such as X-Forwarded-For to be used as well.
937
+ # headers such as X-Forwarded-For to be used as well. If this header is absent,
938
+ # Puma will fall back to the behavior of :socket
892
939
  # 4. **proxy_protocol: :v1**- set the remote address to the value read from the
893
940
  # HAproxy PROXY protocol, version 1. If the request does not have the PROXY
894
941
  # protocol attached to it, will fall back to :socket
@@ -942,23 +989,6 @@ module Puma
942
989
  @options[:fork_worker] = Integer(after_requests)
943
990
  end
944
991
 
945
- # When enabled, Puma will GC 4 times before forking workers.
946
- # If available (Ruby 2.7+), we will also call GC.compact.
947
- # Not recommended for non-MRI Rubies.
948
- #
949
- # Based on the work of Koichi Sasada and Aaron Patterson, this option may
950
- # decrease memory utilization of preload-enabled cluster-mode Pumas. It will
951
- # also increase time to boot and fork. See your logs for details on how much
952
- # time this adds to your boot process. For most apps, it will be less than one
953
- # second.
954
- #
955
- # @see Puma::Cluster#nakayoshi_gc
956
- # @version 5.0.0
957
- #
958
- def nakayoshi_fork(enabled=true)
959
- @options[:nakayoshi_fork] = enabled
960
- end
961
-
962
992
  # The number of requests to attempt inline before sending a client back to
963
993
  # the reactor to be subject to normal ordering.
964
994
  #
@@ -1008,5 +1038,16 @@ module Puma
1008
1038
  end
1009
1039
  end
1010
1040
  end
1041
+
1042
+ def process_hook(options_key, key, block, meth)
1043
+ @options[options_key] ||= []
1044
+ if ON_WORKER_KEY.include? key.class
1045
+ @options[options_key] << [block, key.to_sym]
1046
+ elsif key.nil?
1047
+ @options[options_key] << block
1048
+ else
1049
+ raise "'#{method}' key must be String or Symbol"
1050
+ end
1051
+ end
1011
1052
  end
1012
1053
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'puma/const'
3
+ require_relative 'const'
4
4
 
5
5
  module Puma
6
6
  # The implementation of a detailed error logging.
@@ -13,6 +13,8 @@ module Puma
13
13
 
14
14
  REQUEST_FORMAT = %{"%s %s%s" - (%s)}
15
15
 
16
+ LOG_QUEUE = Queue.new
17
+
16
18
  def initialize(ioerr)
17
19
  @ioerr = ioerr
18
20
 
@@ -31,7 +33,7 @@ module Puma
31
33
  # and before all remaining info.
32
34
  #
33
35
  def info(options={})
34
- log title(options)
36
+ internal_write title(options)
35
37
  end
36
38
 
37
39
  # Print occurred error details only if
@@ -53,7 +55,7 @@ module Puma
53
55
  string_block << request_dump(req) if request_parsed?(req)
54
56
  string_block << error.backtrace if error
55
57
 
56
- log string_block.join("\n")
58
+ internal_write string_block.join("\n")
57
59
  end
58
60
 
59
61
  def title(options={})
@@ -93,12 +95,18 @@ module Puma
93
95
  req && req.env[REQUEST_METHOD]
94
96
  end
95
97
 
96
- private
97
-
98
- def log(str)
99
- ioerr.puts str
100
-
101
- ioerr.flush unless ioerr.sync
98
+ def internal_write(str)
99
+ LOG_QUEUE << str
100
+ while (w_str = LOG_QUEUE.pop(true)) do
101
+ begin
102
+ @ioerr.is_a?(IO) and @ioerr.wait_writable(1)
103
+ @ioerr.write "#{w_str}\n"
104
+ @ioerr.flush unless @ioerr.sync
105
+ rescue Errno::EPIPE, Errno::EBADF, IOError
106
+ end
107
+ end
108
+ rescue ThreadError
102
109
  end
110
+ private :internal_write
103
111
  end
104
112
  end