puma 7.0.0.pre1-java → 7.0.2-java

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.
data/lib/puma/request.rb CHANGED
@@ -52,6 +52,7 @@ module Puma
52
52
  io_buffer = client.io_buffer
53
53
  socket = client.io # io may be a MiniSSL::Socket
54
54
  app_body = nil
55
+ error = nil
55
56
 
56
57
  return false if closed_socket?(socket)
57
58
 
@@ -68,7 +69,7 @@ module Puma
68
69
  end
69
70
 
70
71
  env[HIJACK_P] = true
71
- env[HIJACK] = client
72
+ env[HIJACK] = client.method :full_hijack
72
73
 
73
74
  env[RACK_INPUT] = client.body
74
75
  env[RACK_URL_SCHEME] ||= default_server_port(env) == PORT_443 ? HTTPS : HTTP
@@ -92,6 +93,7 @@ module Puma
92
93
  # array, we will invoke them when the request is done.
93
94
  #
94
95
  env[RACK_AFTER_REPLY] ||= []
96
+ env[RACK_RESPONSE_FINISHED] ||= []
95
97
 
96
98
  begin
97
99
  if @supported_http_methods == :any || @supported_http_methods.key?(env[REQUEST_METHOD])
@@ -119,15 +121,15 @@ module Puma
119
121
 
120
122
  return :async
121
123
  end
122
- rescue ThreadPool::ForceShutdown => e
123
- @log_writer.unknown_error e, client, "Rack app"
124
+ rescue ThreadPool::ForceShutdown => error
125
+ @log_writer.unknown_error error, client, "Rack app"
124
126
  @log_writer.log "Detected force shutdown of a thread"
125
127
 
126
- status, headers, res_body = lowlevel_error(e, env, 503)
127
- rescue Exception => e
128
- @log_writer.unknown_error e, client, "Rack app"
128
+ status, headers, res_body = lowlevel_error(error, env, 503)
129
+ rescue Exception => error
130
+ @log_writer.unknown_error error, client, "Rack app"
129
131
 
130
- status, headers, res_body = lowlevel_error(e, env, 500)
132
+ status, headers, res_body = lowlevel_error(error, env, 500)
131
133
  end
132
134
  prepare_response(status, headers, res_body, requests, client)
133
135
  ensure
@@ -144,6 +146,16 @@ module Puma
144
146
  end
145
147
  end
146
148
  end
149
+
150
+ if response_finished = env[RACK_RESPONSE_FINISHED]
151
+ response_finished.reverse_each do |o|
152
+ begin
153
+ o.call(env, status, headers, error)
154
+ rescue StandardError => e
155
+ @log_writer.debug_error e
156
+ end
157
+ end
158
+ end
147
159
  end
148
160
 
149
161
  # Assembles the headers and prepares the body for actually sending the
@@ -261,11 +273,17 @@ module Puma
261
273
  !shutting_down? && keep_alive
262
274
  end
263
275
 
276
+ HTTP_ON_VALUES = { "on" => true, HTTPS => true }
277
+ private_constant :HTTP_ON_VALUES
278
+
264
279
  # @param env [Hash] see Puma::Client#env, from request
265
280
  # @return [Puma::Const::PORT_443,Puma::Const::PORT_80]
266
281
  #
267
282
  def default_server_port(env)
268
- if ['on', HTTPS].include?(env[HTTPS_KEY]) || env[HTTP_X_FORWARDED_PROTO].to_s[0...5] == HTTPS || env[HTTP_X_FORWARDED_SCHEME] == HTTPS || env[HTTP_X_FORWARDED_SSL] == "on"
283
+ if HTTP_ON_VALUES[env[HTTPS_KEY]] ||
284
+ env[HTTP_X_FORWARDED_PROTO]&.start_with?(HTTPS) ||
285
+ env[HTTP_X_FORWARDED_SCHEME] == HTTPS ||
286
+ env[HTTP_X_FORWARDED_SSL] == "on"
269
287
  PORT_443
270
288
  else
271
289
  PORT_80
