puma 6.4.3 → 6.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -15,14 +15,14 @@ module Puma
15
15
 
16
16
  LOG_QUEUE = Queue.new
17
17
 
18
- def initialize(ioerr)
18
+ def initialize(ioerr, env: ENV)
19
19
  @ioerr = ioerr
20
20
 
21
- @debug = ENV.key? 'PUMA_DEBUG'
21
+ @debug = env.key?('PUMA_DEBUG')
22
22
  end
23
23
 
24
- def self.stdio
25
- new $stderr
24
+ def self.stdio(env: ENV)
25
+ new($stderr, env: env)
26
26
  end
27
27
 
28
28
  # Print occurred error details.
@@ -6,22 +6,6 @@ module Puma
6
6
  module JRubyRestart
7
7
  extend FFI::Library
8
8
  ffi_lib 'c'
9
-
10
- attach_function :execlp, [:string, :varargs], :int
11
9
  attach_function :chdir, [:string], :int
12
- attach_function :fork, [], :int
13
- attach_function :exit, [:int], :void
14
- attach_function :setsid, [], :int
15
-
16
- def self.chdir_exec(dir, argv)
17
- chdir(dir)
18
- cmd = argv.first
19
- argv = ([:string] * argv.size).zip(argv)
20
- argv.flatten!
21
- argv << :string
22
- argv << nil
23
- execlp(cmd, *argv)
24
- raise SystemCallError.new(FFI.errno)
25
- end
26
10
  end
27
11
  end
data/lib/puma/launcher.rb CHANGED
@@ -46,6 +46,8 @@ module Puma
46
46
  @original_argv = @argv.dup
47
47
  @config = conf
48
48
 
49
+ env = launcher_args.delete(:env) || ENV
50
+
49
51
  @config.options[:log_writer] = @log_writer
50
52
 
51
53
  # Advertise the Configuration
@@ -62,7 +64,7 @@ module Puma
62
64
  # Load the systemd integration if we detect systemd's NOTIFY_SOCKET.
63
65
  # Skip this on JRuby though, because it is incompatible with the systemd
64
66
  # integration due to https://github.com/jruby/jruby/issues/6504
65
- if ENV["NOTIFY_SOCKET"] && !Puma.jruby?
67
+ if ENV["NOTIFY_SOCKET"] && !Puma.jruby? && !ENV["PUMA_SKIP_SYSTEMD"]
66
68
  @config.plugins.create('systemd')
67
69
  end
68
70
 
@@ -105,7 +107,7 @@ module Puma
105
107
 
106
108
  @status = :run
107
109
 
108
- log_config if ENV['PUMA_LOG_CONFIG']
110
+ log_config if env['PUMA_LOG_CONFIG']
109
111
  end
110
112
 
111
113
  attr_reader :binder, :log_writer, :events, :config, :options, :restart_dir
@@ -287,7 +289,9 @@ module Puma
287
289
  close_binder_listeners
288
290
 
289
291
  require_relative 'jruby_restart'
290
- JRubyRestart.chdir_exec(@restart_dir, restart_args)
292
+ argv = restart_args
293
+ JRubyRestart.chdir(@restart_dir)
294
+ Kernel.exec(*argv)
291
295
  elsif Puma.windows?
292
296
  close_binder_listeners
293
297
 
@@ -31,31 +31,31 @@ module Puma
31
31
  attr_accessor :formatter, :custom_logger
32
32
 
33
33
  # Create a LogWriter that prints to +stdout+ and +stderr+.
34
- def initialize(stdout, stderr)
34
+ def initialize(stdout, stderr, env: ENV)
35
35
  @formatter = DefaultFormatter.new
36
36
  @custom_logger = nil
37
37
  @stdout = stdout
38
38
  @stderr = stderr
39
39
 
40
- @debug = ENV.key?('PUMA_DEBUG')
41
- @error_logger = ErrorLogger.new(@stderr)
40
+ @debug = env.key?('PUMA_DEBUG')
41
+ @error_logger = ErrorLogger.new(@stderr, env: env)
42
42
  end
43
43
 
44
44
  DEFAULT = new(STDOUT, STDERR)
45
45
 
46
46
  # Returns an LogWriter object which writes its status to
47
47
  # two StringIO objects.
48
- def self.strings
49
- LogWriter.new(StringIO.new, StringIO.new)
48
+ def self.strings(env: ENV)
49
+ LogWriter.new(StringIO.new, StringIO.new, env: env)
50
50
  end
51
51
 
