puma 6.6.1 → 7.0.4
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.
- checksums.yaml +4 -4
- data/History.md +130 -4
- data/README.md +11 -24
- data/docs/fork_worker.md +5 -5
- data/docs/kubernetes.md +6 -4
- data/docs/restart.md +2 -2
- data/docs/signals.md +9 -9
- data/docs/stats.md +2 -1
- data/ext/puma_http11/extconf.rb +2 -17
- data/ext/puma_http11/mini_ssl.c +18 -8
- data/ext/puma_http11/org/jruby/puma/Http11.java +9 -1
- data/ext/puma_http11/puma_http11.c +23 -11
- data/lib/puma/binder.rb +10 -8
- data/lib/puma/cli.rb +3 -5
- data/lib/puma/client.rb +31 -31
- data/lib/puma/cluster/worker.rb +9 -10
- data/lib/puma/cluster/worker_handle.rb +36 -5
- data/lib/puma/cluster.rb +12 -13
- data/lib/puma/cluster_accept_loop_delay.rb +92 -0
- data/lib/puma/commonlogger.rb +3 -3
- data/lib/puma/configuration.rb +88 -43
- data/lib/puma/const.rb +9 -10
- data/lib/puma/control_cli.rb +6 -2
- data/lib/puma/detect.rb +2 -0
- data/lib/puma/dsl.rb +108 -81
- data/lib/puma/error_logger.rb +3 -1
- data/lib/puma/events.rb +25 -10
- data/lib/puma/io_buffer.rb +8 -4
- data/lib/puma/launcher/bundle_pruner.rb +1 -1
- data/lib/puma/launcher.rb +28 -29
- data/lib/puma/minissl.rb +0 -1
- data/lib/puma/plugin/systemd.rb +3 -3
- data/lib/puma/rack/urlmap.rb +1 -1
- data/lib/puma/reactor.rb +19 -4
- data/lib/puma/request.rb +33 -24
- data/lib/puma/runner.rb +8 -17
- data/lib/puma/server.rb +114 -68
- data/lib/puma/single.rb +4 -1
- data/lib/puma/thread_pool.rb +37 -81
- data/lib/puma/util.rb +0 -7
- data/lib/puma.rb +10 -0
- data/lib/rack/handler/puma.rb +2 -2
- metadata +3 -2
data/lib/puma/reactor.rb
CHANGED
@@ -15,6 +15,12 @@ module Puma
|
|
15
15
|
#
|
16
16
|
# The implementation uses a Queue to synchronize adding new objects from the internal select loop.
|
17
17
|
class Reactor
|
18
|
+
|
19
|
+
# @!attribute [rw] reactor_max
|
20
|
+
# Maximum number of clients in the selector. Reset with calls to `Server.stats`.
|
21
|
+
attr_accessor :reactor_max
|
22
|
+
attr_reader :reactor_size
|
23
|
+
|
18
24
|
# Create a new Reactor to monitor IO objects added by #add.
|
19
25
|
# The provided block will be invoked when an IO has data available to read,
|
20
26
|
# its timeout elapses, or when the Reactor shuts down.
|
@@ -29,6 +35,8 @@ module Puma
|
|
29
35
|
@input = Queue.new
|
30
36
|
@timeouts = []
|
31
37
|
@block = block
|
38
|
+
@reactor_size = 0
|
39
|
+
@reactor_max = 0
|
32
40
|
end
|
33
41
|
|
34
42
|
# Run the internal select loop, using a background thread by default.
|
@@ -73,11 +81,15 @@ module Puma
|
|
73
81
|
# Wakeup any registered object that receives incoming data.
|
74
82
|
# Block until the earliest timeout or Selector#wakeup is called.
|
75
83
|
timeout = (earliest = @timeouts.first) && earliest.timeout
|
76
|
-
|
84
|
+
monitor_wake_up = false
|
85
|
+
@selector.select(timeout) do |monitor|
|
86
|
+
monitor_wake_up = true
|
87
|
+
wakeup!(monitor.value)
|
88
|
+
end
|
77
89
|
|
78
90
|
# Wakeup all objects that timed out.
|
79
|
-
timed_out = @timeouts.take_while {|
|
80
|
-
timed_out.each { |
|
91
|
+
timed_out = @timeouts.take_while { |client| client.timeout == 0 }
|
92
|
+
timed_out.each { |client| wakeup!(client) }
|
81
93
|
|
82
94
|
unless @input.empty?
|
83
95
|
until @input.empty?
|
@@ -94,7 +106,7 @@ module Puma
|
|
94
106
|
# NoMethodError may be rarely raised when calling @selector.select, which
|
95
107
|
# is odd. Regardless, it may continue for thousands of calls if retried.
|
96
108
|
# Also, when it raises, @selector.close also raises an error.
|
97
|
-
if NoMethodError === e
|
109
|
+
if !monitor_wake_up && NoMethodError === e
|
98
110
|
close_selector = false
|
99
111
|
else
|
100
112
|
retry
|
@@ -108,6 +120,8 @@ module Puma
|
|
108
120
|
# Start monitoring the object.
|
109
121
|
def register(client)
|
110
122
|
@selector.register(client.to_io, :r).value = client
|
123
|
+
@reactor_size += 1
|
124
|
+
@reactor_max = @reactor_size if @reactor_max < @reactor_size
|
111
125
|
@timeouts << client
|
112
126
|
rescue ArgumentError
|
113
127
|
# unreadable clients raise error when processed by NIO
|
@@ -118,6 +132,7 @@ module Puma
|
|
118
132
|
def wakeup!(client)
|
119
133
|
if @block.call client
|
120
134
|
@selector.deregister client.to_io
|
135
|
+
@reactor_size -= 1
|
121
136
|
@timeouts.delete client
|
122
137
|
end
|
123
138
|
end
|
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 =>
|
123
|
-
@log_writer.unknown_error
|
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(
|
127
|
-
rescue Exception =>
|
128
|
-
@log_writer.unknown_error
|
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(
|
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
|
@@ -164,17 +176,7 @@ module Puma
|
|
164
176
|
return false if closed_socket?(socket)
|
165
177
|
|
166
178
|
# Close the connection after a reasonable number of inline requests
|
167
|
-
|
168
|
-
# This allows Puma to service connections fairly when the number
|
169
|
-
# of concurrent connections exceeds the size of the threadpool.
|
170
|
-
force_keep_alive = if @enable_keep_alives
|
171
|
-
requests < @max_fast_inline ||
|
172
|
-
@thread_pool.busy_threads < @max_threads ||
|
173
|
-
!client.listener.to_io.wait_readable(0)
|
174
|
-
else
|
175
|
-
# Always set force_keep_alive to false if the server has keep-alives not enabled.
|
176
|
-
false
|
177
|
-
end
|
179
|
+
force_keep_alive = @enable_keep_alives && client.requests_served < @max_keep_alive
|
178
180
|
|
179
181
|
resp_info = str_headers(env, status, headers, res_body, io_buffer, force_keep_alive)
|
180
182
|
|
@@ -267,14 +269,21 @@ module Puma
|
|
267
269
|
|
268
270
|
fast_write_response socket, body, io_buffer, chunked, content_length.to_i
|
269
271
|
body.close if close_body
|
270
|
-
keep_alive
|
272
|
+
# if we're shutting down, close keep_alive connections
|
273
|
+
!shutting_down? && keep_alive
|
271
274
|
end
|
272
275
|
|
276
|
+
HTTP_ON_VALUES = { "on" => true, HTTPS => true }
|
277
|
+
private_constant :HTTP_ON_VALUES
|
278
|
+
|
273
279
|
# @param env [Hash] see Puma::Client#env, from request
|
274
280
|
# @return [Puma::Const::PORT_443,Puma::Const::PORT_80]
|
275
281
|
#
|
276
282
|
def default_server_port(env)
|
277
|
-
if [
|
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"
|
278
287
|
PORT_443
|
279
288
|
else
|
280
289
|
PORT_80
|
@@ -478,7 +487,7 @@ module Puma
|
|
478
487
|
|
479
488
|
# The legacy HTTP_VERSION header can be sent as a client header.
|
480
489
|
# Rack v4 may remove using HTTP_VERSION. If so, remove this line.
|
481
|
-
env[HTTP_VERSION] = env[SERVER_PROTOCOL]
|
490
|
+
env[HTTP_VERSION] = env[SERVER_PROTOCOL] if @env_set_http_version
|
482
491
|
end
|
483
492
|
private :normalize_env
|
484
493
|
|
@@ -585,7 +594,7 @@ module Puma
|
|
585
594
|
# response body
|
586
595
|
# @param io_buffer [Puma::IOBuffer] modified inn place
|
587
596
|
# @param force_keep_alive [Boolean] 'anded' with keep_alive, based on system
|
588
|
-
# status and `@
|
597
|
+
# status and `@max_keep_alive`
|
589
598
|
# @return [Hash] resp_info
|
590
599
|
# @version 5.0.3
|
591
600
|
#
|
@@ -668,10 +677,10 @@ module Puma
|
|
668
677
|
if ary
|
669
678
|
ary.each do |v|
|
670
679
|
next if illegal_header_value?(v)
|
671
|
-
io_buffer.append k, colon, v, line_ending
|
680
|
+
io_buffer.append k.downcase, colon, v, line_ending
|
672
681
|
end
|
673
682
|
else
|
674
|
-
io_buffer.append k, colon, line_ending
|
683
|
+
io_buffer.append k.downcase, colon, line_ending
|
675
684
|
end
|
676
685
|
end
|
677
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,15 @@ require_relative 'client'
|
|
12
12
|
require_relative 'binder'
|
13
13
|
require_relative 'util'
|
14
14
|
require_relative 'request'
|
15
|
+
require_relative 'configuration'
|
16
|
+
require_relative 'cluster_accept_loop_delay'
|
15
17
|
|
16
18
|
require 'socket'
|
17
19
|
require 'io/wait' unless Puma::HAS_NATIVE_IO_WAIT
|
18
20
|
|
19
21
|
module Puma
|
20
|
-
|
21
|
-
|
22
|
-
Thread.send(:attr_accessor, :puma_server)
|
22
|
+
# Add `Thread#puma_server` and `Thread#puma_server=`
|
23
|
+
Thread.attr_accessor(:puma_server)
|
23
24
|
|
24
25
|
# The HTTP Server itself. Serves out a single Rack app.
|
25
26
|
#
|
@@ -32,6 +33,14 @@ module Puma
|
|
32
33
|
#
|
33
34
|
# Each `Puma::Server` will have one reactor and one thread pool.
|
34
35
|
class Server
|
36
|
+
module FiberPerRequest
|
37
|
+
def handle_request(client, requests)
|
38
|
+
Fiber.new do
|
39
|
+
super
|
40
|
+
end.resume
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
35
44
|
include Puma::Const
|
36
45
|
include Request
|
37
46
|
|
@@ -50,7 +59,6 @@ module Puma
|
|
50
59
|
attr_accessor :app
|
51
60
|
attr_accessor :binder
|
52
61
|
|
53
|
-
|
54
62
|
# Create a server for the rack app +app+.
|
55
63
|
#
|
56
64
|
# +log_writer+ is a Puma::LogWriter object used to log info and error messages.
|
@@ -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
|
@@ -94,10 +105,19 @@ module Puma
|
|
94
105
|
@min_threads = @options[:min_threads]
|
95
106
|
@max_threads = @options[:max_threads]
|
96
107
|
@queue_requests = @options[:queue_requests]
|
97
|
-
@
|
108
|
+
@max_keep_alive = @options[:max_keep_alive]
|
98
109
|
@enable_keep_alives = @options[:enable_keep_alives]
|
110
|
+
@enable_keep_alives &&= @queue_requests
|
99
111
|
@io_selector_backend = @options[:io_selector_backend]
|
100
112
|
@http_content_length_limit = @options[:http_content_length_limit]
|
113
|
+
@cluster_accept_loop_delay = ClusterAcceptLoopDelay.new(
|
114
|
+
workers: @options[:workers],
|
115
|
+
max_delay: @options[:wait_for_less_busy_worker] || 0 # Real default is in Configuration::DEFAULTS, this is for unit testing
|
116
|
+
)
|
117
|
+
|
118
|
+
if @options[:fiber_per_request]
|
119
|
+
singleton_class.prepend(FiberPerRequest)
|
120
|
+
end
|
101
121
|
|
102
122
|
# make this a hash, since we prefer `key?` over `include?`
|
103
123
|
@supported_http_methods =
|
@@ -114,7 +134,7 @@ module Puma
|
|
114
134
|
temp = !!(@options[:environment] =~ /\A(development|test)\z/)
|
115
135
|
@leak_stack_on_error = @options[:environment] ? temp : true
|
116
136
|
|
117
|
-
@binder = Binder.new(log_writer)
|
137
|
+
@binder = Binder.new(log_writer, @options)
|
118
138
|
|
119
139
|
ENV['RACK_ENV'] ||= "development"
|
120
140
|
|
@@ -165,7 +185,6 @@ module Puma
|
|
165
185
|
begin
|
166
186
|
skt.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 1) if skt.kind_of? TCPSocket
|
167
187
|
rescue IOError, SystemCallError
|
168
|
-
Puma::Util.purge_interrupt_queue
|
169
188
|
end
|
170
189
|
end
|
171
190
|
|
@@ -174,7 +193,6 @@ module Puma
|
|
174
193
|
begin
|
175
194
|
skt.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 0) if skt.kind_of? TCPSocket
|
176
195
|
rescue IOError, SystemCallError
|
177
|
-
Puma::Util.purge_interrupt_queue
|
178
196
|
end
|
179
197
|
end
|
180
198
|
else
|
@@ -195,7 +213,6 @@ module Puma
|
|
195
213
|
begin
|
196
214
|
tcp_info = skt.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_INFO)
|
197
215
|
rescue IOError, SystemCallError
|
198
|
-
Puma::Util.purge_interrupt_queue
|
199
216
|
@precheck_closing = false
|
200
217
|
false
|
201
218
|
else
|
@@ -220,7 +237,6 @@ module Puma
|
|
220
237
|
@thread_pool&.spawned
|
221
238
|
end
|
222
239
|
|
223
|
-
|
224
240
|
# This number represents the number of requests that
|
225
241
|
# the server is capable of taking right now.
|
226
242
|
#
|
@@ -233,11 +249,6 @@ module Puma
|
|
233
249
|
@thread_pool&.pool_capacity
|
234
250
|
end
|
235
251
|
|
236
|
-
# @!attribute [r] busy_threads
|
237
|
-
def busy_threads
|
238
|
-
@thread_pool&.busy_threads
|
239
|
-
end
|
240
|
-
|
241
252
|
# Runs the server.
|
242
253
|
#
|
243
254
|
# If +background+ is true (the default) then a thread is spun
|
@@ -254,7 +265,11 @@ module Puma
|
|
254
265
|
@thread_pool = ThreadPool.new(thread_name, options) { |client| process_client client }
|
255
266
|
|
256
267
|
if @queue_requests
|
257
|
-
@reactor = Reactor.new(@io_selector_backend) { |c|
|
268
|
+
@reactor = Reactor.new(@io_selector_backend) { |c|
|
269
|
+
# Inversion of control, the reactor is calling a method on the server when it
|
270
|
+
# is done buffering a request or receives a new request from a keepalive connection.
|
271
|
+
self.reactor_wakeup(c)
|
272
|
+
}
|
258
273
|
@reactor.run
|
259
274
|
end
|
260
275
|
|
@@ -279,6 +294,9 @@ module Puma
|
|
279
294
|
# This method is called from the Reactor thread when a queued Client receives data,
|
280
295
|
# times out, or when the Reactor is shutting down.
|
281
296
|
#
|
297
|
+
# While the code lives in the Server, the logic is executed on the reactor thread, independently
|
298
|
+
# from the server.
|
299
|
+
#
|
282
300
|
# It is responsible for ensuring that a request has been completely received
|
283
301
|
# before it starts to be processed by the ThreadPool. This may be known as read buffering.
|
284
302
|
# If read buffering is not done, and no other read buffering is performed (such as by an application server
|
@@ -313,11 +331,14 @@ module Puma
|
|
313
331
|
end
|
314
332
|
rescue StandardError => e
|
315
333
|
client_error(e, client)
|
316
|
-
client
|
334
|
+
close_client_safely(client)
|
317
335
|
true
|
318
336
|
end
|
319
337
|
|
320
338
|
def handle_servers
|
339
|
+
@env_set_http_version = Object.const_defined?(:Rack) && ::Rack.respond_to?(:release) &&
|
340
|
+
Gem::Version.new(::Rack.release) < Gem::Version.new('3.1.0')
|
341
|
+
|
321
342
|
begin
|
322
343
|
check = @check
|
323
344
|
sockets = [check] + @binder.ios
|
@@ -364,8 +385,18 @@ module Puma
|
|
364
385
|
if sock == check
|
365
386
|
break if handle_check
|
366
387
|
else
|
367
|
-
|
368
|
-
|
388
|
+
# if ThreadPool out_of_band code is running, we don't want to add
|
389
|
+
# clients until the code is finished.
|
390
|
+
pool.wait_while_out_of_band_running
|
391
|
+
|
392
|
+
# A well rested herd (cluster) runs faster
|
393
|
+
if @cluster_accept_loop_delay.on? && (busy_threads_plus_todo = pool.busy_threads) > 0
|
394
|
+
delay = @cluster_accept_loop_delay.calculate(
|
395
|
+
max_threads: @max_threads,
|
396
|
+
busy_threads_plus_todo: busy_threads_plus_todo
|
397
|
+
)
|
398
|
+
sleep(delay)
|
399
|
+
end
|
369
400
|
|
370
401
|
io = begin
|
371
402
|
sock.accept_nonblock
|
@@ -373,11 +404,9 @@ module Puma
|
|
373
404
|
next
|
374
405
|
end
|
375
406
|
drain += 1 if shutting_down?
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
c.send(addr_send_name, addr_value) if addr_value
|
380
|
-
}
|
407
|
+
client = new_client(io, sock)
|
408
|
+
client.send(addr_send_name, addr_value) if addr_value
|
409
|
+
pool << client
|
381
410
|
end
|
382
411
|
end
|
383
412
|
rescue IOError, Errno::EBADF
|
@@ -414,6 +443,14 @@ module Puma
|
|
414
443
|
@events.fire :state, :done
|
415
444
|
end
|
416
445
|
|
446
|
+
# :nodoc:
|
447
|
+
def new_client(io, sock)
|
448
|
+
client = Client.new(io, @binder.env(sock))
|
449
|
+
client.listener = sock
|
450
|
+
client.http_content_length_limit = @http_content_length_limit
|
451
|
+
client
|
452
|
+
end
|
453
|
+
|
417
454
|
# :nodoc:
|
418
455
|
def handle_check
|
419
456
|
cmd = @check.read(1)
|
@@ -447,14 +484,12 @@ module Puma
|
|
447
484
|
# Advertise this server into the thread
|
448
485
|
Thread.current.puma_server = self
|
449
486
|
|
450
|
-
clean_thread_locals = options[:clean_thread_locals]
|
451
487
|
close_socket = true
|
452
488
|
|
453
489
|
requests = 0
|
454
490
|
|
455
491
|
begin
|
456
|
-
if @queue_requests &&
|
457
|
-
!client.eagerly_finish
|
492
|
+
if @queue_requests && !client.eagerly_finish
|
458
493
|
|
459
494
|
client.set_timeout(@first_data_timeout)
|
460
495
|
if @reactor.add client
|
@@ -467,39 +502,31 @@ module Puma
|
|
467
502
|
client.finish(@first_data_timeout)
|
468
503
|
end
|
469
504
|
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
break
|
478
|
-
when true
|
479
|
-
ThreadPool.clean_thread_locals if clean_thread_locals
|
480
|
-
|
481
|
-
requests += 1
|
482
|
-
|
483
|
-
# As an optimization, try to read the next request from the
|
484
|
-
# socket for a short time before returning to the reactor.
|
485
|
-
fast_check = @status == :run
|
505
|
+
@requests_count += 1
|
506
|
+
case handle_request(client, requests + 1)
|
507
|
+
when false
|
508
|
+
when :async
|
509
|
+
close_socket = false
|
510
|
+
when true
|
511
|
+
requests += 1
|
486
512
|
|
487
|
-
|
488
|
-
# number of inline requests if there are other requests pending.
|
489
|
-
fast_check = false if requests >= @max_fast_inline &&
|
490
|
-
@thread_pool.backlog > 0
|
513
|
+
client.reset
|
491
514
|
|
492
|
-
|
493
|
-
|
494
|
-
|
515
|
+
# This indicates data exists in the client read buffer and there may be
|
516
|
+
# additional requests on it, so process them
|
517
|
+
next_request_ready = if client.has_back_to_back_requests?
|
518
|
+
with_force_shutdown(client) { client.process_back_to_back_requests }
|
519
|
+
else
|
520
|
+
with_force_shutdown(client) { client.eagerly_finish }
|
521
|
+
end
|
495
522
|
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
523
|
+
if next_request_ready
|
524
|
+
@thread_pool << client
|
525
|
+
close_socket = false
|
526
|
+
elsif @queue_requests
|
527
|
+
client.set_timeout @persistent_timeout
|
528
|
+
if @reactor.add client
|
529
|
+
close_socket = false
|
503
530
|
end
|
504
531
|
end
|
505
532
|
end
|
@@ -511,17 +538,21 @@ module Puma
|
|
511
538
|
ensure
|
512
539
|
client.io_buffer.reset
|
513
540
|
|
514
|
-
|
515
|
-
client.close if close_socket
|
516
|
-
rescue IOError, SystemCallError
|
517
|
-
Puma::Util.purge_interrupt_queue
|
518
|
-
# Already closed
|
519
|
-
rescue StandardError => e
|
520
|
-
@log_writer.unknown_error e, nil, "Client"
|
521
|
-
end
|
541
|
+
close_client_safely(client) if close_socket
|
522
542
|
end
|
523
543
|
end
|
524
544
|
|
545
|
+
# :nodoc:
|
546
|
+
def close_client_safely(client)
|
547
|
+
client.close
|
548
|
+
rescue IOError, SystemCallError
|
549
|
+
# Already closed
|
550
|
+
rescue MiniSSL::SSLError => e
|
551
|
+
@log_writer.ssl_error e, client.io
|
552
|
+
rescue StandardError => e
|
553
|
+
@log_writer.unknown_error e, nil, "Client"
|
554
|
+
end
|
555
|
+
|
525
556
|
# Triggers a client timeout if the thread-pool shuts down
|
526
557
|
# during execution of the provided block.
|
527
558
|
def with_force_shutdown(client, &block)
|
@@ -615,11 +646,10 @@ module Puma
|
|
615
646
|
@notify << message
|
616
647
|
rescue IOError, NoMethodError, Errno::EPIPE, Errno::EBADF
|
617
648
|
# The server, in another thread, is shutting down
|
618
|
-
Puma::Util.purge_interrupt_queue
|
619
649
|
rescue RuntimeError => e
|
620
650
|
# Temporary workaround for https://bugs.ruby-lang.org/issues/13239
|
621
651
|
if e.message.include?('IOError')
|
622
|
-
|
652
|
+
# ignore
|
623
653
|
else
|
624
654
|
raise e
|
625
655
|
end
|
@@ -650,7 +680,16 @@ module Puma
|
|
650
680
|
|
651
681
|
# List of methods invoked by #stats.
|
652
682
|
# @version 5.0.0
|
653
|
-
STAT_METHODS = [
|
683
|
+
STAT_METHODS = [
|
684
|
+
:backlog,
|
685
|
+
:running,
|
686
|
+
:pool_capacity,
|
687
|
+
:busy_threads,
|
688
|
+
:backlog_max,
|
689
|
+
:max_threads,
|
690
|
+
:requests_count,
|
691
|
+
:reactor_max,
|
692
|
+
].freeze
|
654
693
|
|
655
694
|
# Returns a hash of stats about the running server for reporting purposes.
|
656
695
|
# @version 5.0.0
|
@@ -660,9 +699,16 @@ module Puma
|
|
660
699
|
stats = @thread_pool&.stats || {}
|
661
700
|
stats[:max_threads] = @max_threads
|
662
701
|
stats[:requests_count] = @requests_count
|
702
|
+
stats[:reactor_max] = @reactor.reactor_max if @reactor
|
703
|
+
reset_max
|
663
704
|
stats
|
664
705
|
end
|
665
706
|
|
707
|
+
def reset_max
|
708
|
+
@reactor.reactor_max = 0 if @reactor
|
709
|
+
@thread_pool.reset_max
|
710
|
+
end
|
711
|
+
|
666
712
|
# below are 'delegations' to binder
|
667
713
|
# remove in Puma 7?
|
668
714
|
|
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.
|
61
|
+
@events.fire_after_booted!
|
59
62
|
|
60
63
|
debug_loaded_extensions("Loaded Extensions:") if @log_writer.debug?
|
61
64
|
|