@@ -469,7 +487,7 @@ module Puma
469
487
 
470
488
  # The legacy HTTP_VERSION header can be sent as a client header.
471
489
  # Rack v4 may remove using HTTP_VERSION. If so, remove this line.
472
- env[HTTP_VERSION] = env[SERVER_PROTOCOL]
490
+ env[HTTP_VERSION] = env[SERVER_PROTOCOL] if @env_set_http_version
473
491
  end
474
492
  private :normalize_env
475
493
 
@@ -659,10 +677,10 @@ module Puma
659
677
  if ary
660
678
  ary.each do |v|
661
679
  next if illegal_header_value?(v)
662
- io_buffer.append k, colon, v, line_ending
680
+ io_buffer.append k.downcase, colon, v, line_ending
663
681
  end
664
682
  else
665
- io_buffer.append k, colon, line_ending
683
+ io_buffer.append k.downcase, colon, line_ending
666
684
  end
667
685
  end
668
686
 
data/lib/puma/runner.rb CHANGED
@@ -33,7 +33,6 @@ module Puma
33
33
  @wakeup.write PIPE_WAKEUP unless @wakeup.closed?
34
34
 
35
35
  rescue SystemCallError, IOError
36
- Puma::Util.purge_interrupt_queue
37
36
  end
38
37
 
39
38
  def development?
@@ -93,22 +92,6 @@ module Puma
93
92
  @control.binder.close_listeners if @control
94
93
  end
95
94
 
96
- # @!attribute [r] ruby_engine
97
- # @deprecated Use `RUBY_DESCRIPTION` instead
98
- def ruby_engine
99
- warn "Puma::Runner#ruby_engine is deprecated; use RUBY_DESCRIPTION instead. It will be removed in puma v7."
100
-
101
- if !defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby"
102
- "ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}"
103
- else
104
- if defined?(RUBY_ENGINE_VERSION)
105
- "#{RUBY_ENGINE} #{RUBY_ENGINE_VERSION} - ruby #{RUBY_VERSION}"
106
- else
107
- "#{RUBY_ENGINE} #{RUBY_VERSION}"
108
- end
109
- end
110
- end
111
-
112
95
  def output_header(mode)
113
96
  min_t = @options[:min_threads]
114
97
  max_t = @options[:max_threads]
@@ -128,6 +111,14 @@ module Puma
128
111
  end
129
112
  end
130
113
 
114
+ def warn_ruby_mn_threads
115
+ return if !ENV.key?('RUBY_MN_THREADS')
116
+
117
+ log "! WARNING: Detected `RUBY_MN_THREADS=#{ENV['RUBY_MN_THREADS']}`"
118
+ log "! This setting is known to cause performance regressions with Puma."
119
+ log "! Consider disabling this environment variable: https://github.com/puma/puma/issues/3720"
120
+ end
121
+
131
122
  def redirected_io?
132
123
  @options[:redirect_stdout] || @options[:redirect_stderr]
133
124
  end
data/lib/puma/server.rb CHANGED
@@ -12,14 +12,14 @@ require_relative 'client'
12
12
  require_relative 'binder'
13
13
  require_relative 'util'
14
14
  require_relative 'request'
15
+ require_relative 'configuration'
15
16
 
16
17
  require 'socket'
17
18
  require 'io/wait' unless Puma::HAS_NATIVE_IO_WAIT
18
19
 
19
20
  module Puma
20
-
21
- # This method was private on Ruby 2.4 but became public on Ruby 2.5+:
22
- Thread.send(:attr_accessor, :puma_server)
21
+ # Add `Thread#puma_server` and `Thread#puma_server=`
22
+ Thread.attr_accessor(:puma_server)
23
23
 
24
24
  # The HTTP Server itself. Serves out a single Rack app.
25
25
  #
@@ -32,6 +32,14 @@ module Puma
32
32
  #
33
33
  # Each `Puma::Server` will have one reactor and one thread pool.
34
34
  class Server
