puma 5.3.2 → 5.6.4

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +148 -8
  3. data/LICENSE +0 -0
  4. data/README.md +47 -6
  5. data/bin/puma-wild +0 -0
  6. data/docs/architecture.md +49 -16
  7. data/docs/compile_options.md +4 -2
  8. data/docs/deployment.md +53 -67
  9. data/docs/fork_worker.md +0 -0
  10. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  11. data/docs/images/puma-connection-flow.png +0 -0
  12. data/docs/images/puma-general-arch.png +0 -0
  13. data/docs/jungle/README.md +0 -0
  14. data/docs/jungle/rc.d/README.md +0 -0
  15. data/docs/jungle/rc.d/puma.conf +0 -0
  16. data/docs/kubernetes.md +0 -0
  17. data/docs/nginx.md +0 -0
  18. data/docs/plugins.md +15 -15
  19. data/docs/rails_dev_mode.md +2 -3
  20. data/docs/restart.md +6 -6
  21. data/docs/signals.md +11 -10
  22. data/docs/stats.md +8 -8
  23. data/docs/systemd.md +64 -67
  24. data/ext/puma_http11/PumaHttp11Service.java +0 -0
  25. data/ext/puma_http11/ext_help.h +0 -0
  26. data/ext/puma_http11/extconf.rb +28 -5
  27. data/ext/puma_http11/http11_parser.c +23 -10
  28. data/ext/puma_http11/http11_parser.h +0 -0
  29. data/ext/puma_http11/http11_parser.java.rl +0 -0
  30. data/ext/puma_http11/http11_parser.rl +0 -0
  31. data/ext/puma_http11/http11_parser_common.rl +1 -1
  32. data/ext/puma_http11/mini_ssl.c +69 -9
  33. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +0 -0
  34. data/ext/puma_http11/org/jruby/puma/Http11.java +0 -0
  35. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +49 -47
  36. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +28 -43
  37. data/ext/puma_http11/puma_http11.c +1 -1
  38. data/lib/puma/app/status.rb +4 -4
  39. data/lib/puma/binder.rb +50 -5
  40. data/lib/puma/cli.rb +14 -4
  41. data/lib/puma/client.rb +104 -20
  42. data/lib/puma/cluster/worker.rb +8 -18
  43. data/lib/puma/cluster/worker_handle.rb +4 -0
  44. data/lib/puma/cluster.rb +30 -24
  45. data/lib/puma/commonlogger.rb +0 -0
  46. data/lib/puma/configuration.rb +4 -1
  47. data/lib/puma/const.rb +9 -8
  48. data/lib/puma/control_cli.rb +1 -1
  49. data/lib/puma/detect.rb +8 -2
  50. data/lib/puma/dsl.rb +105 -11
  51. data/lib/puma/error_logger.rb +0 -0
  52. data/lib/puma/events.rb +0 -0
  53. data/lib/puma/io_buffer.rb +0 -0
  54. data/lib/puma/jruby_restart.rb +0 -0
  55. data/lib/puma/{json.rb → json_serialization.rb} +1 -1
  56. data/lib/puma/launcher.rb +4 -1
  57. data/lib/puma/minissl/context_builder.rb +8 -6
  58. data/lib/puma/minissl.rb +24 -23
  59. data/lib/puma/null_io.rb +0 -0
  60. data/lib/puma/plugin/tmp_restart.rb +0 -0
  61. data/lib/puma/plugin.rb +2 -2
  62. data/lib/puma/queue_close.rb +0 -0
  63. data/lib/puma/rack/builder.rb +1 -1
  64. data/lib/puma/rack/urlmap.rb +0 -0
  65. data/lib/puma/rack_default.rb +0 -0
  66. data/lib/puma/reactor.rb +0 -0
  67. data/lib/puma/request.rb +14 -9
  68. data/lib/puma/runner.rb +22 -8
  69. data/lib/puma/server.rb +35 -29
  70. data/lib/puma/single.rb +0 -0
  71. data/lib/puma/state_file.rb +41 -7
  72. data/lib/puma/systemd.rb +0 -0
  73. data/lib/puma/thread_pool.rb +7 -5
  74. data/lib/puma/util.rb +8 -1
  75. data/lib/puma.rb +2 -2
  76. data/lib/rack/handler/puma.rb +0 -0
  77. data/tools/Dockerfile +1 -1
  78. data/tools/trickletest.rb +0 -0
  79. metadata +7 -7
data/lib/puma/dsl.rb CHANGED
@@ -48,6 +48,8 @@ module Puma
48
48
 
49
49
  ca_additions = "&ca=#{opts[:ca]}" if ['peer', 'force_peer'].include?(verify)
