puma 5.3.2 → 5.6.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of puma might be problematic. Click here for more details.

Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +211 -11
  3. data/README.md +47 -6
  4. data/docs/architecture.md +49 -16
  5. data/docs/compile_options.md +4 -2
  6. data/docs/deployment.md +53 -67
  7. data/docs/plugins.md +15 -15
  8. data/docs/rails_dev_mode.md +2 -3
  9. data/docs/restart.md +6 -6
  10. data/docs/signals.md +11 -10
  11. data/docs/stats.md +8 -8
  12. data/docs/systemd.md +64 -67
  13. data/ext/puma_http11/extconf.rb +34 -6
  14. data/ext/puma_http11/http11_parser.c +23 -10
  15. data/ext/puma_http11/http11_parser_common.rl +1 -1
  16. data/ext/puma_http11/mini_ssl.c +90 -12
  17. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +49 -47
  18. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +38 -55
  19. data/ext/puma_http11/puma_http11.c +1 -1
  20. data/lib/puma/app/status.rb +7 -4
  21. data/lib/puma/binder.rb +51 -6
  22. data/lib/puma/cli.rb +14 -4
  23. data/lib/puma/client.rb +143 -25
  24. data/lib/puma/cluster/worker.rb +8 -18
  25. data/lib/puma/cluster/worker_handle.rb +4 -0
  26. data/lib/puma/cluster.rb +30 -24
  27. data/lib/puma/configuration.rb +4 -1
  28. data/lib/puma/const.rb +9 -8
  29. data/lib/puma/control_cli.rb +19 -13
  30. data/lib/puma/detect.rb +8 -2
  31. data/lib/puma/dsl.rb +111 -13
  32. data/lib/puma/{json.rb → json_serialization.rb} +1 -1
  33. data/lib/puma/launcher.rb +15 -1
  34. data/lib/puma/minissl/context_builder.rb +8 -6
  35. data/lib/puma/minissl.rb +33 -27
  36. data/lib/puma/null_io.rb +5 -0
  37. data/lib/puma/plugin.rb +2 -2
  38. data/lib/puma/rack/builder.rb +1 -1
  39. data/lib/puma/request.rb +19 -10
  40. data/lib/puma/runner.rb +22 -8
  41. data/lib/puma/server.rb +37 -29
  42. data/lib/puma/state_file.rb +42 -7
  43. data/lib/puma/thread_pool.rb +7 -5
  44. data/lib/puma/util.rb +20 -4
  45. data/lib/puma.rb +6 -4
  46. data/lib/rack/version_restriction.rb +15 -0
  47. data/tools/Dockerfile +1 -1
  48. metadata +8 -7
data/lib/puma/dsl.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'puma/const'
4
+ require 'puma/util'
4
5
 
5
6
  module Puma
6
7
  # The methods that are available for use inside the configuration file.
@@ -46,7 +47,9 @@ module Puma
46
47
  else ''
47
48
  end
48
49
 
49
- ca_additions = "&ca=#{opts[:ca]}" if ['peer', 'force_peer'].include?(verify)
50
+ ca_additions = "&ca=#{Puma::Util.escape(opts[:ca])}" if ['peer', 'force_peer'].include?(verify)
51
+
52
+ backlog_str = opts[:backlog] ? "&backlog=#{Integer(opts[:backlog])}" : ''
50
53
 
51
54
  if defined?(JRUBY_VERSION)
52
55
  ssl_cipher_list = opts[:ssl_cipher_list] ?
@@ -55,7 +58,7 @@ module Puma
55
58
  keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
56
59
 
57
60
  "ssl://#{host}:#{port}?#{keystore_additions}#{ssl_cipher_list}" \
58
- "&verify_mode=#{verify}#{tls_str}#{ca_additions}"
61
+ "&verify_mode=#{verify}#{tls_str}#{ca_additions}#{backlog_str}"
59
62
  else
60
63
  ssl_cipher_filter = opts[:ssl_cipher_filter] ?
61
64
  "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" : nil
@@ -63,8 +66,11 @@ module Puma
63
66
  v_flags = (ary = opts[:verification_flags]) ?
