puma 5.0.0 → 5.0.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/History.md +1131 -574
- data/lib/puma.rb +5 -2
- data/lib/puma/binder.rb +2 -0
- data/lib/puma/client.rb +5 -1
- data/lib/puma/cluster.rb +2 -0
- data/lib/puma/const.rb +1 -1
- data/lib/puma/error_logger.rb +2 -2
- data/lib/puma/events.rb +5 -3
- data/lib/puma/launcher.rb +8 -0
- data/lib/puma/minissl.rb +8 -0
- data/lib/puma/reactor.rb +2 -13
- data/lib/puma/server.rb +57 -38
- data/lib/puma/single.rb +1 -0
- data/lib/puma/thread_pool.rb +22 -2
- data/lib/puma/util.rb +1 -0
- metadata +6 -6
data/lib/puma.rb
CHANGED
@@ -10,23 +10,26 @@ require 'stringio'
|
|
10
10
|
|
11
11
|
require 'thread'
|
12
12
|
|
13
|
-
|
14
|
-
|
13
|
+
require 'puma/puma_http11'
|
14
|
+
require 'puma/detect'
|
15
15
|
|
16
16
|
module Puma
|
17
17
|
autoload :Const, 'puma/const'
|
18
18
|
autoload :Server, 'puma/server'
|
19
19
|
autoload :Launcher, 'puma/launcher'
|
20
20
|
|
21
|
+
# @!attribute [rw] stats_object=
|
21
22
|
def self.stats_object=(val)
|
22
23
|
@get_stats = val
|
23
24
|
end
|
24
25
|
|
26
|
+
# @!attribute [rw] stats_object
|
25
27
|
def self.stats
|
26
28
|
require 'json'
|
27
29
|
@get_stats.stats.to_json
|
28
30
|
end
|
29
31
|
|
32
|
+
# @!attribute [r] stats_hash
|
30
33
|
# @version 5.0.0
|
31
34
|
def self.stats_hash
|
32
35
|
@get_stats.stats
|
data/lib/puma/binder.rb
CHANGED
@@ -66,6 +66,7 @@ module Puma
|
|
66
66
|
@ios.each { |i| i.close }
|
67
67
|
end
|
68
68
|
|
69
|
+
# @!attribute [r] connected_ports
|
69
70
|
# @version 5.0.0
|
70
71
|
def connected_ports
|
71
72
|
ios.map { |io| io.addr[1] }.uniq
|
@@ -391,6 +392,7 @@ module Puma
|
|
391
392
|
|
392
393
|
private
|
393
394
|
|
395
|
+
# @!attribute [r] loopback_addresses
|
394
396
|
def loopback_addresses
|
395
397
|
Socket.ip_address_list.select do |addrinfo|
|
396
398
|
addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
|
data/lib/puma/client.rb
CHANGED
@@ -85,6 +85,7 @@ module Puma
|
|
85
85
|
|
86
86
|
def_delegators :@io, :closed?
|
87
87
|
|
88
|
+
# @!attribute [r] inspect
|
88
89
|
def inspect
|
89
90
|
"#<Puma::Client:0x#{object_id.to_s(16)} @ready=#{@ready.inspect}>"
|
90
91
|
end
|
@@ -96,6 +97,7 @@ module Puma
|
|
96
97
|
env[HIJACK_IO] ||= @io
|
97
98
|
end
|
98
99
|
|
100
|
+
# @!attribute [r] in_data_phase
|
99
101
|
def in_data_phase
|
100
102
|
!@read_header
|
101
103
|
end
|
@@ -155,7 +157,9 @@ module Puma
|
|
155
157
|
data = @io.read_nonblock(CHUNK_SIZE)
|
156
158
|
rescue IO::WaitReadable
|
157
159
|
return false
|
158
|
-
rescue
|
160
|
+
rescue EOFError
|
161
|
+
# Swallow error, don't log
|
162
|
+
rescue SystemCallError, IOError
|
159
163
|
raise ConnectionError, "Connection error detected during read"
|
160
164
|
end
|
161
165
|
|
data/lib/puma/cluster.rb
CHANGED
@@ -194,6 +194,7 @@ module Puma
|
|
194
194
|
end
|
195
195
|
end
|
196
196
|
|
197
|
+
# @!attribute [r] next_worker_index
|
197
198
|
def next_worker_index
|
198
199
|
all_positions = 0...@options[:workers]
|
199
200
|
occupied_positions = @workers.map { |w| w.index }
|
@@ -396,6 +397,7 @@ module Puma
|
|
396
397
|
|
397
398
|
# Inside of a child process, this will return all zeroes, as @workers is only populated in
|
398
399
|
# the master process.
|
400
|
+
# @!attribute [r] stats
|
399
401
|
def stats
|
400
402
|
old_worker_count = @workers.count { |w| w.phase != @phase }
|
401
403
|
worker_status = @workers.map do |w|
|
data/lib/puma/const.rb
CHANGED
@@ -100,7 +100,7 @@ module Puma
|
|
100
100
|
# too taxing on performance.
|
101
101
|
module Const
|
102
102
|
|
103
|
-
PUMA_VERSION = VERSION = "5.0.
|
103
|
+
PUMA_VERSION = VERSION = "5.0.2".freeze
|
104
104
|
CODE_NAME = "Spoony Bard".freeze
|
105
105
|
|
106
106
|
PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
|
data/lib/puma/error_logger.rb
CHANGED
@@ -51,8 +51,8 @@ module Puma
|
|
51
51
|
|
52
52
|
string_block = []
|
53
53
|
string_block << title(options)
|
54
|
-
string_block << request_dump(req) if req
|
55
|
-
string_block <<
|
54
|
+
string_block << request_dump(req) if request_parsed?(req)
|
55
|
+
string_block << error.backtrace if error
|
56
56
|
|
57
57
|
ioerr.puts string_block.join("\n")
|
58
58
|
end
|
data/lib/puma/events.rb
CHANGED
@@ -106,10 +106,12 @@ module Puma
|
|
106
106
|
end
|
107
107
|
|
108
108
|
# An SSL error has occurred.
|
109
|
-
#
|
110
|
-
#
|
109
|
+
# @param error <Puma::MiniSSL::SSLError>
|
110
|
+
# @param ssl_socket <Puma::MiniSSL::Socket>
|
111
111
|
#
|
112
|
-
def ssl_error(error,
|
112
|
+
def ssl_error(error, ssl_socket)
|
113
|
+
peeraddr = ssl_socket.peeraddr.last rescue "<unknown>"
|
114
|
+
peercert = ssl_socket.peercert
|
113
115
|
subject = peercert ? peercert.subject : nil
|
114
116
|
@error_logger.info(error: error, text: "SSL error, peer: #{peeraddr}, peer cert: #{subject}")
|
115
117
|
end
|
data/lib/puma/launcher.rb
CHANGED
@@ -188,11 +188,13 @@ module Puma
|
|
188
188
|
end
|
189
189
|
|
190
190
|
# Return all tcp ports the launcher may be using, TCP or SSL
|
191
|
+
# @!attribute [r] connected_ports
|
191
192
|
# @version 5.0.0
|
192
193
|
def connected_ports
|
193
194
|
@binder.connected_ports
|
194
195
|
end
|
195
196
|
|
197
|
+
# @!attribute [r] restart_args
|
196
198
|
def restart_args
|
197
199
|
cmd = @options[:restart_cmd]
|
198
200
|
if cmd
|
@@ -207,6 +209,7 @@ module Puma
|
|
207
209
|
@binder.close_listeners
|
208
210
|
end
|
209
211
|
|
212
|
+
# @!attribute [r] thread_status
|
210
213
|
# @version 5.0.0
|
211
214
|
def thread_status
|
212
215
|
Thread.list.each do |thread|
|
@@ -261,6 +264,7 @@ module Puma
|
|
261
264
|
end
|
262
265
|
end
|
263
266
|
|
267
|
+
# @!attribute [r] dependencies_and_files_to_require_after_prune
|
264
268
|
def dependencies_and_files_to_require_after_prune
|
265
269
|
puma = spec_for_gem("puma")
|
266
270
|
|
@@ -271,6 +275,7 @@ module Puma
|
|
271
275
|
[deps, require_paths_for_gem(puma) + extra_runtime_deps_directories]
|
272
276
|
end
|
273
277
|
|
278
|
+
# @!attribute [r] extra_runtime_deps_directories
|
274
279
|
def extra_runtime_deps_directories
|
275
280
|
Array(@options[:extra_runtime_dependencies]).map do |d_name|
|
276
281
|
if (spec = spec_for_gem(d_name))
|
@@ -282,6 +287,7 @@ module Puma
|
|
282
287
|
end.flatten.compact
|
283
288
|
end
|
284
289
|
|
290
|
+
# @!attribute [r] puma_wild_location
|
285
291
|
def puma_wild_location
|
286
292
|
puma = spec_for_gem("puma")
|
287
293
|
dirs = require_paths_for_gem(puma)
|
@@ -345,6 +351,7 @@ module Puma
|
|
345
351
|
Process.respond_to?(:setproctitle) ? Process.setproctitle(title) : $0 = title
|
346
352
|
end
|
347
353
|
|
354
|
+
# @!attribute [r] title
|
348
355
|
def title
|
349
356
|
buffer = "puma #{Puma::Const::VERSION} (#{@options[:binds].join(',')})"
|
350
357
|
buffer += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
|
@@ -356,6 +363,7 @@ module Puma
|
|
356
363
|
ENV['RACK_ENV'] = environment
|
357
364
|
end
|
358
365
|
|
366
|
+
# @!attribute [r] environment
|
359
367
|
def environment
|
360
368
|
@environment
|
361
369
|
end
|
data/lib/puma/minissl.rb
CHANGED
@@ -24,6 +24,7 @@ module Puma
|
|
24
24
|
@peercert = nil
|
25
25
|
end
|
26
26
|
|
27
|
+
# @!attribute [r] to_io
|
27
28
|
def to_io
|
28
29
|
@socket
|
29
30
|
end
|
@@ -38,6 +39,7 @@ module Puma
|
|
38
39
|
#
|
39
40
|
# Used for dropping tcp connections to ssl.
|
40
41
|
# See OpenSSL ssl/ssl_stat.c SSL_state_string for info
|
42
|
+
# @!attribute [r] ssl_version_state
|
41
43
|
# @version 5.0.0
|
42
44
|
#
|
43
45
|
def ssl_version_state
|
@@ -188,10 +190,12 @@ module Puma
|
|
188
190
|
end
|
189
191
|
end
|
190
192
|
|
193
|
+
# @!attribute [r] peeraddr
|
191
194
|
def peeraddr
|
192
195
|
@socket.peeraddr
|
193
196
|
end
|
194
197
|
|
198
|
+
# @!attribute [r] peercert
|
195
199
|
def peercert
|
196
200
|
return @peercert if @peercert
|
197
201
|
|
@@ -264,12 +268,14 @@ module Puma
|
|
264
268
|
end
|
265
269
|
|
266
270
|
# disables TLSv1
|
271
|
+
# @!attribute [w] no_tlsv1=
|
267
272
|
def no_tlsv1=(tlsv1)
|
268
273
|
raise ArgumentError, "Invalid value of no_tlsv1=" unless ['true', 'false', true, false].include?(tlsv1)
|
269
274
|
@no_tlsv1 = tlsv1
|
270
275
|
end
|
271
276
|
|
272
277
|
# disables TLSv1 and TLSv1.1. Overrides `#no_tlsv1=`
|
278
|
+
# @!attribute [w] no_tlsv1_1=
|
273
279
|
def no_tlsv1_1=(tlsv1_1)
|
274
280
|
raise ArgumentError, "Invalid value of no_tlsv1_1=" unless ['true', 'false', true, false].include?(tlsv1_1)
|
275
281
|
@no_tlsv1_1 = tlsv1_1
|
@@ -287,6 +293,7 @@ module Puma
|
|
287
293
|
@ctx = ctx
|
288
294
|
end
|
289
295
|
|
296
|
+
# @!attribute [r] to_io
|
290
297
|
def to_io
|
291
298
|
@socket
|
292
299
|
end
|
@@ -307,6 +314,7 @@ module Puma
|
|
307
314
|
Socket.new io, engine
|
308
315
|
end
|
309
316
|
|
317
|
+
# @!attribute [r] addr
|
310
318
|
# @version 5.0.0
|
311
319
|
def addr
|
312
320
|
@socket.addr
|
data/lib/puma/reactor.rb
CHANGED
@@ -237,23 +237,12 @@ module Puma
|
|
237
237
|
|
238
238
|
# SSL handshake failure
|
239
239
|
rescue MiniSSL::SSLError => e
|
240
|
-
@server.lowlevel_error
|
241
|
-
|
242
|
-
ssl_socket = c.io
|
243
|
-
begin
|
244
|
-
addr = ssl_socket.peeraddr.last
|
245
|
-
# EINVAL can happen when browser closes socket w/security exception
|
246
|
-
rescue IOError, Errno::EINVAL
|
247
|
-
addr = "<unknown>"
|
248
|
-
end
|
249
|
-
|
250
|
-
cert = ssl_socket.peercert
|
240
|
+
@server.lowlevel_error e, c.env
|
241
|
+
@events.ssl_error e, c.io
|
251
242
|
|
252
243
|
c.close
|
253
244
|
clear_monitor mon
|
254
245
|
|
255
|
-
@events.ssl_error e, addr, cert
|
256
|
-
|
257
246
|
# The client doesn't know HTTP well
|
258
247
|
rescue HttpParserError => e
|
259
248
|
@server.lowlevel_error(e, c.env)
|
data/lib/puma/server.rb
CHANGED
@@ -85,6 +85,8 @@ module Puma
|
|
85
85
|
@precheck_closing = true
|
86
86
|
|
87
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
|
@@ -101,10 +103,18 @@ module Puma
|
|
101
103
|
def tcp_cork_supported?
|
102
104
|
RbConfig::CONFIG['host_os'] =~ /linux/ &&
|
103
105
|
Socket.const_defined?(:IPPROTO_TCP) &&
|
104
|
-
Socket.const_defined?(:TCP_CORK)
|
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) &&
|
105
114
|
Socket.const_defined?(:TCP_INFO)
|
106
115
|
end
|
107
116
|
private :tcp_cork_supported?
|
117
|
+
private :closed_socket_supported?
|
108
118
|
end
|
109
119
|
|
110
120
|
# On Linux, use TCP_CORK to better control how the TCP stack
|
@@ -131,7 +141,15 @@ module Puma
|
|
131
141
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
132
142
|
end
|
133
143
|
end
|
144
|
+
else
|
145
|
+
def cork_socket(socket)
|
146
|
+
end
|
147
|
+
|
148
|
+
def uncork_socket(socket)
|
149
|
+
end
|
150
|
+
end
|
134
151
|
|
152
|
+
if closed_socket_supported?
|
135
153
|
def closed_socket?(socket)
|
136
154
|
return false unless socket.kind_of? TCPSocket
|
137
155
|
return false unless @precheck_closing
|
@@ -149,12 +167,6 @@ module Puma
|
|
149
167
|
end
|
150
168
|
end
|
151
169
|
else
|
152
|
-
def cork_socket(socket)
|
153
|
-
end
|
154
|
-
|
155
|
-
def uncork_socket(socket)
|
156
|
-
end
|
157
|
-
|
158
170
|
def closed_socket?(socket)
|
159
171
|
false
|
160
172
|
end
|
@@ -206,33 +218,36 @@ module Puma
|
|
206
218
|
if @queue_requests
|
207
219
|
process_now = client.eagerly_finish
|
208
220
|
else
|
209
|
-
|
221
|
+
@thread_pool.with_force_shutdown do
|
222
|
+
client.finish(@first_data_timeout)
|
223
|
+
end
|
210
224
|
process_now = true
|
211
225
|
end
|
212
226
|
rescue MiniSSL::SSLError => e
|
213
|
-
|
214
|
-
addr = ssl_socket.peeraddr.last
|
215
|
-
cert = ssl_socket.peercert
|
216
|
-
|
227
|
+
@events.ssl_error e, client.io
|
217
228
|
client.close
|
218
229
|
|
219
|
-
@events.ssl_error e, addr, cert
|
220
230
|
rescue HttpParserError => e
|
221
231
|
client.write_error(400)
|
222
232
|
client.close
|
223
233
|
|
224
234
|
@events.parse_error e, client
|
225
|
-
rescue
|
235
|
+
rescue EOFError => e
|
236
|
+
client.close
|
237
|
+
|
238
|
+
# Swallow, do not log
|
239
|
+
rescue ConnectionError, ThreadPool::ForceShutdown => e
|
226
240
|
client.close
|
227
241
|
|
228
242
|
@events.connection_error e, client
|
229
243
|
else
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
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
|
236
251
|
end
|
237
252
|
|
238
253
|
process_now
|
@@ -318,7 +333,9 @@ module Puma
|
|
318
333
|
@events.fire :state, @status
|
319
334
|
|
320
335
|
if queue_requests
|
321
|
-
@
|
336
|
+
@shutdown_mutex.synchronize do
|
337
|
+
@queue_requests = false
|
338
|
+
end
|
322
339
|
@reactor.clear!
|
323
340
|
@reactor.shutdown
|
324
341
|
end
|
@@ -397,32 +414,32 @@ module Puma
|
|
397
414
|
check_for_more_data = false
|
398
415
|
end
|
399
416
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
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
|
406
429
|
end
|
407
430
|
end
|
408
431
|
end
|
409
432
|
|
410
433
|
# The client disconnected while we were reading data
|
411
|
-
rescue ConnectionError
|
434
|
+
rescue ConnectionError, ThreadPool::ForceShutdown
|
412
435
|
# Swallow them. The ensure tries to close +client+ down
|
413
436
|
|
414
437
|
# SSL handshake error
|
415
438
|
rescue MiniSSL::SSLError => e
|
416
|
-
lowlevel_error
|
417
|
-
|
418
|
-
ssl_socket = client.io
|
419
|
-
addr = ssl_socket.peeraddr.last
|
420
|
-
cert = ssl_socket.peercert
|
421
|
-
|
439
|
+
lowlevel_error e, client.env
|
440
|
+
@events.ssl_error e, client.io
|
422
441
|
close_socket = true
|
423
442
|
|
424
|
-
@events.ssl_error e, addr, cert
|
425
|
-
|
426
443
|
# The client doesn't know HTTP well
|
427
444
|
rescue HttpParserError => e
|
428
445
|
lowlevel_error(e, client.env)
|
@@ -617,7 +634,9 @@ module Puma
|
|
617
634
|
|
618
635
|
begin
|
619
636
|
begin
|
620
|
-
status, headers, res_body = @
|
637
|
+
status, headers, res_body = @thread_pool.with_force_shutdown do
|
638
|
+
@app.call(env)
|
639
|
+
end
|
621
640
|
|
622
641
|
return :async if req.hijacked
|
623
642
|
|
@@ -915,7 +934,7 @@ module Puma
|
|
915
934
|
|
916
935
|
if @thread_pool
|
917
936
|
if timeout = @options[:force_shutdown_after]
|
918
|
-
@thread_pool.shutdown timeout.
|
937
|
+
@thread_pool.shutdown timeout.to_f
|
919
938
|
else
|
920
939
|
@thread_pool.shutdown
|
921
940
|
end
|