50
50
 
51
+ backlog_str = opts[:backlog] ? "&backlog=#{Integer(opts[:backlog])}" : ''
52
+
51
53
  if defined?(JRUBY_VERSION)
52
54
  ssl_cipher_list = opts[:ssl_cipher_list] ?
53
55
  "&ssl_cipher_list=#{opts[:ssl_cipher_list]}" : nil
@@ -55,7 +57,7 @@ module Puma
55
57
  keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
56
58
 
57
59
  "ssl://#{host}:#{port}?#{keystore_additions}#{ssl_cipher_list}" \
58
- "&verify_mode=#{verify}#{tls_str}#{ca_additions}"
60
+ "&verify_mode=#{verify}#{tls_str}#{ca_additions}#{backlog_str}"
59
61
  else
60
62
  ssl_cipher_filter = opts[:ssl_cipher_filter] ?
61
63
  "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" : nil
@@ -64,7 +66,7 @@ module Puma
64
66
  "&verification_flags=#{Array(ary).join ','}" : nil
65
67
 
66
68
  "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}" \
67
- "#{ssl_cipher_filter}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}"
69
+ "#{ssl_cipher_filter}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}#{backlog_str}"
68
70
  end
69
71
  end
70
72
 
@@ -191,7 +193,7 @@ module Puma
191
193
  end
192
194
 
193
195
  # Bind the server to +url+. "tcp://", "unix://" and "ssl://" are the only
194
- # accepted protocols. Multiple urls can be bound to, calling `bind` does
196
+ # accepted protocols. Multiple urls can be bound to, calling +bind+ does
195
197
  # not overwrite previous bindings.
196
198
  #
197
199
  # The default is "tcp://0.0.0.0:9292".
@@ -381,6 +383,13 @@ module Puma
381
383
  @options[:rackup] ||= path.to_s
382
384
  end
383
385
 
386
+ # Allows setting `env['rack.url_scheme']`.
387
+ # Only necessary if X-Forwarded-Proto is not being set by your proxy
388
+ # Normal values are 'http' or 'https'.
389
+ def rack_url_scheme(scheme=nil)
390
+ @options[:rack_url_scheme] = scheme
391
+ end
392
+
384
393
  def early_hints(answer=true)
385
394
  @options[:early_hints] = answer
386
395
  end
@@ -429,8 +438,15 @@ module Puma
429
438
  @options[:max_threads] = max
430
439
  end
431
440
 
432
- # Instead of `bind 'ssl://127.0.0.1:9292?key=key_path&cert=cert_path'` you
433
- # can also use the this method.
441
+ # Instead of using +bind+ and manually constructing a URI like:
442
+ #
443
+ # bind 'ssl://127.0.0.1:9292?key=key_path&cert=cert_path'
444
+ #
445
+ # you can use the this method.
446
+ #
447
+ # When binding on localhost you don't need to specify +cert+ and +key+,
448
+ # Puma will assume you are using the +localhost+ gem and try to load the
449
+ # appropriate files.
434
450
  #
435
451
  # @example
436
452
  # ssl_bind '127.0.0.1', '9292', {
@@ -440,14 +456,25 @@ module Puma
440
456
  # verify_mode: verify_mode, # default 'none'
441
457
  # verification_flags: flags, # optional, not supported by JRuby
442
458
  # }
443
- # @example For JRuby, two keys are required: keystore & keystore_pass.
459
+ #
460
+ # @example Using self-signed certificate with the +localhost+ gem:
461
+ # ssl_bind '127.0.0.1', '9292'
462
+ #
463
+ # @example Alternatively, you can provide +cert_pem+ and +key_pem+:
464
+ # ssl_bind '127.0.0.1', '9292', {
465
+ # cert_pem: File.read(path_to_cert),
466
+ # key_pem: File.read(path_to_key),
467
+ # }
468
+ #
469
+ # @example For JRuby, two keys are required: +keystore+ & +keystore_pass+
444
470
  # ssl_bind '127.0.0.1', '9292', {
445
471
  # keystore: path_to_keystore,
446
472
  # keystore_pass: password,
447
473
  # ssl_cipher_list: cipher_list, # optional
448
474
  # verify_mode: verify_mode # default 'none'
449
475
  # }
450
- def ssl_bind(host, port, opts)
476
+ def ssl_bind(host, port, opts = {})
477
+ add_pem_values_to_options_store(opts)
451
478
  bind self.class.ssl_bind_str(host, port, opts)
452
479
  end
453
480
 
@@ -578,7 +605,7 @@ module Puma
578
605
  # end
579
606
  def after_worker_fork(&block)