64
67
  "&verification_flags=#{Array(ary).join ','}" : nil
65
68
 
66
- "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}" \
67
- "#{ssl_cipher_filter}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}"
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
71
+
72
+ "ssl://#{host}:#{port}?#{cert_flags}#{key_flags}" \
73
+ "#{ssl_cipher_filter}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}#{backlog_str}"
68
74
  end
69
75
  end
70
76
 
@@ -191,7 +197,7 @@ module Puma
191
197
  end
192
198
 
193
199
  # Bind the server to +url+. "tcp://", "unix://" and "ssl://" are the only
194
- # accepted protocols. Multiple urls can be bound to, calling `bind` does
200
+ # accepted protocols. Multiple urls can be bound to, calling +bind+ does
195
201
  # not overwrite previous bindings.
196
202
  #
197
203
  # The default is "tcp://0.0.0.0:9292".
@@ -381,6 +387,13 @@ module Puma
381
387
  @options[:rackup] ||= path.to_s
382
388
  end
383
389
 
390
+ # Allows setting `env['rack.url_scheme']`.
391
+ # Only necessary if X-Forwarded-Proto is not being set by your proxy
392
+ # Normal values are 'http' or 'https'.
393
+ def rack_url_scheme(scheme=nil)
394
+ @options[:rack_url_scheme] = scheme
395
+ end
396
+
384
397
  def early_hints(answer=true)
385
398
  @options[:early_hints] = answer
386
399
  end
@@ -429,8 +442,15 @@ module Puma
429
442
  @options[:max_threads] = max
430
443
  end
431
444
 
432
- # Instead of `bind 'ssl://127.0.0.1:9292?key=key_path&cert=cert_path'` you
433
- # can also use the this method.
445
+ # Instead of using +bind+ and manually constructing a URI like:
446
+ #
447
+ # bind 'ssl://127.0.0.1:9292?key=key_path&cert=cert_path'
448
+ #
449
+ # you can use the this method.
450
+ #
451
+ # When binding on localhost you don't need to specify +cert+ and +key+,
452
+ # Puma will assume you are using the +localhost+ gem and try to load the
453
+ # appropriate files.
434
454
  #
435
455
  # @example
436
456
  # ssl_bind '127.0.0.1', '9292', {
@@ -440,14 +460,25 @@ module Puma
440
460
  # verify_mode: verify_mode, # default 'none'
441
461
  # verification_flags: flags, # optional, not supported by JRuby
442
462
  # }
443
- # @example For JRuby, two keys are required: keystore & keystore_pass.
463
+ #
464
+ # @example Using self-signed certificate with the +localhost+ gem:
465
+ # ssl_bind '127.0.0.1', '9292'
466
+ #
467
+ # @example Alternatively, you can provide +cert_pem+ and +key_pem+:
468
+ # ssl_bind '127.0.0.1', '9292', {
469
+ # cert_pem: File.read(path_to_cert),
470
+ # key_pem: File.read(path_to_key),
471
+ # }
472
+ #
473
+ # @example For JRuby, two keys are required: +keystore+ & +keystore_pass+
444
474
  # ssl_bind '127.0.0.1', '9292', {
445
475
  # keystore: path_to_keystore,
446
476
  # keystore_pass: password,
447
477
  # ssl_cipher_list: cipher_list, # optional
448
478
  # verify_mode: verify_mode # default 'none'
449
479
  # }
450
- def ssl_bind(host, port, opts)
480
+ def ssl_bind(host, port, opts = {})
481
+ add_pem_values_to_options_store(opts)
451
482
  bind self.class.ssl_bind_str(host, port, opts)
452
483
  end
453
484
 
@@ -578,7 +609,7 @@ module Puma
578
609
  # end
579
610
  def after_worker_fork(&block)
580
611
  @options[:after_worker_fork] ||= []
581
- @options[:after_worker_fork] = block
612
+ @options[:after_worker_fork] << block
582
613
  end
583
614
 
584
615
  alias_method :after_worker_boot, :after_worker_fork
@@ -720,6 +751,19 @@ module Puma
720
751
  @options[:tag] = string.to_s
