puma 4.3.6-java → 5.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.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/History.md +1153 -518
- data/LICENSE +23 -20
- data/README.md +26 -13
- data/docs/architecture.md +3 -3
- data/docs/deployment.md +9 -3
- data/docs/fork_worker.md +31 -0
- data/docs/jungle/README.md +13 -0
- data/{tools → docs}/jungle/rc.d/README.md +0 -0
- data/{tools → docs}/jungle/rc.d/puma +0 -0
- data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
- data/{tools → docs}/jungle/upstart/README.md +0 -0
- data/{tools → docs}/jungle/upstart/puma-manager.conf +0 -0
- data/{tools → docs}/jungle/upstart/puma.conf +0 -0
- data/docs/signals.md +7 -6
- data/docs/systemd.md +1 -63
- data/ext/puma_http11/PumaHttp11Service.java +2 -4
- data/ext/puma_http11/extconf.rb +4 -3
- data/ext/puma_http11/mini_ssl.c +15 -2
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +77 -18
- data/ext/puma_http11/puma_http11.c +6 -38
- data/lib/puma.rb +20 -0
- data/lib/puma/app/status.rb +14 -1
- data/lib/puma/binder.rb +90 -68
- data/lib/puma/cli.rb +7 -15
- data/lib/puma/client.rb +62 -13
- data/lib/puma/cluster.rb +193 -74
- data/lib/puma/commonlogger.rb +2 -2
- data/lib/puma/configuration.rb +31 -42
- data/lib/puma/const.rb +3 -3
- data/lib/puma/control_cli.rb +29 -17
- data/lib/puma/detect.rb +17 -0
- data/lib/puma/dsl.rb +144 -70
- data/lib/puma/error_logger.rb +97 -0
- data/lib/puma/events.rb +37 -31
- data/lib/puma/io_buffer.rb +9 -2
- data/lib/puma/jruby_restart.rb +0 -58
- data/lib/puma/launcher.rb +57 -31
- data/lib/puma/minissl.rb +68 -18
- data/lib/puma/minissl/context_builder.rb +0 -3
- data/lib/puma/null_io.rb +1 -1
- data/lib/puma/plugin.rb +1 -10
- data/lib/puma/puma_http11.jar +0 -0
- data/lib/puma/rack/builder.rb +0 -4
- data/lib/puma/reactor.rb +10 -16
- data/lib/puma/runner.rb +8 -36
- data/lib/puma/server.rb +161 -218
- data/lib/puma/single.rb +8 -64
- data/lib/puma/state_file.rb +6 -3
- data/lib/puma/thread_pool.rb +116 -51
- data/lib/puma/util.rb +1 -0
- data/lib/rack/handler/puma.rb +1 -3
- data/tools/{docker/Dockerfile → Dockerfile} +0 -0
- metadata +17 -19
- data/docs/tcp_mode.md +0 -96
- data/ext/puma_http11/io_buffer.c +0 -155
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
- data/lib/puma/tcp_logger.rb +0 -41
- data/tools/jungle/README.md +0 -19
- data/tools/jungle/init.d/README.md +0 -61
- data/tools/jungle/init.d/puma +0 -421
- data/tools/jungle/init.d/run-puma +0 -18
data/lib/puma/minissl.rb
CHANGED
@@ -5,8 +5,18 @@ begin
|
|
5
5
|
rescue LoadError
|
6
6
|
end
|
7
7
|
|
8
|
+
# need for Puma::MiniSSL::OPENSSL constants used in `HAS_TLS1_3`
|
9
|
+
require 'puma/puma_http11'
|
10
|
+
|
8
11
|
module Puma
|
9
12
|
module MiniSSL
|
13
|
+
# Define constant at runtime, as it's easy to determine at built time,
|
14
|
+
# but Puma could (it shouldn't) be loaded with an older OpenSSL version
|
15
|
+
# @version 5.0.0
|
16
|
+
HAS_TLS1_3 = !IS_JRUBY &&
|
17
|
+
(OPENSSL_VERSION[/ \d+\.\d+\.\d+/].split('.').map(&:to_i) <=> [1,1,1]) != -1 &&
|
18
|
+
(OPENSSL_LIBRARY_VERSION[/ \d+\.\d+\.\d+/].split('.').map(&:to_i) <=> [1,1,1]) !=-1
|
19
|
+
|
10
20
|
class Socket
|
11
21
|
def initialize(socket, engine)
|
12
22
|
@socket = socket
|
@@ -14,6 +24,7 @@ module Puma
|
|
14
24
|
@peercert = nil
|
15
25
|
end
|
16
26
|
|
27
|
+
# @!attribute [r] to_io
|
17
28
|
def to_io
|
18
29
|
@socket
|
19
30
|
end
|
@@ -22,6 +33,27 @@ module Puma
|
|
22
33
|
@socket.closed?
|
23
34
|
end
|
24
35
|
|
36
|
+
# Returns a two element array,
|
37
|
+
# first is protocol version (SSL_get_version),
|
38
|
+
# second is 'handshake' state (SSL_state_string)
|
39
|
+
#
|
40
|
+
# Used for dropping tcp connections to ssl.
|
41
|
+
# See OpenSSL ssl/ssl_stat.c SSL_state_string for info
|
42
|
+
# @!attribute [r] ssl_version_state
|
43
|
+
# @version 5.0.0
|
44
|
+
#
|
45
|
+
def ssl_version_state
|
46
|
+
IS_JRUBY ? [nil, nil] : @engine.ssl_vers_st
|
47
|
+
end
|
48
|
+
|
49
|
+
# Used to check the handshake status, in particular when a TCP connection
|
50
|
+
# is made with TLSv1.3 as an available protocol
|
51
|
+
# @version 5.0.0
|
52
|
+
def bad_tlsv1_3?
|
53
|
+
HAS_TLS1_3 && @engine.ssl_vers_st == ['TLSv1.3', 'SSLERR']
|
54
|
+
end
|
55
|
+
private :bad_tlsv1_3?
|
56
|
+
|
25
57
|
def readpartial(size)
|
26
58
|
while true
|
27
59
|
output = @engine.read
|
@@ -41,6 +73,7 @@ module Puma
|
|
41
73
|
|
42
74
|
def engine_read_all
|
43
75
|
output = @engine.read
|
76
|
+
raise SSLError.exception "HTTP connection?" if bad_tlsv1_3?
|
44
77
|
while output and additional_output = @engine.read
|
45
78
|
output << additional_output
|
46
79
|
end
|
@@ -107,14 +140,18 @@ module Puma
|
|
107
140
|
alias_method :<<, :write
|
108
141
|
|
109
142
|
# This is a temporary fix to deal with websockets code using
|
110
|
-
# write_nonblock.
|
143
|
+
# write_nonblock.
|
144
|
+
|
145
|
+
# The problem with implementing it properly
|
111
146
|
# is that it means we'd have to have the ability to rewind
|
112
147
|
# an engine because after we write+extract, the socket
|
113
148
|
# write_nonblock call might raise an exception and later
|
114
149
|
# code would pass the same data in, but the engine would think
|
115
|
-
# it had already written the data in.
|
116
|
-
#
|
117
|
-
#
|
150
|
+
# it had already written the data in.
|
151
|
+
#
|
152
|
+
# So for the time being (and since write blocking is quite rare),
|
153
|
+
# go ahead and actually block in write_nonblock.
|
154
|
+
#
|
118
155
|
def write_nonblock(data, *_)
|
119
156
|
write data
|
120
157
|
end
|
@@ -125,11 +162,14 @@ module Puma
|
|
125
162
|
|
126
163
|
def read_and_drop(timeout = 1)
|
127
164
|
return :timeout unless IO.select([@socket], nil, nil, timeout)
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
165
|
+
case @socket.read_nonblock(1024, exception: false)
|
166
|
+
when nil
|
167
|
+
:eof
|
168
|
+
when :wait_readable
|
169
|
+
:eagain
|
170
|
+
else
|
171
|
+
:drop
|
172
|
+
end
|
133
173
|
end
|
134
174
|
|
135
175
|
def should_drop_bytes?
|
@@ -141,9 +181,7 @@ module Puma
|
|
141
181
|
# Read any drop any partially initialized sockets and any received bytes during shutdown.
|
142
182
|
# Don't let this socket hold this loop forever.
|
143
183
|
# If it can't send more packets within 1s, then give up.
|
144
|
-
while should_drop_bytes?
|
145
|
-
return if [:timeout, :eof].include?(read_and_drop(1))
|
146
|
-
end
|
184
|
+
return if [:timeout, :eof].include?(read_and_drop(1)) while should_drop_bytes?
|
147
185
|
rescue IOError, SystemCallError
|
148
186
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
149
187
|
# nothing
|
@@ -152,10 +190,12 @@ module Puma
|
|
152
190
|
end
|
153
191
|
end
|
154
192
|
|
193
|
+
# @!attribute [r] peeraddr
|
155
194
|
def peeraddr
|
156
195
|
@socket.peeraddr
|
157
196
|
end
|
158
197
|
|
198
|
+
# @!attribute [r] peercert
|
159
199
|
def peercert
|
160
200
|
return @peercert if @peercert
|
161
201
|
|
@@ -166,12 +206,13 @@ module Puma
|
|
166
206
|
end
|
167
207
|
end
|
168
208
|
|
169
|
-
if
|
209
|
+
if IS_JRUBY
|
210
|
+
OPENSSL_NO_SSL3 = false
|
211
|
+
OPENSSL_NO_TLS1 = false
|
212
|
+
|
170
213
|
class SSLError < StandardError
|
171
214
|
# Define this for jruby even though it isn't used.
|
172
215
|
end
|
173
|
-
|
174
|
-
def self.check; end
|
175
216
|
end
|
176
217
|
|
177
218
|
class Context
|
@@ -183,7 +224,7 @@ module Puma
|
|
183
224
|
@no_tlsv1_1 = false
|
184
225
|
end
|
185
226
|
|
186
|
-
if
|
227
|
+
if IS_JRUBY
|
187
228
|
# jruby-specific Context properties: java uses a keystore and password pair rather than a cert/key pair
|
188
229
|
attr_reader :keystore
|
189
230
|
attr_accessor :keystore_pass
|
@@ -227,14 +268,16 @@ module Puma
|
|
227
268
|
end
|
228
269
|
|
229
270
|
# disables TLSv1
|
271
|
+
# @!attribute [w] no_tlsv1=
|
230
272
|
def no_tlsv1=(tlsv1)
|
231
|
-
raise ArgumentError, "Invalid value of no_tlsv1" unless ['true', 'false', true, false].include?(tlsv1)
|
273
|
+
raise ArgumentError, "Invalid value of no_tlsv1=" unless ['true', 'false', true, false].include?(tlsv1)
|
232
274
|
@no_tlsv1 = tlsv1
|
233
275
|
end
|
234
276
|
|
235
277
|
# disables TLSv1 and TLSv1.1. Overrides `#no_tlsv1=`
|
278
|
+
# @!attribute [w] no_tlsv1_1=
|
236
279
|
def no_tlsv1_1=(tlsv1_1)
|
237
|
-
raise ArgumentError, "Invalid value of
|
280
|
+
raise ArgumentError, "Invalid value of no_tlsv1_1=" unless ['true', 'false', true, false].include?(tlsv1_1)
|
238
281
|
@no_tlsv1_1 = tlsv1_1
|
239
282
|
end
|
240
283
|
|
@@ -250,6 +293,7 @@ module Puma
|
|
250
293
|
@ctx = ctx
|
251
294
|
end
|
252
295
|
|
296
|
+
# @!attribute [r] to_io
|
253
297
|
def to_io
|
254
298
|
@socket
|
255
299
|
end
|
@@ -270,6 +314,12 @@ module Puma
|
|
270
314
|
Socket.new io, engine
|
271
315
|
end
|
272
316
|
|
317
|
+
# @!attribute [r] addr
|
318
|
+
# @version 5.0.0
|
319
|
+
def addr
|
320
|
+
@socket.addr
|
321
|
+
end
|
322
|
+
|
273
323
|
def close
|
274
324
|
@socket.close unless @socket.closed? # closed? call is for Windows
|
275
325
|
end
|
data/lib/puma/null_io.rb
CHANGED
data/lib/puma/plugin.rb
CHANGED
@@ -10,7 +10,7 @@ module Puma
|
|
10
10
|
|
11
11
|
def create(name)
|
12
12
|
if cls = Plugins.find(name)
|
13
|
-
plugin = cls.new
|
13
|
+
plugin = cls.new
|
14
14
|
@instances << plugin
|
15
15
|
return plugin
|
16
16
|
end
|
@@ -104,17 +104,8 @@ module Puma
|
|
104
104
|
Plugins.register name, cls
|
105
105
|
end
|
106
106
|
|
107
|
-
def initialize(loader)
|
108
|
-
@loader = loader
|
109
|
-
end
|
110
|
-
|
111
107
|
def in_background(&blk)
|
112
108
|
Plugins.add_background blk
|
113
109
|
end
|
114
|
-
|
115
|
-
def workers_supported?
|
116
|
-
return false if Puma.jruby? || Puma.windows?
|
117
|
-
true
|
118
|
-
end
|
119
110
|
end
|
120
111
|
end
|
data/lib/puma/puma_http11.jar
CHANGED
Binary file
|
data/lib/puma/rack/builder.rb
CHANGED
@@ -67,10 +67,6 @@ module Puma::Rack
|
|
67
67
|
options[:environment] = e
|
68
68
|
}
|
69
69
|
|
70
|
-
opts.on("-D", "--daemonize", "run daemonized in the background") { |d|
|
71
|
-
options[:daemonize] = d ? true : false
|
72
|
-
}
|
73
|
-
|
74
70
|
opts.on("-P", "--pid FILE", "file to store PID") { |f|
|
75
71
|
options[:pid] = ::File.expand_path(f)
|
76
72
|
}
|
data/lib/puma/reactor.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'puma/util'
|
4
|
-
require 'puma/minissl'
|
4
|
+
require 'puma/minissl' if ::Puma::HAS_SSL
|
5
5
|
|
6
6
|
require 'nio'
|
7
7
|
|
@@ -189,7 +189,12 @@ module Puma
|
|
189
189
|
if submon.value == @ready
|
190
190
|
false
|
191
191
|
else
|
192
|
-
submon.value.
|
192
|
+
if submon.value.can_close?
|
193
|
+
submon.value.close
|
194
|
+
else
|
195
|
+
# Pass remaining open client connections to the thread pool.
|
196
|
+
@app_pool << submon.value
|
197
|
+
end
|
193
198
|
begin
|
194
199
|
selector.deregister submon.value
|
195
200
|
rescue IOError
|
@@ -232,23 +237,12 @@ module Puma
|
|
232
237
|
|
233
238
|
# SSL handshake failure
|
234
239
|
rescue MiniSSL::SSLError => e
|
235
|
-
@server.lowlevel_error
|
236
|
-
|
237
|
-
ssl_socket = c.io
|
238
|
-
begin
|
239
|
-
addr = ssl_socket.peeraddr.last
|
240
|
-
# EINVAL can happen when browser closes socket w/security exception
|
241
|
-
rescue IOError, Errno::EINVAL
|
242
|
-
addr = "<unknown>"
|
243
|
-
end
|
244
|
-
|
245
|
-
cert = ssl_socket.peercert
|
240
|
+
@server.lowlevel_error e, c.env
|
241
|
+
@events.ssl_error e, c.io
|
246
242
|
|
247
243
|
c.close
|
248
244
|
clear_monitor mon
|
249
245
|
|
250
|
-
@events.ssl_error @server, addr, cert, e
|
251
|
-
|
252
246
|
# The client doesn't know HTTP well
|
253
247
|
rescue HttpParserError => e
|
254
248
|
@server.lowlevel_error(e, c.env)
|
@@ -258,7 +252,7 @@ module Puma
|
|
258
252
|
|
259
253
|
clear_monitor mon
|
260
254
|
|
261
|
-
@events.parse_error
|
255
|
+
@events.parse_error e, c
|
262
256
|
rescue StandardError => e
|
263
257
|
@server.lowlevel_error(e, c.env)
|
264
258
|
|
data/lib/puma/runner.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
require 'puma/server'
|
4
4
|
require 'puma/const'
|
5
|
-
require 'puma/minissl/context_builder'
|
6
5
|
|
7
6
|
module Puma
|
8
7
|
# Generic class that is used by `Puma::Cluster` and `Puma::Single` to
|
@@ -18,10 +17,6 @@ module Puma
|
|
18
17
|
@started_at = Time.now
|
19
18
|
end
|
20
19
|
|
21
|
-
def daemon?
|
22
|
-
@options[:daemon]
|
23
|
-
end
|
24
|
-
|
25
20
|
def development?
|
26
21
|
@options[:environment] == "development"
|
27
22
|
end
|
@@ -34,7 +29,8 @@ module Puma
|
|
34
29
|
@events.log str
|
35
30
|
end
|
36
31
|
|
37
|
-
|
32
|
+
# @version 5.0.0
|
33
|
+
def stop_control
|
38
34
|
@control.stop(true) if @control
|
39
35
|
end
|
40
36
|
|
@@ -52,8 +48,6 @@ module Puma
|
|
52
48
|
|
53
49
|
require 'puma/app/status'
|
54
50
|
|
55
|
-
uri = URI.parse str
|
56
|
-
|
57
51
|
if token = @options[:control_auth_token]
|
58
52
|
token = nil if token.empty? || token == 'none'
|
59
53
|
end
|
@@ -64,30 +58,17 @@ module Puma
|
|
64
58
|
control.min_threads = 0
|
65
59
|
control.max_threads = 1
|
66
60
|
|
67
|
-
|
68
|
-
when "ssl"
|
69
|
-
log "* Starting control server on #{str}"
|
70
|
-
params = Util.parse_query uri.query
|
71
|
-
ctx = MiniSSL::ContextBuilder.new(params, @events).context
|
72
|
-
|
73
|
-
control.add_ssl_listener uri.host, uri.port, ctx
|
74
|
-
when "tcp"
|
75
|
-
log "* Starting control server on #{str}"
|
76
|
-
control.add_tcp_listener uri.host, uri.port
|
77
|
-
when "unix"
|
78
|
-
log "* Starting control server on #{str}"
|
79
|
-
path = "#{uri.host}#{uri.path}"
|
80
|
-
mask = @options[:control_url_umask]
|
81
|
-
|
82
|
-
control.add_unix_listener path, mask
|
83
|
-
else
|
84
|
-
error "Invalid control URI: #{str}"
|
85
|
-
end
|
61
|
+
control.binder.parse [str], self, 'Starting control server'
|
86
62
|
|
87
63
|
control.run
|
88
64
|
@control = control
|
89
65
|
end
|
90
66
|
|
67
|
+
# @version 5.0.0
|
68
|
+
def close_control_listeners
|
69
|
+
@control.binder.close_listeners if @control
|
70
|
+
end
|
71
|
+
|
91
72
|
def ruby_engine
|
92
73
|
if !defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby"
|
93
74
|
"ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}"
|
@@ -108,10 +89,6 @@ module Puma
|
|
108
89
|
log "* Version #{Puma::Const::PUMA_VERSION} (#{ruby_engine}), codename: #{Puma::Const::CODE_NAME}"
|
109
90
|
log "* Min threads: #{min_t}, max threads: #{max_t}"
|
110
91
|
log "* Environment: #{ENV['RACK_ENV']}"
|
111
|
-
|
112
|
-
if @options[:mode] == :tcp
|
113
|
-
log "* Mode: Lopez Express (tcp)"
|
114
|
-
end
|
115
92
|
end
|
116
93
|
|
117
94
|
def redirected_io?
|
@@ -150,7 +127,6 @@ module Puma
|
|
150
127
|
exit 1
|
151
128
|
end
|
152
129
|
|
153
|
-
# Load the app before we daemonize.
|
154
130
|
begin
|
155
131
|
@app = @launcher.config.app
|
156
132
|
rescue Exception => e
|
@@ -174,10 +150,6 @@ module Puma
|
|
174
150
|
server.max_threads = max_t
|
175
151
|
server.inherit_binder @launcher.binder
|
176
152
|
|
177
|
-
if @options[:mode] == :tcp
|
178
|
-
server.tcp_mode!
|
179
|
-
end
|
180
|
-
|
181
153
|
if @options[:early_hints]
|
182
154
|
server.early_hints = true
|
183
155
|
end
|
data/lib/puma/server.rb
CHANGED
@@ -9,10 +9,8 @@ require 'puma/null_io'
|
|
9
9
|
require 'puma/reactor'
|
10
10
|
require 'puma/client'
|
11
11
|
require 'puma/binder'
|
12
|
-
require 'puma/accept_nonblock'
|
13
12
|
require 'puma/util'
|
14
|
-
|
15
|
-
require 'puma/puma_http11'
|
13
|
+
require 'puma/io_buffer'
|
16
14
|
|
17
15
|
require 'socket'
|
18
16
|
require 'forwardable'
|
@@ -36,6 +34,7 @@ module Puma
|
|
36
34
|
|
37
35
|
attr_reader :thread
|
38
36
|
attr_reader :events
|
37
|
+
attr_reader :requests_count # @version 5.0.0
|
39
38
|
attr_accessor :app
|
40
39
|
|
41
40
|
attr_accessor :min_threads
|
@@ -57,8 +56,7 @@ module Puma
|
|
57
56
|
@app = app
|
58
57
|
@events = events
|
59
58
|
|
60
|
-
@check, @notify =
|
61
|
-
|
59
|
+
@check, @notify = nil
|
62
60
|
@status = :stop
|
63
61
|
|
64
62
|
@min_threads = 0
|
@@ -85,24 +83,44 @@ module Puma
|
|
85
83
|
@mode = :http
|
86
84
|
|
87
85
|
@precheck_closing = true
|
86
|
+
|
87
|
+
@requests_count = 0
|
88
|
+
|
89
|
+
@shutdown_mutex = Mutex.new
|
88
90
|
end
|
89
91
|
|
90
92
|
attr_accessor :binder, :leak_stack_on_error, :early_hints
|
91
93
|
|
92
|
-
def_delegators :@binder, :add_tcp_listener, :add_ssl_listener, :add_unix_listener, :
|
94
|
+
def_delegators :@binder, :add_tcp_listener, :add_ssl_listener, :add_unix_listener, :connected_ports
|
93
95
|
|
94
96
|
def inherit_binder(bind)
|
95
97
|
@binder = bind
|
96
98
|
end
|
97
99
|
|
98
|
-
|
99
|
-
|
100
|
+
class << self
|
101
|
+
# :nodoc:
|
102
|
+
# @version 5.0.0
|
103
|
+
def tcp_cork_supported?
|
104
|
+
RbConfig::CONFIG['host_os'] =~ /linux/ &&
|
105
|
+
Socket.const_defined?(:IPPROTO_TCP) &&
|
106
|
+
Socket.const_defined?(:TCP_CORK)
|
107
|
+
end
|
108
|
+
|
109
|
+
# :nodoc:
|
110
|
+
# @version 5.0.0
|
111
|
+
def closed_socket_supported?
|
112
|
+
RbConfig::CONFIG['host_os'] =~ /linux/ &&
|
113
|
+
Socket.const_defined?(:IPPROTO_TCP) &&
|
114
|
+
Socket.const_defined?(:TCP_INFO)
|
115
|
+
end
|
116
|
+
private :tcp_cork_supported?
|
117
|
+
private :closed_socket_supported?
|
100
118
|
end
|
101
119
|
|
102
120
|
# On Linux, use TCP_CORK to better control how the TCP stack
|
103
121
|
# packetizes our stream. This improves both latency and throughput.
|
104
122
|
#
|
105
|
-
if
|
123
|
+
if tcp_cork_supported?
|
106
124
|
UNPACK_TCP_STATE_FROM_TCP_INFO = "C".freeze
|
107
125
|
|
108
126
|
# 6 == Socket::IPPROTO_TCP
|
@@ -110,7 +128,7 @@ module Puma
|
|
110
128
|
# 1/0 == turn on/off
|
111
129
|
def cork_socket(socket)
|
112
130
|
begin
|
113
|
-
socket.setsockopt(
|
131
|
+
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 1) if socket.kind_of? TCPSocket
|
114
132
|
rescue IOError, SystemCallError
|
115
133
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
116
134
|
end
|
@@ -118,18 +136,26 @@ module Puma
|
|
118
136
|
|
119
137
|
def uncork_socket(socket)
|
120
138
|
begin
|
121
|
-
socket.setsockopt(
|
139
|
+
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 0) if socket.kind_of? TCPSocket
|
122
140
|
rescue IOError, SystemCallError
|
123
141
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
124
142
|
end
|
125
143
|
end
|
144
|
+
else
|
145
|
+
def cork_socket(socket)
|
146
|
+
end
|
126
147
|
|
148
|
+
def uncork_socket(socket)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
if closed_socket_supported?
|
127
153
|
def closed_socket?(socket)
|
128
154
|
return false unless socket.kind_of? TCPSocket
|
129
155
|
return false unless @precheck_closing
|
130
156
|
|
131
157
|
begin
|
132
|
-
tcp_info = socket.getsockopt(Socket::
|
158
|
+
tcp_info = socket.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_INFO)
|
133
159
|
rescue IOError, SystemCallError
|
134
160
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
135
161
|
@precheck_closing = false
|
@@ -141,12 +167,6 @@ module Puma
|
|
141
167
|
end
|
142
168
|
end
|
143
169
|
else
|
144
|
-
def cork_socket(socket)
|
145
|
-
end
|
146
|
-
|
147
|
-
def uncork_socket(socket)
|
148
|
-
end
|
149
|
-
|
150
170
|
def closed_socket?(socket)
|
151
171
|
false
|
152
172
|
end
|
@@ -172,107 +192,6 @@ module Puma
|
|
172
192
|
@thread_pool and @thread_pool.pool_capacity
|
173
193
|
end
|
174
194
|
|
175
|
-
# Lopez Mode == raw tcp apps
|
176
|
-
|
177
|
-
def run_lopez_mode(background=true)
|
178
|
-
@thread_pool = ThreadPool.new(@min_threads,
|
179
|
-
@max_threads,
|
180
|
-
Hash) do |client, tl|
|
181
|
-
|
182
|
-
io = client.to_io
|
183
|
-
addr = io.peeraddr.last
|
184
|
-
|
185
|
-
if addr.empty?
|
186
|
-
# Set unix socket addrs to localhost
|
187
|
-
addr = "127.0.0.1:0"
|
188
|
-
else
|
189
|
-
addr = "#{addr}:#{io.peeraddr[1]}"
|
190
|
-
end
|
191
|
-
|
192
|
-
env = { 'thread' => tl, REMOTE_ADDR => addr }
|
193
|
-
|
194
|
-
begin
|
195
|
-
@app.call env, client.to_io
|
196
|
-
rescue Object => e
|
197
|
-
STDERR.puts "! Detected exception at toplevel: #{e.message} (#{e.class})"
|
198
|
-
STDERR.puts e.backtrace
|
199
|
-
end
|
200
|
-
|
201
|
-
client.close unless env['detach']
|
202
|
-
end
|
203
|
-
|
204
|
-
@events.fire :state, :running
|
205
|
-
|
206
|
-
if background
|
207
|
-
@thread = Thread.new do
|
208
|
-
Puma.set_thread_name "server"
|
209
|
-
handle_servers_lopez_mode
|
210
|
-
end
|
211
|
-
return @thread
|
212
|
-
else
|
213
|
-
handle_servers_lopez_mode
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
def handle_servers_lopez_mode
|
218
|
-
begin
|
219
|
-
check = @check
|
220
|
-
sockets = [check] + @binder.ios
|
221
|
-
pool = @thread_pool
|
222
|
-
|
223
|
-
while @status == :run
|
224
|
-
begin
|
225
|
-
ios = IO.select sockets
|
226
|
-
ios.first.each do |sock|
|
227
|
-
if sock == check
|
228
|
-
break if handle_check
|
229
|
-
else
|
230
|
-
begin
|
231
|
-
if io = sock.accept_nonblock
|
232
|
-
client = Client.new io, nil
|
233
|
-
pool << client
|
234
|
-
end
|
235
|
-
rescue SystemCallError
|
236
|
-
# nothing
|
237
|
-
rescue Errno::ECONNABORTED
|
238
|
-
# client closed the socket even before accept
|
239
|
-
begin
|
240
|
-
io.close
|
241
|
-
rescue
|
242
|
-
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
243
|
-
end
|
244
|
-
end
|
245
|
-
end
|
246
|
-
end
|
247
|
-
rescue Object => e
|
248
|
-
@events.unknown_error self, e, "Listen loop"
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
|
-
@events.fire :state, @status
|
253
|
-
|
254
|
-
graceful_shutdown if @status == :stop || @status == :restart
|
255
|
-
|
256
|
-
rescue Exception => e
|
257
|
-
STDERR.puts "Exception handling servers: #{e.message} (#{e.class})"
|
258
|
-
STDERR.puts e.backtrace
|
259
|
-
ensure
|
260
|
-
begin
|
261
|
-
@check.close
|
262
|
-
rescue
|
263
|
-
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
264
|
-
end
|
265
|
-
|
266
|
-
# Prevent can't modify frozen IOError (RuntimeError)
|
267
|
-
begin
|
268
|
-
@notify.close
|
269
|
-
rescue IOError
|
270
|
-
# no biggy
|
271
|
-
end
|
272
|
-
end
|
273
|
-
|
274
|
-
@events.fire :state, :done
|
275
|
-
end
|
276
195
|
# Runs the server.
|
277
196
|
#
|
278
197
|
# If +background+ is true (the default) then a thread is spun
|
@@ -286,15 +205,9 @@ module Puma
|
|
286
205
|
|
287
206
|
@status = :run
|
288
207
|
|
289
|
-
if @mode == :tcp
|
290
|
-
return run_lopez_mode(background)
|
291
|
-
end
|
292
|
-
|
293
|
-
queue_requests = @queue_requests
|
294
|
-
|
295
208
|
@thread_pool = ThreadPool.new(@min_threads,
|
296
209
|
@max_threads,
|
297
|
-
IOBuffer) do |client, buffer|
|
210
|
+
::Puma::IOBuffer) do |client, buffer|
|
298
211
|
|
299
212
|
# Advertise this server into the thread
|
300
213
|
Thread.current[ThreadLocalKey] = self
|
@@ -302,40 +215,48 @@ module Puma
|
|
302
215
|
process_now = false
|
303
216
|
|
304
217
|
begin
|
305
|
-
if queue_requests
|
218
|
+
if @queue_requests
|
306
219
|
process_now = client.eagerly_finish
|
307
220
|
else
|
308
|
-
|
221
|
+
@thread_pool.with_force_shutdown do
|
222
|
+
client.finish(@first_data_timeout)
|
223
|
+
end
|
309
224
|
process_now = true
|
310
225
|
end
|
311
226
|
rescue MiniSSL::SSLError => e
|
312
|
-
|
313
|
-
addr = ssl_socket.peeraddr.last
|
314
|
-
cert = ssl_socket.peercert
|
315
|
-
|
227
|
+
@events.ssl_error e, client.io
|
316
228
|
client.close
|
317
229
|
|
318
|
-
@events.ssl_error self, addr, cert, e
|
319
230
|
rescue HttpParserError => e
|
320
231
|
client.write_error(400)
|
321
232
|
client.close
|
322
233
|
|
323
|
-
@events.parse_error
|
324
|
-
rescue
|
234
|
+
@events.parse_error e, client
|
235
|
+
rescue EOFError => e
|
325
236
|
client.close
|
237
|
+
|
238
|
+
# Swallow, do not log
|
239
|
+
rescue ConnectionError, ThreadPool::ForceShutdown => e
|
240
|
+
client.close
|
241
|
+
|
242
|
+
@events.connection_error e, client
|
326
243
|
else
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
244
|
+
process_now ||= @shutdown_mutex.synchronize do
|
245
|
+
next true unless @queue_requests
|
246
|
+
client.set_timeout @first_data_timeout
|
247
|
+
@reactor.add client
|
248
|
+
false
|
249
|
+
end
|
250
|
+
process_client client, buffer if process_now
|
333
251
|
end
|
252
|
+
|
253
|
+
process_now
|
334
254
|
end
|
335
255
|
|
256
|
+
@thread_pool.out_of_band_hook = @options[:out_of_band]
|
336
257
|
@thread_pool.clean_thread_locals = @options[:clean_thread_locals]
|
337
258
|
|
338
|
-
if queue_requests
|
259
|
+
if @queue_requests
|
339
260
|
@reactor = Reactor.new self, @thread_pool
|
340
261
|
@reactor.run_in_thread
|
341
262
|
end
|
@@ -362,6 +283,7 @@ module Puma
|
|
362
283
|
end
|
363
284
|
|
364
285
|
def handle_servers
|
286
|
+
@check, @notify = Puma::Util.pipe unless @notify
|
365
287
|
begin
|
366
288
|
check = @check
|
367
289
|
sockets = [check] + @binder.ios
|
@@ -385,51 +307,51 @@ module Puma
|
|
385
307
|
if sock == check
|
386
308
|
break if handle_check
|
387
309
|
else
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
end
|
403
|
-
rescue SystemCallError
|
404
|
-
# nothing
|
405
|
-
rescue Errno::ECONNABORTED
|
406
|
-
# client closed the socket even before accept
|
407
|
-
begin
|
408
|
-
io.close
|
409
|
-
rescue
|
410
|
-
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
411
|
-
end
|
310
|
+
pool.wait_until_not_full
|
311
|
+
pool.wait_for_less_busy_worker(
|
312
|
+
@options[:wait_for_less_busy_worker].to_f)
|
313
|
+
|
314
|
+
io = begin
|
315
|
+
sock.accept_nonblock
|
316
|
+
rescue IO::WaitReadable
|
317
|
+
next
|
318
|
+
end
|
319
|
+
client = Client.new io, @binder.env(sock)
|
320
|
+
if remote_addr_value
|
321
|
+
client.peerip = remote_addr_value
|
322
|
+
elsif remote_addr_header
|
323
|
+
client.remote_addr_header = remote_addr_header
|
412
324
|
end
|
325
|
+
pool << client
|
413
326
|
end
|
414
327
|
end
|
415
328
|
rescue Object => e
|
416
|
-
@events.unknown_error
|
329
|
+
@events.unknown_error e, nil, "Listen loop"
|
417
330
|
end
|
418
331
|
end
|
419
332
|
|
420
333
|
@events.fire :state, @status
|
421
334
|
|
422
|
-
graceful_shutdown if @status == :stop || @status == :restart
|
423
335
|
if queue_requests
|
336
|
+
@shutdown_mutex.synchronize do
|
337
|
+
@queue_requests = false
|
338
|
+
end
|
424
339
|
@reactor.clear!
|
425
340
|
@reactor.shutdown
|
426
341
|
end
|
342
|
+
graceful_shutdown if @status == :stop || @status == :restart
|
427
343
|
rescue Exception => e
|
428
|
-
|
429
|
-
STDERR.puts e.backtrace
|
344
|
+
@events.unknown_error e, nil, "Exception handling servers"
|
430
345
|
ensure
|
431
|
-
|
346
|
+
begin
|
347
|
+
@check.close unless @check.closed?
|
348
|
+
rescue Errno::EBADF, RuntimeError
|
349
|
+
# RuntimeError is Ruby 2.2 issue, can't modify frozen IOError
|
350
|
+
# Errno::EBADF is infrequently raised
|
351
|
+
end
|
432
352
|
@notify.close
|
353
|
+
@notify = nil
|
354
|
+
@check = nil
|
433
355
|
end
|
434
356
|
|
435
357
|
@events.fire :state, :done
|
@@ -476,7 +398,6 @@ module Puma
|
|
476
398
|
close_socket = false
|
477
399
|
return
|
478
400
|
when true
|
479
|
-
return unless @queue_requests
|
480
401
|
buffer.reset
|
481
402
|
|
482
403
|
ThreadPool.clean_thread_locals if clean_thread_locals
|
@@ -493,38 +414,39 @@ module Puma
|
|
493
414
|
check_for_more_data = false
|
494
415
|
end
|
495
416
|
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
417
|
+
next_request_ready = @thread_pool.with_force_shutdown do
|
418
|
+
client.reset(check_for_more_data)
|
419
|
+
end
|
420
|
+
|
421
|
+
unless next_request_ready
|
422
|
+
@shutdown_mutex.synchronize do
|
423
|
+
return unless @queue_requests
|
424
|
+
close_socket = false
|
425
|
+
client.set_timeout @persistent_timeout
|
426
|
+
@reactor.add client
|
427
|
+
return
|
428
|
+
end
|
501
429
|
end
|
502
430
|
end
|
503
431
|
end
|
504
432
|
|
505
433
|
# The client disconnected while we were reading data
|
506
|
-
rescue ConnectionError
|
434
|
+
rescue ConnectionError, ThreadPool::ForceShutdown
|
507
435
|
# Swallow them. The ensure tries to close +client+ down
|
508
436
|
|
509
437
|
# SSL handshake error
|
510
438
|
rescue MiniSSL::SSLError => e
|
511
|
-
lowlevel_error
|
512
|
-
|
513
|
-
ssl_socket = client.io
|
514
|
-
addr = ssl_socket.peeraddr.last
|
515
|
-
cert = ssl_socket.peercert
|
516
|
-
|
439
|
+
lowlevel_error e, client.env
|
440
|
+
@events.ssl_error e, client.io
|
517
441
|
close_socket = true
|
518
442
|
|
519
|
-
@events.ssl_error self, addr, cert, e
|
520
|
-
|
521
443
|
# The client doesn't know HTTP well
|
522
444
|
rescue HttpParserError => e
|
523
445
|
lowlevel_error(e, client.env)
|
524
446
|
|
525
447
|
client.write_error(400)
|
526
448
|
|
527
|
-
@events.parse_error
|
449
|
+
@events.parse_error e, client
|
528
450
|
|
529
451
|
# Server error
|
530
452
|
rescue StandardError => e
|
@@ -532,8 +454,7 @@ module Puma
|
|
532
454
|
|
533
455
|
client.write_error(500)
|
534
456
|
|
535
|
-
@events.unknown_error
|
536
|
-
|
457
|
+
@events.unknown_error e, nil, "Read"
|
537
458
|
ensure
|
538
459
|
buffer.reset
|
539
460
|
|
@@ -543,7 +464,7 @@ module Puma
|
|
543
464
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
544
465
|
# Already closed
|
545
466
|
rescue StandardError => e
|
546
|
-
@events.unknown_error
|
467
|
+
@events.unknown_error e, nil, "Client"
|
547
468
|
end
|
548
469
|
end
|
549
470
|
end
|
@@ -579,7 +500,7 @@ module Puma
|
|
579
500
|
|
580
501
|
env[PATH_INFO] = env[REQUEST_PATH]
|
581
502
|
|
582
|
-
# From
|
503
|
+
# From https://www.ietf.org/rfc/rfc3875 :
|
583
504
|
# "Script authors should be aware that the REMOTE_ADDR and
|
584
505
|
# REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
|
585
506
|
# may not identify the ultimate source of the request.
|
@@ -626,6 +547,8 @@ module Puma
|
|
626
547
|
#
|
627
548
|
# Finally, it'll return +true+ on keep-alive connections.
|
628
549
|
def handle_request(req, lines)
|
550
|
+
@requests_count +=1
|
551
|
+
|
629
552
|
env = req.env
|
630
553
|
client = req.io
|
631
554
|
|
@@ -666,7 +589,8 @@ module Puma
|
|
666
589
|
end
|
667
590
|
|
668
591
|
fast_write client, "\r\n".freeze
|
669
|
-
rescue ConnectionError
|
592
|
+
rescue ConnectionError => e
|
593
|
+
@events.debug_error e
|
670
594
|
# noop, if we lost the socket we just won't send the early hints
|
671
595
|
end
|
672
596
|
}
|
@@ -694,7 +618,7 @@ module Puma
|
|
694
618
|
to_add = {}
|
695
619
|
end
|
696
620
|
|
697
|
-
to_add[k.
|
621
|
+
to_add[k.tr(",", "_")] = v
|
698
622
|
end
|
699
623
|
end
|
700
624
|
|
@@ -710,7 +634,9 @@ module Puma
|
|
710
634
|
|
711
635
|
begin
|
712
636
|
begin
|
713
|
-
status, headers, res_body = @
|
637
|
+
status, headers, res_body = @thread_pool.with_force_shutdown do
|
638
|
+
@app.call(env)
|
639
|
+
end
|
714
640
|
|
715
641
|
return :async if req.hijacked
|
716
642
|
|
@@ -724,17 +650,14 @@ module Puma
|
|
724
650
|
return :async
|
725
651
|
end
|
726
652
|
rescue ThreadPool::ForceShutdown => e
|
727
|
-
@events.
|
728
|
-
@events.
|
729
|
-
|
730
|
-
status = 503
|
731
|
-
headers = {}
|
732
|
-
res_body = ["Request was internally terminated early\n"]
|
653
|
+
@events.unknown_error e, req, "Rack app"
|
654
|
+
@events.log "Detected force shutdown of a thread"
|
733
655
|
|
656
|
+
status, headers, res_body = lowlevel_error(e, env, 503)
|
734
657
|
rescue Exception => e
|
735
|
-
@events.unknown_error
|
658
|
+
@events.unknown_error e, req, "Rack app"
|
736
659
|
|
737
|
-
status, headers, res_body = lowlevel_error(e, env)
|
660
|
+
status, headers, res_body = lowlevel_error(e, env, 500)
|
738
661
|
end
|
739
662
|
|
740
663
|
content_length = nil
|
@@ -749,10 +672,10 @@ module Puma
|
|
749
672
|
line_ending = LINE_END
|
750
673
|
colon = COLON
|
751
674
|
|
752
|
-
http_11 =
|
675
|
+
http_11 = env[HTTP_VERSION] == HTTP_11
|
676
|
+
if http_11
|
753
677
|
allow_chunked = true
|
754
678
|
keep_alive = env.fetch(HTTP_CONNECTION, "").downcase != CLOSE
|
755
|
-
include_keepalive_header = false
|
756
679
|
|
757
680
|
# An optimization. The most common response is 200, so we can
|
758
681
|
# reply with the proper 200 status without having to compute
|
@@ -766,11 +689,9 @@ module Puma
|
|
766
689
|
|
767
690
|
no_body ||= status < 200 || STATUS_WITH_NO_ENTITY_BODY[status]
|
768
691
|
end
|
769
|
-
true
|
770
692
|
else
|
771
693
|
allow_chunked = false
|
772
694
|
keep_alive = env.fetch(HTTP_CONNECTION, "").downcase == KEEP_ALIVE
|
773
|
-
include_keepalive_header = keep_alive
|
774
695
|
|
775
696
|
# Same optimization as above for HTTP/1.1
|
776
697
|
#
|
@@ -782,9 +703,12 @@ module Puma
|
|
782
703
|
|
783
704
|
no_body ||= status < 200 || STATUS_WITH_NO_ENTITY_BODY[status]
|
784
705
|
end
|
785
|
-
false
|
786
706
|
end
|
787
707
|
|
708
|
+
# regardless of what the client wants, we always close the connection
|
709
|
+
# if running without request queueing
|
710
|
+
keep_alive &&= @queue_requests
|
711
|
+
|
788
712
|
response_hijack = nil
|
789
713
|
|
790
714
|
headers.each do |k, vs|
|
@@ -811,10 +735,15 @@ module Puma
|
|
811
735
|
end
|
812
736
|
end
|
813
737
|
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
738
|
+
# HTTP/1.1 & 1.0 assume different defaults:
|
739
|
+
# - HTTP 1.0 assumes the connection will be closed if not specified
|
740
|
+
# - HTTP 1.1 assumes the connection will be kept alive if not specified.
|
741
|
+
# Only set the header if we're doing something which is not the default
|
742
|
+
# for this protocol version
|
743
|
+
if http_11
|
744
|
+
lines << CONNECTION_CLOSE if !keep_alive
|
745
|
+
else
|
746
|
+
lines << CONNECTION_KEEP_ALIVE if keep_alive
|
818
747
|
end
|
819
748
|
|
820
749
|
if no_body
|
@@ -941,19 +870,21 @@ module Puma
|
|
941
870
|
|
942
871
|
# A fallback rack response if +@app+ raises as exception.
|
943
872
|
#
|
944
|
-
def lowlevel_error(e, env)
|
873
|
+
def lowlevel_error(e, env, status=500)
|
945
874
|
if handler = @options[:lowlevel_error_handler]
|
946
875
|
if handler.arity == 1
|
947
876
|
return handler.call(e)
|
948
|
-
|
877
|
+
elsif handler.arity == 2
|
949
878
|
return handler.call(e, env)
|
879
|
+
else
|
880
|
+
return handler.call(e, env, status)
|
950
881
|
end
|
951
882
|
end
|
952
883
|
|
953
884
|
if @leak_stack_on_error
|
954
|
-
[
|
885
|
+
[status, {}, ["Puma caught this error: #{e.message} (#{e.class})\n#{e.backtrace.join("\n")}"]]
|
955
886
|
else
|
956
|
-
[
|
887
|
+
[status, {}, ["An unhandled lowlevel error occurred. The application logs may have details.\n"]]
|
957
888
|
end
|
958
889
|
end
|
959
890
|
|
@@ -1003,7 +934,7 @@ module Puma
|
|
1003
934
|
|
1004
935
|
if @thread_pool
|
1005
936
|
if timeout = @options[:force_shutdown_after]
|
1006
|
-
@thread_pool.shutdown timeout.
|
937
|
+
@thread_pool.shutdown timeout.to_f
|
1007
938
|
else
|
1008
939
|
@thread_pool.shutdown
|
1009
940
|
end
|
@@ -1011,9 +942,10 @@ module Puma
|
|
1011
942
|
end
|
1012
943
|
|
1013
944
|
def notify_safely(message)
|
945
|
+
@check, @notify = Puma::Util.pipe unless @notify
|
1014
946
|
begin
|
1015
947
|
@notify << message
|
1016
|
-
rescue IOError
|
948
|
+
rescue IOError, NoMethodError, Errno::EPIPE
|
1017
949
|
# The server, in another thread, is shutting down
|
1018
950
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
1019
951
|
rescue RuntimeError => e
|
@@ -1040,8 +972,9 @@ module Puma
|
|
1040
972
|
@thread.join if @thread && sync
|
1041
973
|
end
|
1042
974
|
|
1043
|
-
def begin_restart
|
975
|
+
def begin_restart(sync=false)
|
1044
976
|
notify_safely(RESTART_COMMAND)
|
977
|
+
@thread.join if @thread && sync
|
1045
978
|
end
|
1046
979
|
|
1047
980
|
def fast_write(io, str)
|
@@ -1079,5 +1012,15 @@ module Puma
|
|
1079
1012
|
HTTP_INJECTION_REGEX =~ header_value.to_s
|
1080
1013
|
end
|
1081
1014
|
private :possible_header_injection?
|
1015
|
+
|
1016
|
+
# List of methods invoked by #stats.
|
1017
|
+
# @version 5.0.0
|
1018
|
+
STAT_METHODS = [:backlog, :running, :pool_capacity, :max_threads, :requests_count].freeze
|
1019
|
+
|
1020
|
+
# Returns a hash of stats about the running server for reporting purposes.
|
1021
|
+
# @version 5.0.0
|
1022
|
+
def stats
|
1023
|
+
STAT_METHODS.map {|name| [name, send(name) || 0]}.to_h
|
1024
|
+
end
|
1082
1025
|
end
|
1083
1026
|
end
|