580
607
  @options[:after_worker_fork] ||= []
581
- @options[:after_worker_fork] = block
608
+ @options[:after_worker_fork] << block
582
609
  end
583
610
 
584
611
  alias_method :after_worker_boot, :after_worker_fork
@@ -720,6 +747,19 @@ module Puma
720
747
  @options[:tag] = string.to_s
721
748
  end
722
749
 
750
+ # Change the default interval for checking workers.
751
+ #
752
+ # The default value is 5 seconds.
753
+ #
754
+ # @note Cluster mode only.
755
+ # @example
756
+ # worker_check_interval 5
757
+ # @see Puma::Cluster#check_workers
758
+ #
759
+ def worker_check_interval(interval)
760
+ @options[:worker_check_interval] = Integer(interval)
761
+ end
762
+
723
763
  # Verifies that all workers have checked in to the master process within
724
764
  # the given timeout. If not the worker process will be restarted. This is
725
765
  # not a request timeout, it is to protect against a hung or dead process.
@@ -734,7 +774,7 @@ module Puma
734
774
  #
735
775
  def worker_timeout(timeout)
736
776
  timeout = Integer(timeout)
737
- min = Const::WORKER_CHECK_INTERVAL
777
+ min = @options.fetch(:worker_check_interval, Puma::ConfigDefault::DefaultWorkerCheckInterval)
738
778
 
739
779
  if timeout <= min
740
780
  raise "The minimum worker_timeout must be greater than the worker reporting interval (#{min})"
@@ -766,6 +806,30 @@ module Puma
766
806
  @options[:worker_shutdown_timeout] = Integer(timeout)
767
807
  end
768
808
 
809
+ # Set the strategy for worker culling.
810
+ #
811
+ # There are two possible values:
812
+ #
813
+ # 1. **:youngest** - the youngest workers (i.e. the workers that were
814
+ # the most recently started) will be culled.
815
+ # 2. **:oldest** - the oldest workers (i.e. the workers that were started
816
+ # the longest time ago) will be culled.
817
+ #
818
+ # @note Cluster mode only.
819
+ # @example
820
+ # worker_culling_strategy :oldest
821
+ # @see Puma::Cluster#cull_workers
822
+ #
823
+ def worker_culling_strategy(strategy)
824
+ stategy = strategy.to_sym
825
+
826
+ if ![:youngest, :oldest].include?(strategy)
827
+ raise "Invalid value for worker_culling_strategy - #{stategy}"
828
+ end
829
+
830
+ @options[:worker_culling_strategy] = strategy
831
+ end
832
+
769
833
  # When set to true (the default), workers accept all requests
770
834
  # and queue them before passing them to the handlers.
771
835
  # When set to false, each worker process accepts exactly as
@@ -811,7 +875,7 @@ module Puma
811
875
  # a kernel syscall is required which for very fast rack handlers
812
876
  # slows down the handling significantly.
813
877
  #
814
- # There are 4 possible values:
878
+ # There are 5 possible values:
815
879
  #
816
880
  # 1. **:socket** (the default) - read the peername from the socket using the
817
881
  # syscall. This is the normal behavior.
@@ -821,7 +885,10 @@ module Puma
821
885
  # `set_remote_address header: "X-Real-IP"`.
822
886
  # Only the first word (as separated by spaces or comma) is used, allowing
823
887
  # 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
888
+ # 4. **proxy_protocol: :v1**- set the remote address to the value read from the
889
+ # HAproxy PROXY protocol, version 1. If the request does not have the PROXY
890
+ # protocol attached to it, will fall back to :socket
891
+ # 5. **\<Any string\>** - this allows you to hardcode remote address to any value
825
892
  # you wish. Because Puma never uses this field anyway, it's format is
826
893
  # entirely in your hands.
827
894
  #
@@ -839,6 +906,13 @@ module Puma
839
906
  if hdr = val[:header]
840
907
  @options[:remote_address] = :header
841
908
  @options[:remote_address_header] = "HTTP_" + hdr.upcase.tr("-", "_")
909
+ elsif protocol_version = val[:proxy_protocol]
910
+ @options[:remote_address] = :proxy_protocol
911
+ protocol_version = protocol_version.downcase.to_sym
912
+ unless [:v1].include?(protocol_version)
913
+ raise "Invalid value for proxy_protocol - #{protocol_version.inspect}"
914
+ end
915
+ @options[:remote_address_proxy_protocol] = protocol_version
842
916
  else
843
917
  raise "Invalid value for set_remote_address - #{val.inspect}"
844
918
  end
@@ -910,5 +984,25 @@ module Puma
910
984
  def mutate_stdout_and_stderr_to_sync_on_write(enabled=true)