35
+ module FiberPerRequest
36
+ def handle_request(client, requests)
37
+ Fiber.new do
38
+ super
39
+ end.resume
40
+ end
41
+ end
42
+
35
43
  include Puma::Const
36
44
  include Request
37
45
 
@@ -77,6 +85,9 @@ module Puma
77
85
 
78
86
  @thread = nil
79
87
  @thread_pool = nil
88
+ @reactor = nil
89
+
90
+ @env_set_http_version = nil
80
91
 
81
92
  @options = if options.is_a?(UserFileDefaultOptions)
82
93
  options
@@ -100,6 +111,10 @@ module Puma
100
111
  @io_selector_backend = @options[:io_selector_backend]
101
112
  @http_content_length_limit = @options[:http_content_length_limit]
102
113
 
114
+ if @options[:fiber_per_request]
115
+ singleton_class.prepend(FiberPerRequest)
116
+ end
117
+
103
118
  # make this a hash, since we prefer `key?` over `include?`
104
119
  @supported_http_methods =
105
120
  if @options[:supported_http_methods] == :any
@@ -115,7 +130,7 @@ module Puma
115
130
  temp = !!(@options[:environment] =~ /\A(development|test)\z/)
116
131
  @leak_stack_on_error = @options[:environment] ? temp : true
117
132
 
118
- @binder = Binder.new(log_writer)
133
+ @binder = Binder.new(log_writer, @options)
119
134
 
120
135
  ENV['RACK_ENV'] ||= "development"
121
136
 
@@ -166,7 +181,6 @@ module Puma
166
181
  begin
167
182
  skt.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 1) if skt.kind_of? TCPSocket
168
183
  rescue IOError, SystemCallError
169
- Puma::Util.purge_interrupt_queue
170
184
  end
171
185
  end
172
186
 
@@ -175,7 +189,6 @@ module Puma
175
189
  begin
176
190
  skt.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 0) if skt.kind_of? TCPSocket
177
191
  rescue IOError, SystemCallError
178
- Puma::Util.purge_interrupt_queue
179
192
  end
180
193
  end
181
194
  else
@@ -196,7 +209,6 @@ module Puma
196
209
  begin
197
210
  tcp_info = skt.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_INFO)
198
211
  rescue IOError, SystemCallError
199
- Puma::Util.purge_interrupt_queue
200
212
  @precheck_closing = false
201
213
  false
202
214
  else
@@ -318,6 +330,9 @@ module Puma
318
330
  end
319
331
 
320
332
  def handle_servers
333
+ @env_set_http_version = Object.const_defined?(:Rack) && ::Rack.respond_to?(:release) &&
334
+ Gem::Version.new(::Rack.release) < Gem::Version.new('3.1.0')
335
+
321
336
  begin
322
337
  check = @check
323
338
  sockets = [check] + @binder.ios
@@ -367,7 +382,7 @@ module Puma
367
382
  else
368
383
  # if ThreadPool out_of_band code is running, we don't want to add
369
384
  # clients until the code is finished.
370
- sleep 0.001 while pool.out_of_band_running
385
+ pool.wait_while_out_of_band_running
371
386
 
372
387
  # only use delay when clustered and busy
373
388
  if pool.busy_threads >= @max_threads
@@ -460,7 +475,6 @@ module Puma
460
475
  # Advertise this server into the thread
461
476
  Thread.current.puma_server = self
462
477
 
463
- clean_thread_locals = options[:clean_thread_locals]
464
478
  close_socket = true
465
479
 
466
480
  requests = 0
@@ -485,8 +499,6 @@ module Puma
485
499
  when :async
486
500
  close_socket = false
487
501
  when true
488
- ThreadPool.clean_thread_locals if clean_thread_locals
489
-
490
502
  requests += 1
491
503
 
492
504
  client.reset
@@ -520,7 +532,6 @@ module Puma
520
532
  begin
521
533
  client.close if close_socket
522
534
  rescue IOError, SystemCallError
523
- Puma::Util.purge_interrupt_queue
524
535
  # Already closed
525
536
  rescue StandardError => e