52
- def self.stdio
53
- LogWriter.new($stdout, $stderr)
52
+ def self.stdio(env: ENV)
53
+ LogWriter.new($stdout, $stderr, env: env)
54
54
  end
55
55
 
56
- def self.null
56
+ def self.null(env: ENV)
57
57
  n = NullIO.new
58
- LogWriter.new(n, n)
58
+ LogWriter.new(n, n, env: env)
59
59
  end
60
60
 
61
61
  # Write +str+ to +@stdout+
@@ -57,6 +57,7 @@ module Puma
57
57
 
58
58
  ctx.ca = params['ca'] if params['ca']
59
59
  ctx.ssl_cipher_filter = params['ssl_cipher_filter'] if params['ssl_cipher_filter']
60
+ ctx.ssl_ciphersuites = params['ssl_ciphersuites'] if params['ssl_ciphersuites'] && HAS_TLS1_3
60
61
 
61
62
  ctx.reuse = params['reuse'] if params['reuse']
62
63
  end
data/lib/puma/minissl.rb CHANGED
@@ -289,6 +289,7 @@ module Puma
289
289
  attr_reader :cert_pem
290
290
  attr_reader :key_pem
291
291
  attr_accessor :ssl_cipher_filter
292
+ attr_accessor :ssl_ciphersuites
292
293
  attr_accessor :verification_flags
293
294
 
294
295
  attr_reader :reuse, :reuse_cache_size, :reuse_timeout
data/lib/puma/null_io.rb CHANGED
@@ -16,6 +16,10 @@ module Puma
16
16
  def each
17
17
  end
18
18
 
19
+ def pos
20
+ 0
21
+ end
22
+
19
23
  # Mimics IO#read with no data.
20
24
  #
21
25
  def read(length = nil, buffer = nil)
@@ -39,6 +43,11 @@ module Puma
39
43
  def rewind
40
44
  end
41
45
 
46
+ def seek(pos, whence = 0)
47
+ raise ArgumentError, "negative length #{pos} given" if pos.negative?
48
+ 0
49
+ end
50
+
42
51
  def close
43
52
  end
44
53
 
@@ -71,5 +80,22 @@ module Puma
71
80
  def closed?
72
81
  false
73
82
  end
83
+
84
+ def set_encoding(enc)
85
+ self
86
+ end
87
+
88
+ # per rack spec
89
+ def external_encoding
90
+ Encoding::ASCII_8BIT
91
+ end
92
+
93
+ def binmode
94
+ self
95
+ end
96
+
97
+ def binmode?
98
+ true
99
+ end
74
100
  end
75
101
  end
data/lib/puma/request.rb CHANGED
@@ -53,7 +53,6 @@ module Puma
53
53
  socket = client.io # io may be a MiniSSL::Socket
54
54
  app_body = nil
55
55
 
56
-
57
56
  return false if closed_socket?(socket)
58
57
 
59
58
  if client.http_content_length_limit_exceeded
@@ -135,7 +134,7 @@ module Puma
135
134
  io_buffer.reset
136
135
  uncork_socket client.io
137
136
  app_body.close if app_body.respond_to? :close
138
- client.tempfile&.unlink
137
+ client&.tempfile_close
139
138
  after_reply = env[RACK_AFTER_REPLY] || []
140
139
  begin
141
140
  after_reply.each { |o| o.call }
@@ -165,9 +164,14 @@ module Puma
165
164
  # if the server is at capacity and the listener has a new connection ready.
166
165
  # This allows Puma to service connections fairly when the number
167
166
  # of concurrent connections exceeds the size of the threadpool.
168
- force_keep_alive = requests < @max_fast_inline ||
167
+ force_keep_alive = if @enable_keep_alives
168
+ requests < @max_fast_inline ||
169
169
  @thread_pool.busy_threads < @max_threads ||
170
170
  !client.listener.to_io.wait_readable(0)
171
+ else
172
+ # Always set force_keep_alive to false if the server has keep-alives not enabled.
173
+ false
174
+ end
171
175
 
172
176
  resp_info = str_headers(env, status, headers, res_body, io_buffer, force_keep_alive)
173
177
 
data/lib/puma/runner.rb CHANGED
@@ -27,7 +27,7 @@ module Puma
27
27
  def wakeup!
28
28
  return unless @wakeup
29
29
 
30
- @wakeup.write "!" unless @wakeup.closed?
30
+ @wakeup.write Puma::Const::PipeRequest::WAKEUP unless @wakeup.closed?
31
31
 
32
32
  rescue SystemCallError, IOError
33
33
  Puma::Util.purge_interrupt_queue
@@ -91,7 +91,10 @@ module Puma
91
91
  end