911
985
  @options[:mutate_stdout_and_stderr_to_sync_on_write] = enabled
912
986
  end
987
+
988
+ private
989
+
990
+ # To avoid adding cert_pem and key_pem as URI params, we store them on the
991
+ # options[:store] from where Puma binder knows how to find and extract them.
992
+ def add_pem_values_to_options_store(opts)
993
+ return if defined?(JRUBY_VERSION)
994
+
995
+ @options[:store] ||= []
996
+
997
+ # Store cert_pem and key_pem to options[:store] if present
998
+ [:cert, :key].each do |v|
999
+ opt_key = :"#{v}_pem"
1000
+ if opts[opt_key]
1001
+ index = @options[:store].length
1002
+ @options[:store] << opts[opt_key]
1003
+ opts[v] = "store:#{index}"
1004
+ end
1005
+ end
1006
+ end
913
1007
  end
914
1008
  end
File without changes
data/lib/puma/events.rb CHANGED
File without changes
File without changes
File without changes
@@ -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
 
@@ -319,10 +320,12 @@ module Puma
319
320
  log '* Pruning Bundler environment'
320
321
  home = ENV['GEM_HOME']
321
322
  bundle_gemfile = Bundler.original_env['BUNDLE_GEMFILE']
323
+ bundle_app_config = Bundler.original_env['BUNDLE_APP_CONFIG']
322
324
  with_unbundled_env do
323
325
  ENV['GEM_HOME'] = home
324
326
  ENV['BUNDLE_GEMFILE'] = bundle_gemfile
325
327
  ENV['PUMA_BUNDLER_PRUNED'] = '1'
328
+ ENV["BUNDLE_APP_CONFIG"] = bundle_app_config
326
329
  args = [Gem.ruby, puma_wild_location, '-I', dirs.join(':')] + @original_argv
327
330
  # Ruby 2.0+ defaults to true which breaks socket activation
328
331
  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,10 @@ 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
226
215
  end
227
216
 
228
217
  if IS_JRUBY
@@ -245,6 +234,8 @@ module Puma
245
234
  attr_reader :key
246
235
  attr_reader :cert
247
236
  attr_reader :ca
237
+ attr_reader :cert_pem
238
+ attr_reader :key_pem
248
239
  attr_accessor :ssl_cipher_filter
249
240
  attr_accessor :verification_flags
250
241
 
@@ -263,9 +254,19 @@ module Puma
263
254
  @ca = ca
264
255
  end
265
256
 
257
+ def cert_pem=(cert_pem)
258
+ raise ArgumentError, "'cert_pem' is not a String" unless cert_pem.is_a? String
259
+ @cert_pem = cert_pem
260
+ end
261
+
262
+ def key_pem=(key_pem)
263
+ raise ArgumentError, "'key_pem' is not a String" unless key_pem.is_a? String
264
+ @key_pem = key_pem
265
+ end
266
+
266
267
  def check
267
- raise "Key not configured" unless @key
268
- raise "Cert not configured" unless @cert
268
+ raise "Key not configured" if @key.nil? && @key_pem.nil?
269
+ raise "Cert not configured" if @cert.nil? && @cert_pem.nil?
269
270
  end
270
271
  end
271
272
 
data/lib/puma/null_io.rb CHANGED
File without changes
File without changes
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)
File without changes
@@ -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)")
File without changes
File without changes
data/lib/puma/reactor.rb CHANGED
File without changes
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,21 @@ 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
181
  after_reply.each { |o| o.call }
177
182
  end
178
183
 
179
- return res_info[:keep_alive]
184
+ res_info[:keep_alive]
180
185
  end
181
186
 
182
187
  # @param env [Hash] see Puma::Client#env, from request
@@ -201,7 +206,7 @@ module Puma
201
206
  begin
202
207
  n = io.syswrite str
203
208
  rescue Errno::EAGAIN, Errno::EWOULDBLOCK
204
- if !IO.select(nil, [io], nil, WRITE_TIMEOUT)
209
+ unless io.wait_writable WRITE_TIMEOUT
205
210
  raise ConnectionError, "Socket timeout writing data"
206
211
  end
207
212
 
@@ -419,7 +424,7 @@ module Puma
419
424
  # of concurrent connections exceeds the size of the threadpool.
420
425
  res_info[:keep_alive] &&= requests < @max_fast_inline ||
421
426
  @thread_pool.busy_threads < @max_threads ||
422
- !IO.select([client.listener], nil, nil, 0)
427
+ !client.listener.to_io.wait_readable(0)
423
428
 
424
429
  res_info[:response_hijack] = nil
425
430
 
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