526
537
  @log_writer.unknown_error e, nil, "Client"
@@ -621,11 +632,10 @@ module Puma
621
632
  @notify << message
622
633
  rescue IOError, NoMethodError, Errno::EPIPE, Errno::EBADF
623
634
  # The server, in another thread, is shutting down
624
- Puma::Util.purge_interrupt_queue
625
635
  rescue RuntimeError => e
626
636
  # Temporary workaround for https://bugs.ruby-lang.org/issues/13239
627
637
  if e.message.include?('IOError')
628
- Puma::Util.purge_interrupt_queue
638
+ # ignore
629
639
  else
630
640
  raise e
631
641
  end
@@ -675,13 +685,13 @@ module Puma
675
685
  stats = @thread_pool&.stats || {}
676
686
  stats[:max_threads] = @max_threads
677
687
  stats[:requests_count] = @requests_count
678
- stats[:reactor_max] = @reactor.reactor_max
688
+ stats[:reactor_max] = @reactor.reactor_max if @reactor
679
689
  reset_max
680
690
  stats
681
691
  end
682
692
 
683
693
  def reset_max
684
- @reactor.reactor_max = 0
694
+ @reactor.reactor_max = 0 if @reactor
685
695
  @thread_pool.reset_max
686
696
  end
687
697
 
data/lib/puma/single.rb CHANGED
@@ -53,9 +53,12 @@ module Puma
53
53
  server_thread = server.run
54
54
 
55
55
  log "Use Ctrl-C to stop"
56
+
57
+ warn_ruby_mn_threads
58
+
56
59
  redirect_io
57
60
 
58
- @events.fire_on_booted!
61
+ @events.fire_after_booted!
59
62
 
60
63
  debug_loaded_extensions("Loaded Extensions:") if @log_writer.debug?
61
64
 
@@ -53,7 +53,7 @@ module Puma
53
53
  @block = block
54
54
  @out_of_band = options[:out_of_band]
55
55
  @out_of_band_running = false
56
- @clean_thread_locals = options[:clean_thread_locals]
56
+ @out_of_band_condvar = ConditionVariable.new
57
57
  @before_thread_start = options[:before_thread_start]
58
58
  @before_thread_exit = options[:before_thread_exit]
59
59
  @reaping_time = options[:reaping_time]
@@ -82,12 +82,6 @@ module Puma
82
82
 
83
83
  attr_reader :spawned, :trim_requested, :waiting
84
84
 
85
- def self.clean_thread_locals
86
- Thread.current.keys.each do |key| # rubocop: disable Style/HashEachMethods
87
- Thread.current[key] = nil unless key == :__recursive_key__
88
- end
89
- end
90
-
91
85
  # generate stats hash so as not to perform multiple locks
92
86
  # @return [Hash] hash containing stat info from ThreadPool
93
87
  def stats
@@ -175,10 +169,6 @@ module Puma
175
169
  work = todo.shift
176
170
  end
177
171
 
178
- if @clean_thread_locals
179
- ThreadPool.clean_thread_locals
180
- end
181
-
182
172
  begin
183
173
  @out_of_band_pending = true if block.call(work)
184
174
  rescue Exception => e
@@ -199,7 +189,7 @@ module Puma
199
189
 
200
190
  @before_thread_start.each do |b|
201
191
  begin
202
- b.call
192
+ b[:block].call
203
193
  rescue Exception => e
204
194
  STDERR.puts "WARNING before_thread_start hook failed with exception (#{e.class}) #{e.message}"
205
195
  end
@@ -214,7 +204,7 @@ module Puma
214
204
 
215
205
  @before_thread_exit.each do |b|
216
206
  begin
217
- b.call
207
+ b[:block].call
218
208
  rescue Exception => e
219
209
  STDERR.puts "WARNING before_thread_exit hook failed with exception (#{e.class}) #{e.message}"
220
210
  end
@@ -231,17 +221,26 @@ module Puma
231
221
  # we execute on idle hook when all threads are free
232
222
  return false unless @spawned == @waiting