92
92
 
93
93
  # @!attribute [r] ruby_engine
94
+ # @deprecated Use `RUBY_DESCRIPTION` instead
94
95
  def ruby_engine
96
+ warn "Puma::Runner#ruby_engine is deprecated; use RUBY_DESCRIPTION instead. It will be removed in puma v7."
97
+
95
98
  if !defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby"
96
99
  "ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}"
97
100
  else
@@ -109,7 +112,8 @@ module Puma
109
112
  environment = @options[:environment]
110
113
 
111
114
  log "Puma starting in #{mode} mode..."
112
- log "* Puma version: #{Puma::Const::PUMA_VERSION} (#{ruby_engine}) (\"#{Puma::Const::CODE_NAME}\")"
115
+ log "* Puma version: #{Puma::Const::PUMA_VERSION} (\"#{Puma::Const::CODE_NAME}\")"
116
+ log "* Ruby version: #{RUBY_DESCRIPTION}"
113
117
  log "* Min threads: #{min_t}"
114
118
  log "* Max threads: #{max_t}"
115
119
  log "* Environment: #{environment}"
data/lib/puma/server.rb CHANGED
@@ -18,6 +18,9 @@ require 'io/wait' unless Puma::HAS_NATIVE_IO_WAIT
18
18
 
19
19
  module Puma
20
20
 
21
+ # This method was private on Ruby 2.4 but became public on Ruby 2.5+:
22
+ Thread.send(:attr_accessor, :puma_server)
23
+
21
24
  # The HTTP Server itself. Serves out a single Rack app.
22
25
  #
23
26
  # This class is used by the `Puma::Single` and `Puma::Cluster` classes
@@ -32,6 +35,7 @@ module Puma
32
35
  include Puma::Const
33
36
  include Request
34
37
 
38
+ attr_reader :options
35
39
  attr_reader :thread
36
40
  attr_reader :log_writer
37
41
  attr_reader :events
@@ -46,7 +50,6 @@ module Puma
46
50
  attr_accessor :app
47
51
  attr_accessor :binder
48
52
 
49
- THREAD_LOCAL_KEY = :puma_server
50
53
 
51
54
  # Create a server for the rack app +app+.
52
55
  #
@@ -92,6 +95,7 @@ module Puma
92
95
  @max_threads = @options[:max_threads]
93
96
  @queue_requests = @options[:queue_requests]
94
97
  @max_fast_inline = @options[:max_fast_inline]
98
+ @enable_keep_alives = @options[:enable_keep_alives]
95
99
  @io_selector_backend = @options[:io_selector_backend]
96
100
  @http_content_length_limit = @options[:http_content_length_limit]
97
101
 
@@ -130,7 +134,7 @@ module Puma
130
134
  class << self
131
135
  # @!attribute [r] current
132
136
  def current
133
- Thread.current[THREAD_LOCAL_KEY]
137
+ Thread.current.puma_server
134
138
  end
135
139
 
136
140
  # :nodoc:
@@ -242,7 +246,7 @@ module Puma
242
246
 
243
247
  @status = :run
244
248
 
245
- @thread_pool = ThreadPool.new(thread_name, @options) { |client| process_client client }
249
+ @thread_pool = ThreadPool.new(thread_name, options) { |client| process_client client }
246
250
 
247
251
  if @queue_requests
248
252
  @reactor = Reactor.new(@io_selector_backend) { |c| reactor_wakeup c }
@@ -250,8 +254,8 @@ module Puma
250
254
  end
251
255
 
252
256
 
253
- @thread_pool.auto_reap! if @options[:reaping_time]
254
- @thread_pool.auto_trim! if @options[:auto_trim_time]
257
+ @thread_pool.auto_reap! if options[:reaping_time]
258
+ @thread_pool.auto_trim! if options[:auto_trim_time]
255
259
 
256
260
  @check, @notify = Puma::Util.pipe unless @notify
257
261
 
@@ -315,15 +319,15 @@ module Puma
315
319
  sockets = [check] + @binder.ios
316
320
  pool = @thread_pool
317
321
  queue_requests = @queue_requests
318
- drain = @options[:drain_on_shutdown] ? 0 : nil
322
+ drain = options[:drain_on_shutdown] ? 0 : nil
319
323
 
320
- addr_send_name, addr_value = case @options[:remote_address]
324
+ addr_send_name, addr_value = case options[:remote_address]
321
325
  when :value
322
- [:peerip=, @options[:remote_address_value]]
326
+ [:peerip=, options[:remote_address_value]]
323
327
  when :header
