puma 5.0.4 → 5.5.1
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 +250 -48
- data/README.md +90 -24
- data/docs/architecture.md +57 -20
- data/docs/compile_options.md +21 -0
- data/docs/deployment.md +53 -67
- data/docs/fork_worker.md +2 -0
- data/docs/jungle/rc.d/README.md +1 -1
- data/docs/kubernetes.md +66 -0
- data/docs/plugins.md +15 -15
- data/docs/rails_dev_mode.md +28 -0
- data/docs/restart.md +7 -7
- data/docs/signals.md +10 -10
- data/docs/stats.md +142 -0
- data/docs/systemd.md +85 -66
- data/ext/puma_http11/extconf.rb +36 -6
- data/ext/puma_http11/http11_parser.c +64 -59
- data/ext/puma_http11/http11_parser.h +1 -1
- data/ext/puma_http11/http11_parser.java.rl +1 -1
- data/ext/puma_http11/http11_parser.rl +1 -1
- data/ext/puma_http11/http11_parser_common.rl +1 -1
- data/ext/puma_http11/mini_ssl.c +177 -84
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +39 -41
- data/ext/puma_http11/puma_http11.c +8 -2
- data/lib/puma/app/status.rb +4 -7
- data/lib/puma/binder.rb +121 -46
- data/lib/puma/cli.rb +9 -0
- data/lib/puma/client.rb +58 -19
- data/lib/puma/cluster/worker.rb +19 -16
- data/lib/puma/cluster/worker_handle.rb +9 -2
- data/lib/puma/cluster.rb +46 -22
- data/lib/puma/configuration.rb +18 -2
- data/lib/puma/const.rb +14 -4
- data/lib/puma/control_cli.rb +76 -71
- data/lib/puma/detect.rb +14 -10
- data/lib/puma/dsl.rb +143 -26
- data/lib/puma/error_logger.rb +12 -5
- data/lib/puma/events.rb +18 -3
- data/lib/puma/json_serialization.rb +96 -0
- data/lib/puma/launcher.rb +54 -6
- data/lib/puma/minissl/context_builder.rb +6 -0
- data/lib/puma/minissl.rb +54 -38
- data/lib/puma/null_io.rb +12 -0
- data/lib/puma/plugin.rb +1 -1
- data/lib/puma/queue_close.rb +7 -7
- data/lib/puma/rack/builder.rb +1 -1
- data/lib/puma/reactor.rb +19 -12
- data/lib/puma/request.rb +45 -16
- data/lib/puma/runner.rb +38 -13
- data/lib/puma/server.rb +62 -123
- data/lib/puma/state_file.rb +5 -3
- data/lib/puma/systemd.rb +46 -0
- data/lib/puma/thread_pool.rb +10 -7
- data/lib/puma/util.rb +8 -1
- data/lib/puma.rb +36 -10
- data/lib/rack/handler/puma.rb +1 -0
- metadata +15 -9
data/lib/puma/server.rb
CHANGED
@@ -14,6 +14,7 @@ require 'puma/io_buffer'
|
|
14
14
|
require 'puma/request'
|
15
15
|
|
16
16
|
require 'socket'
|
17
|
+
require 'io/wait'
|
17
18
|
require 'forwardable'
|
18
19
|
|
19
20
|
module Puma
|
@@ -84,12 +85,14 @@ module Puma
|
|
84
85
|
|
85
86
|
@options = options
|
86
87
|
|
87
|
-
@early_hints
|
88
|
-
@first_data_timeout
|
89
|
-
@min_threads
|
90
|
-
@max_threads
|
91
|
-
@persistent_timeout
|
92
|
-
@queue_requests
|
88
|
+
@early_hints = options.fetch :early_hints, nil
|
89
|
+
@first_data_timeout = options.fetch :first_data_timeout, FIRST_DATA_TIMEOUT
|
90
|
+
@min_threads = options.fetch :min_threads, 0
|
91
|
+
@max_threads = options.fetch :max_threads , (Puma.mri? ? 5 : 16)
|
92
|
+
@persistent_timeout = options.fetch :persistent_timeout, PERSISTENT_TIMEOUT
|
93
|
+
@queue_requests = options.fetch :queue_requests, true
|
94
|
+
@max_fast_inline = options.fetch :max_fast_inline, MAX_FAST_INLINE
|
95
|
+
@io_selector_backend = options.fetch :io_selector_backend, :auto
|
93
96
|
|
94
97
|
temp = !!(@options[:environment] =~ /\A(development|test)\z/)
|
95
98
|
@leak_stack_on_error = @options[:environment] ? temp : true
|
@@ -118,17 +121,13 @@ module Puma
|
|
118
121
|
# :nodoc:
|
119
122
|
# @version 5.0.0
|
120
123
|
def tcp_cork_supported?
|
121
|
-
|
122
|
-
Socket.const_defined?(:IPPROTO_TCP) &&
|
123
|
-
Socket.const_defined?(:TCP_CORK)
|
124
|
+
Socket.const_defined?(:TCP_CORK) && Socket.const_defined?(:IPPROTO_TCP)
|
124
125
|
end
|
125
126
|
|
126
127
|
# :nodoc:
|
127
128
|
# @version 5.0.0
|
128
129
|
def closed_socket_supported?
|
129
|
-
|
130
|
-
Socket.const_defined?(:IPPROTO_TCP) &&
|
131
|
-
Socket.const_defined?(:TCP_INFO)
|
130
|
+
Socket.const_defined?(:TCP_INFO) && Socket.const_defined?(:IPPROTO_TCP)
|
132
131
|
end
|
133
132
|
private :tcp_cork_supported?
|
134
133
|
private :closed_socket_supported?
|
@@ -136,26 +135,27 @@ module Puma
|
|
136
135
|
|
137
136
|
# On Linux, use TCP_CORK to better control how the TCP stack
|
138
137
|
# packetizes our stream. This improves both latency and throughput.
|
138
|
+
# socket parameter may be an MiniSSL::Socket, so use to_io
|
139
139
|
#
|
140
140
|
if tcp_cork_supported?
|
141
|
-
UNPACK_TCP_STATE_FROM_TCP_INFO = "C".freeze
|
142
|
-
|
143
141
|
# 6 == Socket::IPPROTO_TCP
|
144
142
|
# 3 == TCP_CORK
|
145
143
|
# 1/0 == turn on/off
|
146
144
|
def cork_socket(socket)
|
145
|
+
skt = socket.to_io
|
147
146
|
begin
|
148
|
-
|
147
|
+
skt.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 1) if skt.kind_of? TCPSocket
|
149
148
|
rescue IOError, SystemCallError
|
150
|
-
|
149
|
+
Puma::Util.purge_interrupt_queue
|
151
150
|
end
|
152
151
|
end
|
153
152
|
|
154
153
|
def uncork_socket(socket)
|
154
|
+
skt = socket.to_io
|
155
155
|
begin
|
156
|
-
|
156
|
+
skt.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 0) if skt.kind_of? TCPSocket
|
157
157
|
rescue IOError, SystemCallError
|
158
|
-
|
158
|
+
Puma::Util.purge_interrupt_queue
|
159
159
|
end
|
160
160
|
end
|
161
161
|
else
|
@@ -167,14 +167,16 @@ module Puma
|
|
167
167
|
end
|
168
168
|
|
169
169
|
if closed_socket_supported?
|
170
|
+
UNPACK_TCP_STATE_FROM_TCP_INFO = "C".freeze
|
171
|
+
|
170
172
|
def closed_socket?(socket)
|
171
|
-
|
172
|
-
return false unless @precheck_closing
|
173
|
+
skt = socket.to_io
|
174
|
+
return false unless skt.kind_of?(TCPSocket) && @precheck_closing
|
173
175
|
|
174
176
|
begin
|
175
|
-
tcp_info =
|
177
|
+
tcp_info = skt.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_INFO)
|
176
178
|
rescue IOError, SystemCallError
|
177
|
-
|
179
|
+
Puma::Util.purge_interrupt_queue
|
178
180
|
@precheck_closing = false
|
179
181
|
false
|
180
182
|
else
|
@@ -218,7 +220,7 @@ module Puma
|
|
218
220
|
# up in the background to handle requests. Otherwise requests
|
219
221
|
# are handled synchronously.
|
220
222
|
#
|
221
|
-
def run(background=true)
|
223
|
+
def run(background=true, thread_name: 'server')
|
222
224
|
BasicSocket.do_not_reverse_lookup = true
|
223
225
|
|
224
226
|
@events.fire :state, :booting
|
@@ -226,6 +228,7 @@ module Puma
|
|
226
228
|
@status = :run
|
227
229
|
|
228
230
|
@thread_pool = ThreadPool.new(
|
231
|
+
thread_name,
|
229
232
|
@min_threads,
|
230
233
|
@max_threads,
|
231
234
|
::Puma::IOBuffer,
|
@@ -236,7 +239,7 @@ module Puma
|
|
236
239
|
@thread_pool.clean_thread_locals = @options[:clean_thread_locals]
|
237
240
|
|
238
241
|
if @queue_requests
|
239
|
-
@reactor = Reactor.new(&method(:reactor_wakeup))
|
242
|
+
@reactor = Reactor.new(@io_selector_backend, &method(:reactor_wakeup))
|
240
243
|
@reactor.run
|
241
244
|
end
|
242
245
|
|
@@ -254,7 +257,7 @@ module Puma
|
|
254
257
|
|
255
258
|
if background
|
256
259
|
@thread = Thread.new do
|
257
|
-
Puma.set_thread_name
|
260
|
+
Puma.set_thread_name thread_name
|
258
261
|
handle_servers
|
259
262
|
end
|
260
263
|
return @thread
|
@@ -294,6 +297,9 @@ module Puma
|
|
294
297
|
@thread_pool << client
|
295
298
|
elsif shutdown || client.timeout == 0
|
296
299
|
client.timeout!
|
300
|
+
else
|
301
|
+
client.set_timeout(@first_data_timeout)
|
302
|
+
false
|
297
303
|
end
|
298
304
|
rescue StandardError => e
|
299
305
|
client_error(e, client)
|
@@ -307,6 +313,7 @@ module Puma
|
|
307
313
|
sockets = [check] + @binder.ios
|
308
314
|
pool = @thread_pool
|
309
315
|
queue_requests = @queue_requests
|
316
|
+
drain = @options[:drain_on_shutdown] ? 0 : nil
|
310
317
|
|
311
318
|
remote_addr_value = nil
|
312
319
|
remote_addr_header = nil
|
@@ -316,38 +323,48 @@ module Puma
|
|
316
323
|
remote_addr_value = @options[:remote_address_value]
|
317
324
|
when :header
|
318
325
|
remote_addr_header = @options[:remote_address_header]
|
326
|
+
when :proxy_protocol
|
327
|
+
remote_addr_proxy_protocol = @options[:remote_address_proxy_protocol]
|
319
328
|
end
|
320
329
|
|
321
|
-
while @status == :run
|
330
|
+
while @status == :run || (drain && shutting_down?)
|
322
331
|
begin
|
323
|
-
ios = IO.select sockets
|
332
|
+
ios = IO.select sockets, nil, nil, (shutting_down? ? 0 : nil)
|
333
|
+
break unless ios
|
324
334
|
ios.first.each do |sock|
|
325
335
|
if sock == check
|
326
336
|
break if handle_check
|
327
337
|
else
|
328
338
|
pool.wait_until_not_full
|
329
|
-
pool.wait_for_less_busy_worker(
|
330
|
-
@options[:wait_for_less_busy_worker].to_f)
|
339
|
+
pool.wait_for_less_busy_worker(@options[:wait_for_less_busy_worker])
|
331
340
|
|
332
341
|
io = begin
|
333
342
|
sock.accept_nonblock
|
334
343
|
rescue IO::WaitReadable
|
335
344
|
next
|
336
345
|
end
|
346
|
+
drain += 1 if shutting_down?
|
337
347
|
client = Client.new io, @binder.env(sock)
|
348
|
+
client.listener = sock
|
338
349
|
if remote_addr_value
|
339
350
|
client.peerip = remote_addr_value
|
340
351
|
elsif remote_addr_header
|
341
352
|
client.remote_addr_header = remote_addr_header
|
353
|
+
elsif remote_addr_proxy_protocol
|
354
|
+
client.expect_proxy_proto = remote_addr_proxy_protocol
|
342
355
|
end
|
343
356
|
pool << client
|
344
357
|
end
|
345
358
|
end
|
346
|
-
rescue
|
359
|
+
rescue IOError, Errno::EBADF
|
360
|
+
# In the case that any of the sockets are unexpectedly close.
|
361
|
+
raise
|
362
|
+
rescue StandardError => e
|
347
363
|
@events.unknown_error e, nil, "Listen loop"
|
348
364
|
end
|
349
365
|
end
|
350
366
|
|
367
|
+
@events.debug "Drained #{drain} additional connections." if drain
|
351
368
|
@events.fire :state, @status
|
352
369
|
|
353
370
|
if queue_requests
|
@@ -388,7 +405,7 @@ module Puma
|
|
388
405
|
return true
|
389
406
|
end
|
390
407
|
|
391
|
-
|
408
|
+
false
|
392
409
|
end
|
393
410
|
|
394
411
|
# Given a connection on +client+, handle the incoming requests,
|
@@ -427,7 +444,7 @@ module Puma
|
|
427
444
|
|
428
445
|
while true
|
429
446
|
@requests_count += 1
|
430
|
-
case handle_request(client, buffer)
|
447
|
+
case handle_request(client, buffer, requests + 1)
|
431
448
|
when false
|
432
449
|
break
|
433
450
|
when :async
|
@@ -440,18 +457,17 @@ module Puma
|
|
440
457
|
|
441
458
|
requests += 1
|
442
459
|
|
443
|
-
|
460
|
+
# As an optimization, try to read the next request from the
|
461
|
+
# socket for a short time before returning to the reactor.
|
462
|
+
fast_check = @status == :run
|
444
463
|
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
# one once every MAX_FAST_INLINE requests.
|
450
|
-
check_for_more_data = false
|
451
|
-
end
|
464
|
+
# Always pass the client back to the reactor after a reasonable
|
465
|
+
# number of inline requests if there are other requests pending.
|
466
|
+
fast_check = false if requests >= @max_fast_inline &&
|
467
|
+
@thread_pool.backlog > 0
|
452
468
|
|
453
469
|
next_request_ready = with_force_shutdown(client) do
|
454
|
-
client.reset(
|
470
|
+
client.reset(fast_check)
|
455
471
|
end
|
456
472
|
|
457
473
|
unless next_request_ready
|
@@ -475,7 +491,7 @@ module Puma
|
|
475
491
|
begin
|
476
492
|
client.close if close_socket
|
477
493
|
rescue IOError, SystemCallError
|
478
|
-
|
494
|
+
Puma::Util.purge_interrupt_queue
|
479
495
|
# Already closed
|
480
496
|
rescue StandardError => e
|
481
497
|
@events.unknown_error e, nil, "Client"
|
@@ -493,62 +509,6 @@ module Puma
|
|
493
509
|
|
494
510
|
# :nocov:
|
495
511
|
|
496
|
-
# Given the request +env+ from +client+ and the partial body +body+
|
497
|
-
# plus a potential Content-Length value +cl+, finish reading
|
498
|
-
# the body and return it.
|
499
|
-
#
|
500
|
-
# If the body is larger than MAX_BODY, a Tempfile object is used
|
501
|
-
# for the body, otherwise a StringIO is used.
|
502
|
-
# @deprecated 6.0.0
|
503
|
-
#
|
504
|
-
def read_body(env, client, body, cl)
|
505
|
-
content_length = cl.to_i
|
506
|
-
|
507
|
-
remain = content_length - body.bytesize
|
508
|
-
|
509
|
-
return StringIO.new(body) if remain <= 0
|
510
|
-
|
511
|
-
# Use a Tempfile if there is a lot of data left
|
512
|
-
if remain > MAX_BODY
|
513
|
-
stream = Tempfile.new(Const::PUMA_TMP_BASE)
|
514
|
-
stream.binmode
|
515
|
-
else
|
516
|
-
# The body[0,0] trick is to get an empty string in the same
|
517
|
-
# encoding as body.
|
518
|
-
stream = StringIO.new body[0,0]
|
519
|
-
end
|
520
|
-
|
521
|
-
stream.write body
|
522
|
-
|
523
|
-
# Read an odd sized chunk so we can read even sized ones
|
524
|
-
# after this
|
525
|
-
chunk = client.readpartial(remain % CHUNK_SIZE)
|
526
|
-
|
527
|
-
# No chunk means a closed socket
|
528
|
-
unless chunk
|
529
|
-
stream.close
|
530
|
-
return nil
|
531
|
-
end
|
532
|
-
|
533
|
-
remain -= stream.write(chunk)
|
534
|
-
|
535
|
-
# Read the rest of the chunks
|
536
|
-
while remain > 0
|
537
|
-
chunk = client.readpartial(CHUNK_SIZE)
|
538
|
-
unless chunk
|
539
|
-
stream.close
|
540
|
-
return nil
|
541
|
-
end
|
542
|
-
|
543
|
-
remain -= stream.write(chunk)
|
544
|
-
end
|
545
|
-
|
546
|
-
stream.rewind
|
547
|
-
|
548
|
-
return stream
|
549
|
-
end
|
550
|
-
# :nocov:
|
551
|
-
|
552
512
|
# Handle various error types thrown by Client I/O operations.
|
553
513
|
def client_error(e, client)
|
554
514
|
# Swallow, do not log
|
@@ -581,7 +541,8 @@ module Puma
|
|
581
541
|
end
|
582
542
|
|
583
543
|
if @leak_stack_on_error
|
584
|
-
|
544
|
+
backtrace = e.backtrace.nil? ? '<no backtrace available>' : e.backtrace.join("\n")
|
545
|
+
[status, {}, ["Puma caught this error: #{e.message} (#{e.class})\n#{backtrace}"]]
|
585
546
|
else
|
586
547
|
[status, {}, ["An unhandled lowlevel error occurred. The application logs may have details.\n"]]
|
587
548
|
end
|
@@ -605,28 +566,6 @@ module Puma
|
|
605
566
|
$stdout.syswrite "#{pid}: === End thread backtrace dump ===\n"
|
606
567
|
end
|
607
568
|
|
608
|
-
if @options[:drain_on_shutdown]
|
609
|
-
count = 0
|
610
|
-
|
611
|
-
while true
|
612
|
-
ios = IO.select @binder.ios, nil, nil, 0
|
613
|
-
break unless ios
|
614
|
-
|
615
|
-
ios.first.each do |sock|
|
616
|
-
begin
|
617
|
-
if io = sock.accept_nonblock
|
618
|
-
count += 1
|
619
|
-
client = Client.new io, @binder.env(sock)
|
620
|
-
@thread_pool << client
|
621
|
-
end
|
622
|
-
rescue SystemCallError
|
623
|
-
end
|
624
|
-
end
|
625
|
-
end
|
626
|
-
|
627
|
-
@events.debug "Drained #{count} additional connections."
|
628
|
-
end
|
629
|
-
|
630
569
|
if @status != :restart
|
631
570
|
@binder.close
|
632
571
|
end
|
@@ -644,11 +583,11 @@ module Puma
|
|
644
583
|
@notify << message
|
645
584
|
rescue IOError, NoMethodError, Errno::EPIPE
|
646
585
|
# The server, in another thread, is shutting down
|
647
|
-
|
586
|
+
Puma::Util.purge_interrupt_queue
|
648
587
|
rescue RuntimeError => e
|
649
588
|
# Temporary workaround for https://bugs.ruby-lang.org/issues/13239
|
650
589
|
if e.message.include?('IOError')
|
651
|
-
|
590
|
+
Puma::Util.purge_interrupt_queue
|
652
591
|
else
|
653
592
|
raise e
|
654
593
|
end
|
data/lib/puma/state_file.rb
CHANGED
@@ -9,9 +9,11 @@ module Puma
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def save(path, permission = nil)
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
contents =YAML.dump @options
|
13
|
+
if permission
|
14
|
+
File.write path, contents, mode: 'wb:UTF-8'
|
15
|
+
else
|
16
|
+
File.write path, contents, mode: 'wb:UTF-8', perm: permission
|
15
17
|
end
|
16
18
|
end
|
17
19
|
|
data/lib/puma/systemd.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sd_notify'
|
4
|
+
|
5
|
+
module Puma
|
6
|
+
class Systemd
|
7
|
+
def initialize(events)
|
8
|
+
@events = events
|
9
|
+
end
|
10
|
+
|
11
|
+
def hook_events
|
12
|
+
@events.on_booted { SdNotify.ready }
|
13
|
+
@events.on_stopped { SdNotify.stopping }
|
14
|
+
@events.on_restart { SdNotify.reloading }
|
15
|
+
end
|
16
|
+
|
17
|
+
def start_watchdog
|
18
|
+
return unless SdNotify.watchdog?
|
19
|
+
|
20
|
+
ping_f = watchdog_sleep_time
|
21
|
+
|
22
|
+
log "Pinging systemd watchdog every #{ping_f.round(1)} sec"
|
23
|
+
Thread.new do
|
24
|
+
loop do
|
25
|
+
sleep ping_f
|
26
|
+
SdNotify.watchdog
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def watchdog_sleep_time
|
34
|
+
usec = Integer(ENV["WATCHDOG_USEC"])
|
35
|
+
|
36
|
+
sec_f = usec / 1_000_000.0
|
37
|
+
# "It is recommended that a daemon sends a keep-alive notification message
|
38
|
+
# to the service manager every half of the time returned here."
|
39
|
+
sec_f / 2
|
40
|
+
end
|
41
|
+
|
42
|
+
def log(str)
|
43
|
+
@events.log str
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/puma/thread_pool.rb
CHANGED
@@ -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
|
16
|
+
# and processes it.
|
17
17
|
class ThreadPool
|
18
18
|
class ForceShutdown < RuntimeError
|
19
19
|
end
|
@@ -29,7 +29,7 @@ module Puma
|
|
29
29
|
# The block passed is the work that will be performed in each
|
30
30
|
# thread.
|
31
31
|
#
|
32
|
-
def initialize(min, max, *extra, &block)
|
32
|
+
def initialize(name, min, max, *extra, &block)
|
33
33
|
@not_empty = ConditionVariable.new
|
34
34
|
@not_full = ConditionVariable.new
|
35
35
|
@mutex = Mutex.new
|
@@ -39,6 +39,7 @@ module Puma
|
|
39
39
|
@spawned = 0
|
40
40
|
@waiting = 0
|
41
41
|
|
42
|
+
@name = name
|
42
43
|
@min = Integer(min)
|
43
44
|
@max = Integer(max)
|
44
45
|
@block = block
|
@@ -101,7 +102,7 @@ module Puma
|
|
101
102
|
@spawned += 1
|
102
103
|
|
103
104
|
th = Thread.new(@spawned) do |spawned|
|
104
|
-
Puma.set_thread_name 'threadpool %03i' % spawned
|
105
|
+
Puma.set_thread_name '%s threadpool %03i' % [@name, spawned]
|
105
106
|
todo = @todo
|
106
107
|
block = @block
|
107
108
|
mutex = @mutex
|
@@ -119,6 +120,7 @@ module Puma
|
|
119
120
|
@trim_requested -= 1
|
120
121
|
@spawned -= 1
|
121
122
|
@workers.delete th
|
123
|
+
not_full.signal
|
122
124
|
Thread.exit
|
123
125
|
end
|
124
126
|
|
@@ -220,7 +222,7 @@ module Puma
|
|
220
222
|
# then the `@todo` array would stay the same size as the reactor works
|
221
223
|
# to try to buffer the request. In that scenario the next call to this
|
222
224
|
# method would not block and another request would be added into the reactor
|
223
|
-
# by the server. This would continue until a fully
|
225
|
+
# by the server. This would continue until a fully buffered request
|
224
226
|
# makes it through the reactor and can then be processed by the thread pool.
|
225
227
|
def wait_until_not_full
|
226
228
|
with_mutex do
|
@@ -240,11 +242,12 @@ module Puma
|
|
240
242
|
|
241
243
|
# @version 5.0.0
|
242
244
|
def wait_for_less_busy_worker(delay_s)
|
245
|
+
return unless delay_s && delay_s > 0
|
246
|
+
|
243
247
|
# Ruby MRI does GVL, this can result
|
244
248
|
# in processing contention when multiple threads
|
245
249
|
# (requests) are running concurrently
|
246
250
|
return unless Puma.mri?
|
247
|
-
return unless delay_s > 0
|
248
251
|
|
249
252
|
with_mutex do
|
250
253
|
return if @shutdown
|
@@ -317,12 +320,12 @@ module Puma
|
|
317
320
|
end
|
318
321
|
|
319
322
|
def auto_trim!(timeout=30)
|
320
|
-
@auto_trim = Automaton.new(self, timeout, "threadpool trimmer", :trim)
|
323
|
+
@auto_trim = Automaton.new(self, timeout, "#{@name} threadpool trimmer", :trim)
|
321
324
|
@auto_trim.start!
|
322
325
|
end
|
323
326
|
|
324
327
|
def auto_reap!(timeout=5)
|
325
|
-
@reaper = Automaton.new(self, timeout, "threadpool reaper", :reap)
|
328
|
+
@reaper = Automaton.new(self, timeout, "#{@name} threadpool reaper", :reap)
|
326
329
|
@reaper.start!
|
327
330
|
end
|
328
331
|
|
data/lib/puma/util.rb
CHANGED
@@ -10,6 +10,13 @@ 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 effects 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
|
+
|
13
20
|
# Unescapes a URI escaped string with +encoding+. +encoding+ will be the
|
14
21
|
# target encoding of the string returned, and it defaults to UTF-8
|
15
22
|
if defined?(::Encoding)
|
@@ -61,7 +68,7 @@ module Puma
|
|
61
68
|
end
|
62
69
|
end
|
63
70
|
|
64
|
-
|
71
|
+
params
|
65
72
|
end
|
66
73
|
|
67
74
|
# A case-insensitive Hash that preserves the original case of a
|
data/lib/puma.rb
CHANGED
@@ -12,12 +12,47 @@ require 'thread'
|
|
12
12
|
|
13
13
|
require 'puma/puma_http11'
|
14
14
|
require 'puma/detect'
|
15
|
+
require 'puma/json_serialization'
|
15
16
|
|
16
17
|
module Puma
|
17
18
|
autoload :Const, 'puma/const'
|
18
19
|
autoload :Server, 'puma/server'
|
19
20
|
autoload :Launcher, 'puma/launcher'
|
20
21
|
|
22
|
+
# at present, MiniSSL::Engine is only defined in extension code (puma_http11),
|
23
|
+
# not in minissl.rb
|
24
|
+
HAS_SSL = const_defined?(:MiniSSL, false) && MiniSSL.const_defined?(:Engine, false)
|
25
|
+
|
26
|
+
HAS_UNIX_SOCKET = Object.const_defined? :UNIXSocket
|
27
|
+
|
28
|
+
if HAS_SSL
|
29
|
+
require 'puma/minissl'
|
30
|
+
else
|
31
|
+
module MiniSSL
|
32
|
+
# this class is defined so that it exists when Puma is compiled
|
33
|
+
# without ssl support, as Server and Reactor use it in rescue statements.
|
34
|
+
class SSLError < StandardError ; end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.ssl?
|
39
|
+
HAS_SSL
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.abstract_unix_socket?
|
43
|
+
@abstract_unix ||=
|
44
|
+
if HAS_UNIX_SOCKET
|
45
|
+
begin
|
46
|
+
::UNIXServer.new("\0puma.temp.unix").close
|
47
|
+
true
|
48
|
+
rescue ArgumentError # darwin
|
49
|
+
false
|
50
|
+
end
|
51
|
+
else
|
52
|
+
false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
21
56
|
# @!attribute [rw] stats_object=
|
22
57
|
def self.stats_object=(val)
|
23
58
|
@get_stats = val
|
@@ -25,8 +60,7 @@ module Puma
|
|
25
60
|
|
26
61
|
# @!attribute [rw] stats_object
|
27
62
|
def self.stats
|
28
|
-
|
29
|
-
@get_stats.stats.to_json
|
63
|
+
Puma::JSONSerialization.generate @get_stats.stats
|
30
64
|
end
|
31
65
|
|
32
66
|
# @!attribute [r] stats_hash
|
@@ -40,12 +74,4 @@ module Puma
|
|
40
74
|
return unless Thread.current.respond_to?(:name=)
|
41
75
|
Thread.current.name = "puma #{name}"
|
42
76
|
end
|
43
|
-
|
44
|
-
unless HAS_SSL
|
45
|
-
module MiniSSL
|
46
|
-
# this class is defined so that it exists when Puma is compiled
|
47
|
-
# without ssl support, as Server and Reactor use it in rescue statements.
|
48
|
-
class SSLError < StandardError ; end
|
49
|
-
end
|
50
|
-
end
|
51
77
|
end
|
data/lib/rack/handler/puma.rb
CHANGED