puma 5.2.0 → 5.3.2

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.

data/lib/puma/runner.rb CHANGED
@@ -126,6 +126,11 @@ module Puma
126
126
  STDERR.puts "=== puma startup: #{Time.now} ==="
127
127
  STDERR.flush unless STDERR.sync
128
128
  end
129
+
130
+ if @options[:mutate_stdout_and_stderr_to_sync_on_write]
131
+ STDOUT.sync = true
132
+ STDERR.sync = true
133
+ end
129
134
  end
130
135
 
131
136
  def load_and_bind
data/lib/puma/server.rb CHANGED
@@ -120,17 +120,13 @@ module Puma
120
120
  # :nodoc:
121
121
  # @version 5.0.0
122
122
  def tcp_cork_supported?
123
- RbConfig::CONFIG['host_os'] =~ /linux/ &&
124
- Socket.const_defined?(:IPPROTO_TCP) &&
125
- Socket.const_defined?(:TCP_CORK)
123
+ Socket.const_defined?(:TCP_CORK) && Socket.const_defined?(:IPPROTO_TCP)
126
124
  end
127
125
 
128
126
  # :nodoc:
129
127
  # @version 5.0.0
130
128
  def closed_socket_supported?
131
- RbConfig::CONFIG['host_os'] =~ /linux/ &&
132
- Socket.const_defined?(:IPPROTO_TCP) &&
133
- Socket.const_defined?(:TCP_INFO)
129
+ Socket.const_defined?(:TCP_INFO) && Socket.const_defined?(:IPPROTO_TCP)
134
130
  end
135
131
  private :tcp_cork_supported?
136
132
  private :closed_socket_supported?
@@ -138,24 +134,25 @@ module Puma
138
134
 
139
135
  # On Linux, use TCP_CORK to better control how the TCP stack
140
136
  # packetizes our stream. This improves both latency and throughput.
137
+ # socket parameter may be an MiniSSL::Socket, so use to_io
141
138
  #
142
139
  if tcp_cork_supported?
143
- UNPACK_TCP_STATE_FROM_TCP_INFO = "C".freeze
144
-
145
140
  # 6 == Socket::IPPROTO_TCP
146
141
  # 3 == TCP_CORK
147
142
  # 1/0 == turn on/off
148
143
  def cork_socket(socket)
144
+ skt = socket.to_io
149
145
  begin
150
- socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 1) if socket.kind_of? TCPSocket
146
+ skt.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 1) if skt.kind_of? TCPSocket
151
147
  rescue IOError, SystemCallError
152
148
  Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
153
149
  end
154
150
  end
155
151
 
156
152
  def uncork_socket(socket)
153
+ skt = socket.to_io
157
154
  begin
158
- socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 0) if socket.kind_of? TCPSocket
155
+ skt.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 0) if skt.kind_of? TCPSocket
159
156
  rescue IOError, SystemCallError
160
157
  Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
161
158
  end
@@ -169,12 +166,14 @@ module Puma
169
166
  end
170
167
 
171
168
  if closed_socket_supported?
169
+ UNPACK_TCP_STATE_FROM_TCP_INFO = "C".freeze
170
+
172
171
  def closed_socket?(socket)
173
- return false unless socket.kind_of? TCPSocket
174
- return false unless @precheck_closing
172
+ skt = socket.to_io
173
+ return false unless skt.kind_of?(TCPSocket) && @precheck_closing
175
174
 
176
175
  begin
177
- tcp_info = socket.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_INFO)
176
+ tcp_info = skt.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_INFO)
178
177
  rescue IOError, SystemCallError
179
178
  Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
180
179
  @precheck_closing = false
@@ -296,6 +295,9 @@ module Puma
296
295
  @thread_pool << client
297
296
  elsif shutdown || client.timeout == 0
298
297
  client.timeout!
298
+ else
299
+ client.set_timeout(@first_data_timeout)
300
+ false
299
301
  end
300
302
  rescue StandardError => e
301
303
  client_error(e, client)
@@ -309,6 +311,7 @@ module Puma
309
311
  sockets = [check] + @binder.ios
310
312
  pool = @thread_pool
311
313
  queue_requests = @queue_requests
314
+ drain = @options[:drain_on_shutdown] ? 0 : nil
312
315
 
313
316
  remote_addr_value = nil
314
317
  remote_addr_header = nil
@@ -320,23 +323,25 @@ module Puma
320
323
  remote_addr_header = @options[:remote_address_header]
321
324
  end
322
325
 
323
- while @status == :run
326
+ while @status == :run || (drain && shutting_down?)
324
327
  begin
325
- ios = IO.select sockets
328
+ ios = IO.select sockets, nil, nil, (shutting_down? ? 0 : nil)
329
+ break unless ios
326
330
  ios.first.each do |sock|
327
331
  if sock == check
328
332
  break if handle_check
329
333
  else
330
334
  pool.wait_until_not_full
331
- pool.wait_for_less_busy_worker(
332
- @options[:wait_for_less_busy_worker].to_f)
335
+ pool.wait_for_less_busy_worker(@options[:wait_for_less_busy_worker])
333
336
 
334
337
  io = begin
335
338
  sock.accept_nonblock
336
339
  rescue IO::WaitReadable
337
340
  next
338
341
  end
342
+ drain += 1 if shutting_down?
339
343
  client = Client.new io, @binder.env(sock)
344
+ client.listener = sock
340
345
  if remote_addr_value
341
346
  client.peerip = remote_addr_value
342
347
  elsif remote_addr_header
@@ -350,6 +355,7 @@ module Puma
350
355
  end