324
- [:remote_addr_header=, @options[:remote_address_header]]
328
+ [:remote_addr_header=, options[:remote_address_header]]
325
329
  when :proxy_protocol
326
- [:expect_proxy_proto=, @options[:remote_address_proxy_protocol]]
330
+ [:expect_proxy_proto=, options[:remote_address_proxy_protocol]]
327
331
  else
328
332
  [nil, nil]
329
333
  end
@@ -336,7 +340,7 @@ module Puma
336
340
  @idle_timeout_reached = true
337
341
 
338
342
  if @clustered
339
- @worker_write << "i#{Process.pid}\n" rescue nil
343
+ @worker_write << "#{PipeRequest::IDLE}#{Process.pid}\n" rescue nil
340
344
  next
341
345
  else
342
346
  @log_writer.log "- Idle timeout reached"
@@ -349,7 +353,7 @@ module Puma
349
353
 
350
354
  if @idle_timeout_reached && @clustered
351
355
  @idle_timeout_reached = false
352
- @worker_write << "i#{Process.pid}\n" rescue nil
356
+ @worker_write << "#{PipeRequest::IDLE}#{Process.pid}\n" rescue nil
353
357
  end
354
358
 
355
359
  ios.first.each do |sock|
@@ -357,7 +361,7 @@ module Puma
357
361
  break if handle_check
358
362
  else
359
363
  pool.wait_until_not_full
360
- pool.wait_for_less_busy_worker(@options[:wait_for_less_busy_worker])
364
+ pool.wait_for_less_busy_worker(options[:wait_for_less_busy_worker]) if @clustered
361
365
 
362
366
  io = begin
363
367
  sock.accept_nonblock
@@ -437,9 +441,9 @@ module Puma
437
441
  # Return true if one or more requests were processed.
438
442
  def process_client(client)
439
443
  # Advertise this server into the thread
440
- Thread.current[THREAD_LOCAL_KEY] = self
444
+ Thread.current.puma_server = self
441
445
 
442
- clean_thread_locals = @options[:clean_thread_locals]
446
+ clean_thread_locals = options[:clean_thread_locals]
443
447
  close_socket = true
444
448
 
445
449
  requests = 0
@@ -548,7 +552,7 @@ module Puma
548
552
  # A fallback rack response if +@app+ raises as exception.
549
553
  #
550
554
  def lowlevel_error(e, env, status=500)
551
- if handler = @options[:lowlevel_error_handler]
555
+ if handler = options[:lowlevel_error_handler]
552
556
  if handler.arity == 1
553
557
  return handler.call(e)
554
558
  elsif handler.arity == 2
@@ -569,14 +573,13 @@ module Puma
569
573
  def response_to_error(client, requests, err, status_code)
570
574
  status, headers, res_body = lowlevel_error(err, client.env, status_code)
571
575
  prepare_response(status, headers, res_body, requests, client)
572
- client.write_error(status_code)
573
576
  end
574
577
  private :response_to_error
575
578
 
576
579
  # Wait for all outstanding requests to finish.
577
580
  #
578
581
  def graceful_shutdown
579
- if @options[:shutdown_debug]
582
+ if options[:shutdown_debug]
580
583
  threads = Thread.list
581
584
  total = threads.size
582
585
 
@@ -596,7 +599,7 @@ module Puma
596
599
  end
597
600
 
598
601
  if @thread_pool
599
- if timeout = @options[:force_shutdown_after]
602
+ if timeout = options[:force_shutdown_after]
600
603
  @thread_pool.shutdown timeout.to_f
601
604
  else
602
605
  @thread_pool.shutdown
@@ -648,8 +651,12 @@ module Puma
648
651
  # Returns a hash of stats about the running server for reporting purposes.
649
652
  # @version 5.0.0
650
653
  # @!attribute [r] stats
654
+ # @return [Hash] hash containing stat info from `Server` and `ThreadPool`
651
655
  def stats
652
- STAT_METHODS.map {|name| [name, send(name) || 0]}.to_h
656
+ stats = @thread_pool&.stats || {}
657
+ stats[:max_threads] = @max_threads
658
+ stats[:requests_count] = @requests_count
659
+ stats
653
660
  end
654
661
 
655
662
  # below are 'delegations' to binder
@@ -85,6 +85,17 @@ module Puma
85
85
  end
86
86
  end
87
87
 