233
223
  @out_of_band_running = true
234
- @out_of_band.each(&:call)
224
+ @out_of_band.each { |b| b[:block].call }
235
225
  true
236
226
  rescue Exception => e
237
227
  STDERR.puts "Exception calling out_of_band_hook: #{e.message} (#{e.class})"
238
228
  true
239
229
  ensure
240
230
  @out_of_band_running = false
231
+ @out_of_band_condvar.broadcast
241
232
  end
242
233
 
243
234
  private :trigger_out_of_band_hook
244
235
 
236
+ def wait_while_out_of_band_running
237
+ return unless @out_of_band_running
238
+
239
+ with_mutex do
240
+ @out_of_band_condvar.wait(@mutex) while @out_of_band_running
241
+ end
242
+ end
243
+
245
244
  # @version 5.0.0
246
245
  def with_mutex(&block)
247
246
  @mutex.owned? ?
data/lib/puma/util.rb CHANGED
@@ -10,13 +10,6 @@ module Puma
10
10
  IO.pipe
11
11
  end
12
12
 
13
- # An instance method on Thread has been provided to address https://bugs.ruby-lang.org/issues/13632,
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
- # Additional context: https://github.com/puma/puma/pull/1345
16
- def purge_interrupt_queue
17
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
18
- end
19
-
20
13
  # Escapes and unescapes a URI escaped string with
21
14
  # +encoding+. +encoding+ will be the target encoding of the string
22
15
  # returned, and it defaults to UTF-8
data/lib/puma.rb CHANGED
@@ -75,4 +75,14 @@ module Puma
75
75
  def self.set_thread_name(name)
76
76
  Thread.current.name = "puma #{name}"
77
77
  end
78
+
79
+ # Shows deprecated warning for renamed methods.
80
+ # @example
81
+ # Puma.deprecate_method_change :on_booted, __callee__, __method__
82
+ #
83
+ def self.deprecate_method_change(method_old, method_caller, method_new)
84
+ if method_old == method_caller
85
+ warn "Use '#{method_new}', '#{method_caller}' is deprecated and will be removed in v8"
86
+ end
87
+ end
78
88
  end
@@ -32,7 +32,7 @@ module Puma
32
32
 
33
33
  @events = options[:events] || ::Puma::Events.new
34
34
 
35
- conf = ::Puma::Configuration.new(options, default_options.merge({events: @events})) do |user_config, file_config, default_config|
35
+ conf = ::Puma::Configuration.new(options, default_options.merge({ events: @events })) do |user_config, file_config, default_config|
36
36
  if options.delete(:Verbose)
37
37
  begin
38
38
  require 'rack/commonlogger' # Rack 1.x
@@ -72,7 +72,7 @@ module Puma
72
72
 
73
73
  log_writer = options.delete(:Silent) ? ::Puma::LogWriter.strings : ::Puma::LogWriter.stdio
74
74
 
75
- launcher = ::Puma::Launcher.new(conf, :log_writer => log_writer, events: @events)
75
+ launcher = ::Puma::Launcher.new(conf, log_writer: log_writer, events: @events)
76
76
 
77
77
  yield launcher if block_given?
78
78
  begin
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.0.0.pre1
4
+ version: 7.0.2
5
5
  platform: java
6
6
  authors:
7
7
  - Evan Phoenix
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
10
+ date: 2025-09-09 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  requirement: !ruby/object:Gem::Requirement
@@ -138,14 +138,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
138
138
  requirements:
139
139
  - - ">="
140
140
  - !ruby/object:Gem::Version
141
- version: '2.4'
141
+ version: '3.0'
142
142
  required_rubygems_version: !ruby/object:Gem::Requirement
143
143
  requirements:
144
144
  - - ">="
145
145
  - !ruby/object:Gem::Version
146
146
  version: '0'
147
147
  requirements: []
148
- rubygems_version: 3.6.9
148
+ rubygems_version: 3.6.3
149
149
  specification_version: 4
150
150
  summary: A Ruby/Rack web server built for parallelism.
151
151
  test_files: []