351
356
  end
352
357
 
358
+ @events.debug "Drained #{drain} additional connections." if drain
353
359
  @events.fire :state, @status
354
360
 
355
361
  if queue_requests
@@ -429,7 +435,7 @@ module Puma
429
435
 
430
436
  while true
431
437
  @requests_count += 1
432
- case handle_request(client, buffer)
438
+ case handle_request(client, buffer, requests + 1)
433
439
  when false
434
440
  break
435
441
  when :async
@@ -442,18 +448,17 @@ module Puma
442
448
 
443
449
  requests += 1
444
450
 
445
- check_for_more_data = @status == :run
451
+ # As an optimization, try to read the next request from the
452
+ # socket for a short time before returning to the reactor.
453
+ fast_check = @status == :run
446
454
 
447
- if requests >= @max_fast_inline
448
- # This will mean that reset will only try to use the data it already
449
- # has buffered and won't try to read more data. What this means is that
450
- # every client, independent of their request speed, gets treated like a slow
451
- # one once every max_fast_inline requests.
452
- check_for_more_data = false
453
- end
455
+ # Always pass the client back to the reactor after a reasonable
456
+ # number of inline requests if there are other requests pending.
457
+ fast_check = false if requests >= @max_fast_inline &&
458
+ @thread_pool.backlog > 0
454
459
 
455
460
  next_request_ready = with_force_shutdown(client) do
456
- client.reset(check_for_more_data)
461
+ client.reset(fast_check)
457
462
  end
458
463
 
459
464
  unless next_request_ready
@@ -527,7 +532,8 @@ module Puma
527
532
  end
528
533
 
529
534
  if @leak_stack_on_error
530
- [status, {}, ["Puma caught this error: #{e.message} (#{e.class})\n#{e.backtrace.join("\n")}"]]
535
+ backtrace = e.backtrace.nil? ? '<no backtrace available>' : e.backtrace.join("\n")
536
+ [status, {}, ["Puma caught this error: #{e.message} (#{e.class})\n#{backtrace}"]]
531
537
  else
532
538
  [status, {}, ["An unhandled lowlevel error occurred. The application logs may have details.\n"]]
533
539
  end
@@ -551,28 +557,6 @@ module Puma
551
557
  $stdout.syswrite "#{pid}: === End thread backtrace dump ===\n"
552
558
  end
553
559
 
554
- if @options[:drain_on_shutdown]
555
- count = 0
556
-
557
- while true
558
- ios = IO.select @binder.ios, nil, nil, 0
559
- break unless ios
560
-
561
- ios.first.each do |sock|
562
- begin
563
- if io = sock.accept_nonblock
564
- count += 1
565
- client = Client.new io, @binder.env(sock)
566
- @thread_pool << client
567
- end
568
- rescue SystemCallError
569
- end
570
- end
571
- end
572
-
573
- @events.debug "Drained #{count} additional connections."
574
- end
575
-
576
560
  if @status != :restart
577
561
  @binder.close
578
562
  end
@@ -13,7 +13,7 @@ module Puma
13
13
  # a thread pool via the `Puma::ThreadPool#<<` operator where it is stored in a `@todo` array.
14
14
  #
15
15
  # Each thread in the pool has an internal loop where it pulls a request from the `@todo` array
16
- # and proceses it.
16
+ # and processes it.
17
17
  class ThreadPool
18
18
  class ForceShutdown < RuntimeError
19
19
  end
@@ -220,7 +220,7 @@ module Puma
220
220
  # then the `@todo` array would stay the same size as the reactor works
221
221
  # to try to buffer the request. In that scenario the next call to this
222
222
  # method would not block and another request would be added into the reactor
223
- # by the server. This would continue until a fully bufferend request
223
+ # by the server. This would continue until a fully buffered request
224
224
  # makes it through the reactor and can then be processed by the thread pool.
225
225
  def wait_until_not_full
226
226
  with_mutex do
@@ -240,11 +240,12 @@ module Puma
240
240
 
241
241
  # @version 5.0.0
242
242
  def wait_for_less_busy_worker(delay_s)
243
+ return unless delay_s && delay_s > 0
244
+
243
245
  # Ruby MRI does GVL, this can result
244
246
  # in processing contention when multiple threads
245
247
  # (requests) are running concurrently
246
248
  return unless Puma.mri?
247
- return unless delay_s > 0
248
249
 
249
250
  with_mutex do
250
251
  return if @shutdown
@@ -31,6 +31,7 @@ module Rack
31
31
 
32
32
  conf = ::Puma::Configuration.new(options, default_options) do |user_config, file_config, default_config|
33
33
  if options.delete(:Verbose)
34
+ require 'rack/common_logger'
34
35
  app = Rack::CommonLogger.new(app, STDOUT)
35
36
  end
36
37
 
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: 5.2.0
4
+ version: 5.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Phoenix
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-27 00:00:00.000000000 Z
11
+ date: 2021-05-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nio4r
@@ -57,6 +57,7 @@ files:
57
57
  - docs/kubernetes.md
58
58
  - docs/nginx.md
59
59
  - docs/plugins.md
60
+ - docs/rails_dev_mode.md
60
61
  - docs/restart.md
61
62
  - docs/signals.md
62
63
  - docs/stats.md
@@ -139,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
139
140
  - !ruby/object:Gem::Version
140
141
  version: '0'
141
142
  requirements: []
142
- rubygems_version: 3.2.1
143
+ rubygems_version: 3.2.3
143
144
  signing_key:
144
145
  specification_version: 4
145
146
  summary: Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server for