88
+ # generate stats hash so as not to perform multiple locks
89
+ # @return [Hash] hash containing stat info from ThreadPool
90
+ def stats
91
+ with_mutex do
92
+ { backlog: @todo.size,
93
+ running: @spawned,
94
+ pool_capacity: @waiting + (@max - @spawned)
95
+ }
96
+ end
97
+ end
98
+
88
99
  # How many objects have yet to be processed by the pool?
89
100
  #
90
101
  def backlog
data/lib/puma/util.rb CHANGED
@@ -11,7 +11,7 @@ module Puma
11
11
  end
12
12
 
13
13
  # An instance method on Thread has been provided to address https://bugs.ruby-lang.org/issues/13632,
14
- # which currently effects some older versions of Ruby: 2.2.7 2.2.8 2.2.9 2.2.10 2.3.4 2.4.1
14
+ # which currently affects some older versions of Ruby: 2.2.7 2.2.8 2.2.9 2.2.10 2.3.4 2.4.1
15
15
  # Additional context: https://github.com/puma/puma/pull/1345
16
16
  def purge_interrupt_queue
17
17
  Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
@@ -1,7 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # This module is used as an 'include' file in code at bottom of file
4
3
  module Puma
4
+
5
+ # This module is used as an 'include' file in code at bottom of file. It loads
6
+ # into either `Rackup::Handler::Puma` or `Rack::Handler::Puma`.
7
+
5
8
  module RackHandler
6
9
  DEFAULT_OPTIONS = {
7
10
  :Verbose => false,
@@ -93,9 +96,9 @@ module Puma
93
96
  def set_host_port_to_config(host, port, config)
94
97
  config.clear_binds! if host || port
95
98
 
96
- if host && (host[0,1] == '.' || host[0,1] == '/')
99
+ if host&.start_with? '.', '/', '@'
97
100
  config.bind "unix://#{host}"
98
- elsif host && host =~ /^ssl:\/\//
101
+ elsif host&.start_with? 'ssl://'
99
102
  uri = URI.parse(host)
100
103
  uri.port ||= port || ::Puma::Configuration::DEFAULTS[:tcp_port]
101
104
  config.bind uri.to_s
@@ -115,7 +118,7 @@ module Puma
115
118
  end
116
119
 
117
120
  # rackup was removed in Rack 3, it is now a separate gem
118
- if Object.const_defined? :Rackup
121
+ if Object.const_defined?(:Rackup) && ::Rackup.const_defined?(:Handler)
119
122
  module Rackup
120
123
  module Handler
121
124
  module Puma
@@ -127,7 +130,7 @@ if Object.const_defined? :Rackup
127
130
  end
128
131
  end
129
132
  else
130
- do_register = Object.const_defined?(:Rack) && Rack.release < '3'
133
+ do_register = Object.const_defined?(:Rack) && ::Rack.release < '3'
131
134
  module Rack
132
135
  module Handler
133
136
  module Puma
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.4.3
4
+ version: 6.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Phoenix
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-19 00:00:00.000000000 Z
11
+ date: 2024-11-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nio4r
@@ -24,10 +24,11 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.0'
27
- description: Puma is a simple, fast, threaded, and highly parallel HTTP 1.1 server
28
- for Ruby/Rack applications. Puma is intended for use in both development and production
29
- environments. It's great for highly parallel Ruby implementations such as Rubinius
30
- and JRuby as well as as providing process worker support to support CRuby well.
27
+ description: |
28
+ Puma is a simple, fast, multi-threaded, and highly parallel HTTP 1.1 server
29
+ for Ruby/Rack applications. Puma is intended for use in both development and
30
+ production environments. It's great for highly parallel Ruby implementations such as
31
+ JRuby and TruffleRuby as well as as providing process worker support to support CRuby well.
31
32
  email:
32
33
  - evan@phx.io
33
34
  executables:
@@ -50,6 +51,7 @@ files:
50
51
  - docs/images/puma-connection-flow-no-reactor.png
51
52
  - docs/images/puma-connection-flow.png
52
53
  - docs/images/puma-general-arch.png
54
+ - docs/java_options.md
53
55
  - docs/jungle/README.md
54
56
  - docs/jungle/rc.d/README.md
55
57
  - docs/jungle/rc.d/puma
@@ -145,9 +147,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
145
147
  - !ruby/object:Gem::Version
146
148
  version: '0'
147
149
  requirements: []
148
- rubygems_version: 3.5.16
150
+ rubygems_version: 3.5.22
149
151
  signing_key:
150
152
  specification_version: 4
151
- summary: Puma is a simple, fast, threaded, and highly parallel HTTP 1.1 server for
152
- Ruby/Rack applications
153
+ summary: A Ruby/Rack web server built for parallelism.
153
154
  test_files: []