721
752
  end
722
753
 
754
+ # Change the default interval for checking workers.
755
+ #
756
+ # The default value is 5 seconds.
757
+ #
758
+ # @note Cluster mode only.
759
+ # @example
760
+ # worker_check_interval 5
761
+ # @see Puma::Cluster#check_workers
762
+ #
763
+ def worker_check_interval(interval)
764
+ @options[:worker_check_interval] = Integer(interval)
765
+ end
766
+
723
767
  # Verifies that all workers have checked in to the master process within
724
768
  # the given timeout. If not the worker process will be restarted. This is
725
769
  # not a request timeout, it is to protect against a hung or dead process.
@@ -734,7 +778,7 @@ module Puma
734
778
  #
735
779
  def worker_timeout(timeout)
736
780
  timeout = Integer(timeout)
737
- min = Const::WORKER_CHECK_INTERVAL
781
+ min = @options.fetch(:worker_check_interval, Puma::ConfigDefault::DefaultWorkerCheckInterval)
738
782
 
739
783
  if timeout <= min
740
784
  raise "The minimum worker_timeout must be greater than the worker reporting interval (#{min})"
@@ -766,6 +810,30 @@ module Puma
766
810
  @options[:worker_shutdown_timeout] = Integer(timeout)
767
811
  end
768
812
 
813
+ # Set the strategy for worker culling.
814
+ #
815
+ # There are two possible values:
816
+ #
817
+ # 1. **:youngest** - the youngest workers (i.e. the workers that were
818
+ # the most recently started) will be culled.
819
+ # 2. **:oldest** - the oldest workers (i.e. the workers that were started
820
+ # the longest time ago) will be culled.
821
+ #
822
+ # @note Cluster mode only.
823
+ # @example
824
+ # worker_culling_strategy :oldest
825
+ # @see Puma::Cluster#cull_workers
826
+ #
827
+ def worker_culling_strategy(strategy)
828
+ stategy = strategy.to_sym
829
+
830
+ if ![:youngest, :oldest].include?(strategy)
831
+ raise "Invalid value for worker_culling_strategy - #{stategy}"
832
+ end
833
+
834
+ @options[:worker_culling_strategy] = strategy
835
+ end
836
+
769
837
  # When set to true (the default), workers accept all requests
770
838
  # and queue them before passing them to the handlers.
771
839
  # When set to false, each worker process accepts exactly as
@@ -811,7 +879,7 @@ module Puma
811
879
  # a kernel syscall is required which for very fast rack handlers
812
880
  # slows down the handling significantly.
813
881
  #
814
- # There are 4 possible values:
882
+ # There are 5 possible values:
815
883
  #
816
884
  # 1. **:socket** (the default) - read the peername from the socket using the
817
885
  # syscall. This is the normal behavior.
@@ -821,7 +889,10 @@ module Puma
821
889
  # `set_remote_address header: "X-Real-IP"`.
822
890
  # Only the first word (as separated by spaces or comma) is used, allowing
823
891
  # headers such as X-Forwarded-For to be used as well.
824
- # 4. **\<Any string\>** - this allows you to hardcode remote address to any value
892
+ # 4. **proxy_protocol: :v1**- set the remote address to the value read from the
893
+ # HAproxy PROXY protocol, version 1. If the request does not have the PROXY
894
+ # protocol attached to it, will fall back to :socket
895
+ # 5. **\<Any string\>** - this allows you to hardcode remote address to any value
825
896
  # you wish. Because Puma never uses this field anyway, it's format is
826
897
  # entirely in your hands.
827
898
  #
@@ -839,6 +910,13 @@ module Puma
839
910
  if hdr = val[:header]
840
911
  @options[:remote_address] = :header
841
912
  @options[:remote_address_header] = "HTTP_" + hdr.upcase.tr("-", "_")
913
+ elsif protocol_version = val[:proxy_protocol]
914
+ @options[:remote_address] = :proxy_protocol
915
+ protocol_version = protocol_version.downcase.to_sym
916
+ unless [:v1].include?(protocol_version)
917
+ raise "Invalid value for proxy_protocol - #{protocol_version.inspect}"
918
+ end
919
+ @options[:remote_address_proxy_protocol] = protocol_version
842
920
  else
843
921
  raise "Invalid value for set_remote_address - #{val.inspect}"
844
922
  end
@@ -910,5 +988,25 @@ module Puma
910
988
  def mutate_stdout_and_stderr_to_sync_on_write(enabled=true)
911
989
  @options[:mutate_stdout_and_stderr_to_sync_on_write] = enabled
912
990
  end
991
+
992
+ private
993
+
994
+ # To avoid adding cert_pem and key_pem as URI params, we store them on the
995
+ # options[:store] from where Puma binder knows how to find and extract them.
996
+ def add_pem_values_to_options_store(opts)
997
+ return if defined?(JRUBY_VERSION)
998
+
999
+ @options[:store] ||= []
1000
+
1001
+ # Store cert_pem and key_pem to options[:store] if present
1002
+ [:cert, :key].each do |v|
1003
+ opt_key = :"#{v}_pem"
1004
+ if opts[opt_key]
1005
+ index = @options[:store].length
1006
+ @options[:store] << opts[opt_key]
1007
+ opts[v] = "store:#{index}"
1008
+ end
1009
+ end
1010
+ end
913
1011
  end
914
1012
  end
@@ -17,7 +17,7 @@ module Puma
17
17
  # be particularly full-featured or fast. It just has to handle the few places
18
18
  # where Puma relies on JSON serialization internally.
19
19
 
20
- module JSON
20
+ module JSONSerialization
21
21
  QUOTE = /"/
22
22
  BACKSLASH = /\\/
23
23
  CONTROL_CHAR_TO_ESCAPE = /[\x00-\x1F]/ # As required by ECMA-404
data/lib/puma/launcher.rb CHANGED
@@ -15,6 +15,7 @@ module Puma
15
15
  # It is responsible for either launching a cluster of Puma workers or a single
16
16
  # puma server.
17
17
  class Launcher
18
+ # @deprecated 6.0.0
18
19
  KEYS_NOT_TO_PERSIST_IN_STATE = [
19
20
  :logger, :lowlevel_error_handler,
20
21
  :before_worker_shutdown, :before_worker_boot, :before_worker_fork,
@@ -73,7 +74,7 @@ module Puma
73
74
 
74
75
  generate_restart_data
75
76
 
76
- if clustered? && !Process.respond_to?(:fork)
77
+ if clustered? && !Puma.forkable?
77
78
  unsupported "worker mode not supported on #{RUBY_ENGINE} on this platform"
78
79
  end
79
80
 
@@ -158,6 +159,17 @@ module Puma
158
159
  true
159
160
  end
160
161
 
162
+ # Begin a refork if supported
163
+ def refork
164
+ if clustered? && @runner.respond_to?(:fork_worker!) && @options[:fork_worker]
165
+ @runner.fork_worker!
166
+ true
167
+ else
168
+ log "* refork called but not available."
169
+ false
170
+ end
171
+ end
172
+
161
173
  # Run the server. This blocks until the server is stopped
162
174
  def run
163
175
  previous_env =
@@ -319,10 +331,12 @@ module Puma
319
331
  log '* Pruning Bundler environment'
320
332
  home = ENV['GEM_HOME']
321
333
  bundle_gemfile = Bundler.original_env['BUNDLE_GEMFILE']
334
+ bundle_app_config = Bundler.original_env['BUNDLE_APP_CONFIG']
322
335
  with_unbundled_env do
323
336
  ENV['GEM_HOME'] = home
324
337
  ENV['BUNDLE_GEMFILE'] = bundle_gemfile
325
338
  ENV['PUMA_BUNDLER_PRUNED'] = '1'
339
+ ENV["BUNDLE_APP_CONFIG"] = bundle_app_config
326
340
  args = [Gem.ruby, puma_wild_location, '-I', dirs.join(':')] + @original_argv
327
341
  # Ruby 2.0+ defaults to true which breaks socket activation
328
342
  args += [{:close_others => false}]
@@ -23,17 +23,19 @@ module Puma
23
23
  ctx.keystore_pass = params['keystore-pass']
24
24
  ctx.ssl_cipher_list = params['ssl_cipher_list'] if params['ssl_cipher_list']
25
25
  else
26
- unless params['key']
27
- events.error "Please specify the SSL key via 'key='"
26
+ if params['key'].nil? && params['key_pem'].nil?
27
+ events.error "Please specify the SSL key via 'key=' or 'key_pem='"
28
28
  end
29
29
 
30
- ctx.key = params['key']
30
+ ctx.key = params['key'] if params['key']
31
+ ctx.key_pem = params['key_pem'] if params['key_pem']
31
32
 
32
- unless params['cert']
33
- events.error "Please specify the SSL cert via 'cert='"
33
+ if params['cert'].nil? && params['cert_pem'].nil?
34
+ events.error "Please specify the SSL cert via 'cert=' or 'cert_pem='"
34
35
  end
35
36
 
36
- ctx.cert = params['cert']
37
+ ctx.cert = params['cert'] if params['cert']
38
+ ctx.cert_pem = params['cert_pem'] if params['cert_pem']
37
39
 
38
40
  if ['peer', 'force_peer'].include?(params['verify_mode'])
39
41
  unless params['ca']
data/lib/puma/minissl.rb CHANGED
@@ -161,30 +161,15 @@ module Puma
161
161
  @socket.flush
162
162
  end
163
163
 
164
- def read_and_drop(timeout = 1)
165
- return :timeout unless IO.select([@socket], nil, nil, timeout)
166
- case @socket.read_nonblock(1024, exception: false)
167
- when nil
168
- :eof
169
- when :wait_readable
170
- :eagain
171
- else
172
- :drop
173
- end
174
- end
175
-
176
- def should_drop_bytes?
177
- @engine.init? || !@engine.shutdown
178
- end
179
-
180
164
  def close
181
165
  begin
182
- # Read any drop any partially initialized sockets and any received bytes during shutdown.
183
- # Don't let this socket hold this loop forever.
184
- # If it can't send more packets within 1s, then give up.
185
- return if [:timeout, :eof].include?(read_and_drop(1)) while should_drop_bytes?
166
+ unless @engine.shutdown
167
+ while alert_data = @engine.extract
168
+ @socket.write alert_data
169
+ end
170
+ end
186
171
  rescue IOError, SystemCallError
187
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
172
+ Puma::Util.purge_interrupt_queue
188
173
  # nothing
189
174
  ensure
190
175
  @socket.close
@@ -223,6 +208,15 @@ module Puma
223
208
  def initialize
224
209
  @no_tlsv1 = false
225
210
  @no_tlsv1_1 = false
211
+ @key = nil
212
+ @cert = nil
213
+ @key_pem = nil
214
+ @cert_pem = nil
215
+ end
216
+
217
+ def check_file(file, desc)
218
+ raise ArgumentError, "#{desc} file '#{file}' does not exist" unless File.exist? file
219
+ raise ArgumentError, "#{desc} file '#{file}' is not readable" unless File.readable? file
226
220
  end
227
221
 
228
222
  if IS_JRUBY
@@ -232,7 +226,7 @@ module Puma
232
226
  attr_accessor :ssl_cipher_list
233
227
 
234
228
  def keystore=(keystore)
235
- raise ArgumentError, "No such keystore file '#{keystore}'" unless File.exist? keystore
229
+ check_file keystore, 'Keystore'
236
230
  @keystore = keystore
237
231
  end
238
232
 
@@ -245,27 +239,39 @@ module Puma
245
239
  attr_reader :key
246
240
  attr_reader :cert
247
241
  attr_reader :ca
242
+ attr_reader :cert_pem
243
+ attr_reader :key_pem
248
244
  attr_accessor :ssl_cipher_filter
249
245
  attr_accessor :verification_flags
250
246
 
251
247
  def key=(key)
252
- raise ArgumentError, "No such key file '#{key}'" unless File.exist? key
248
+ check_file key, 'Key'
253
249
  @key = key
254
250
  end
255
251
 
256
252
  def cert=(cert)
257
- raise ArgumentError, "No such cert file '#{cert}'" unless File.exist? cert
253
+ check_file cert, 'Cert'
258
254
  @cert = cert
259
255
  end
260
256
 
261
257
  def ca=(ca)
262
- raise ArgumentError, "No such ca file '#{ca}'" unless File.exist? ca
258
+ check_file ca, 'ca'
263
259
  @ca = ca
264
260
  end
265
261
 
262
+ def cert_pem=(cert_pem)
263
+ raise ArgumentError, "'cert_pem' is not a String" unless cert_pem.is_a? String
264
+ @cert_pem = cert_pem
265
+ end
266
+
267
+ def key_pem=(key_pem)
268
+ raise ArgumentError, "'key_pem' is not a String" unless key_pem.is_a? String
269
+ @key_pem = key_pem
270
+ end
271
+
266
272
  def check
267
- raise "Key not configured" unless @key
268
- raise "Cert not configured" unless @cert
273
+ raise "Key not configured" if @key.nil? && @key_pem.nil?
274
+ raise "Cert not configured" if @cert.nil? && @cert_pem.nil?
269
275
  end
270
276
  end
271
277
 
data/lib/puma/null_io.rb CHANGED
@@ -52,5 +52,10 @@ module Puma
52
52
  def flush
53
53
  self
54
54
  end
55
+
56
+ # This is used as singleton class, so can't have state.
57
+ def closed?
58
+ false
59
+ end
55
60
  end
56
61
  end
data/lib/puma/plugin.rb CHANGED
@@ -64,7 +64,7 @@ module Puma
64
64
  def fire_background
65
65
  @background.each_with_index do |b, i|
66
66
  Thread.new do
67
- Puma.set_thread_name "plugin background #{i}"
67
+ Puma.set_thread_name "plgn bg #{i}"
68
68
  b.call
69
69
  end
70
70
  end
@@ -91,7 +91,7 @@ module Puma
91
91
  path = ary.first[CALLER_FILE]
92
92
 
93
93
  m = %r!puma/plugin/([^/]*)\.rb$!.match(path)
94
- return m[1]
94
+ m[1]
95
95
  end
96
96
 
97
97
  def self.create(&blk)
@@ -165,7 +165,7 @@ module Puma::Rack
165
165
  require config
166
166
  app = Object.const_get(::File.basename(config, '.rb').capitalize)
167
167
  end
168
- return app, options
168
+ [app, options]
169
169
  end
170
170
 
171
171
  def self.new_from_string(builder_script, file="(rackup)")
data/lib/puma/request.rb CHANGED
@@ -51,7 +51,7 @@ module Puma
51
51
  head = env[REQUEST_METHOD] == HEAD
52
52
 
53
53
  env[RACK_INPUT] = body
54
- env[RACK_URL_SCHEME] = default_server_port(env) == PORT_443 ? HTTPS : HTTP
54
+ env[RACK_URL_SCHEME] ||= default_server_port(env) == PORT_443 ? HTTPS : HTTP
55
55
 
56
56
  if @early_hints
57
57
  env[EARLY_HINTS] = lambda { |headers|
@@ -167,16 +167,25 @@ module Puma
167
167
  end
168
168
 
169
169
  ensure
170
- uncork_socket io
171
-
172
- body.close
173
- client.tempfile.unlink if client.tempfile
174
- res_body.close if res_body.respond_to? :close
170
+ begin
171
+ uncork_socket io
172
+
173
+ body.close
174
+ client.tempfile.unlink if client.tempfile
175
+ ensure
176
+ # Whatever happens, we MUST call `close` on the response body.
177
+ # Otherwise Rack::BodyProxy callbacks may not fire and lead to various state leaks
178
+ res_body.close if res_body.respond_to? :close
179
+ end
175
180
 
176
- after_reply.each { |o| o.call }
181
+ begin
182
+ after_reply.each { |o| o.call }
183
+ rescue StandardError => e
184
+ @log_writer.debug_error e
185
+ end
177
186
  end
178
187
 
179
- return res_info[:keep_alive]
188
+ res_info[:keep_alive]
180
189
  end
181
190
 
182
191
  # @param env [Hash] see Puma::Client#env, from request
@@ -201,7 +210,7 @@ module Puma
201
210
  begin
202
211
  n = io.syswrite str
203
212
  rescue Errno::EAGAIN, Errno::EWOULDBLOCK
204
- if !IO.select(nil, [io], nil, WRITE_TIMEOUT)
213
+ unless io.wait_writable WRITE_TIMEOUT
205
214
  raise ConnectionError, "Socket timeout writing data"
206
215
  end
207
216
 
@@ -419,7 +428,7 @@ module Puma
419
428
  # of concurrent connections exceeds the size of the threadpool.
420
429
  res_info[:keep_alive] &&= requests < @max_fast_inline ||
421
430
  @thread_pool.busy_threads < @max_threads ||
422
- !IO.select([client.listener], nil, nil, 0)
431
+ !client.listener.to_io.wait_readable(0)
423
432
 
424
433
  res_info[:response_hijack] = nil
425
434
 
data/lib/puma/runner.rb CHANGED
@@ -15,6 +15,16 @@ module Puma
15
15
  @app = nil
16
16
  @control = nil
17
17
  @started_at = Time.now
18
+ @wakeup = nil
19
+ end
20
+
21
+ def wakeup!
22
+ return unless @wakeup
23
+
24
+ @wakeup.write "!" unless @wakeup.closed?
25
+
26
+ rescue SystemCallError, IOError
27
+ Puma::Util.purge_interrupt_queue
18
28
  end
19
29
 
20
30
  def development?
@@ -59,7 +69,7 @@ module Puma
59
69
 
60
70
  control.binder.parse [str], self, 'Starting control server'
61
71
 
62
- control.run thread_name: 'control'
72
+ control.run thread_name: 'ctl'
63
73
  @control = control
64
74
  end
65
75
 
@@ -84,12 +94,13 @@ module Puma
84
94
  def output_header(mode)
85
95
  min_t = @options[:min_threads]
86
96
  max_t = @options[:max_threads]
97
+ environment = @options[:environment]
87
98
 
88
99
  log "Puma starting in #{mode} mode..."
89
100
  log "* Puma version: #{Puma::Const::PUMA_VERSION} (#{ruby_engine}) (\"#{Puma::Const::CODE_NAME}\")"
90
101
  log "* Min threads: #{min_t}"
91
102
  log "* Max threads: #{max_t}"
92
- log "* Environment: #{ENV['RACK_ENV']}"
103
+ log "* Environment: #{environment}"
93
104
 
94
105
  if mode == "cluster"
95
106
  log "* Master PID: #{Process.pid}"
@@ -108,9 +119,7 @@ module Puma
108
119
  append = @options[:redirect_append]
109
120
 
110
121
  if stdout
111
- unless Dir.exist?(File.dirname(stdout))
112
- raise "Cannot redirect STDOUT to #{stdout}"
113
- end
122
+ ensure_output_directory_exists(stdout, 'STDOUT')
114
123
 
115
124
  STDOUT.reopen stdout, (append ? "a" : "w")
116
125
  STDOUT.puts "=== puma startup: #{Time.now} ==="
@@ -118,9 +127,7 @@ module Puma
118
127
  end
119
128
 
120
129
  if stderr
121
- unless Dir.exist?(File.dirname(stderr))
122
- raise "Cannot redirect STDERR to #{stderr}"
123
- end
130
+ ensure_output_directory_exists(stderr, 'STDERR')
124
131
 
125
132
  STDERR.reopen stderr, (append ? "a" : "w")
126
133
  STDERR.puts "=== puma startup: #{Time.now} ==="
@@ -159,5 +166,12 @@ module Puma
159
166
  server.inherit_binder @launcher.binder
160
167
  server
161
168
  end
169
+
170
+ private
171
+ def ensure_output_directory_exists(path, io_name)
172
+ unless Dir.exist?(File.dirname(path))
173
+ raise "Cannot redirect #{io_name} to #{path}"
174
+ end
175
+ end
162
176
  end
163
177
  end