puma 3.8.2 → 4.3.12
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 +5 -5
- data/History.md +305 -0
- data/LICENSE +0 -0
- data/README.md +162 -224
- data/bin/puma-wild +0 -0
- data/docs/architecture.md +37 -0
- data/{DEPLOYMENT.md → docs/deployment.md} +24 -4
- data/docs/images/puma-connection-flow-no-reactor.png +0 -0
- data/docs/images/puma-connection-flow.png +0 -0
- data/docs/images/puma-general-arch.png +0 -0
- data/docs/nginx.md +0 -0
- data/docs/plugins.md +38 -0
- data/docs/restart.md +41 -0
- data/docs/signals.md +56 -3
- data/docs/systemd.md +130 -37
- data/docs/tcp_mode.md +96 -0
- data/ext/puma_http11/PumaHttp11Service.java +2 -0
- data/ext/puma_http11/ext_help.h +0 -0
- data/ext/puma_http11/extconf.rb +21 -0
- data/ext/puma_http11/http11_parser.c +134 -144
- data/ext/puma_http11/http11_parser.h +0 -0
- data/ext/puma_http11/http11_parser.java.rl +21 -37
- data/ext/puma_http11/http11_parser.rl +12 -10
- data/ext/puma_http11/http11_parser_common.rl +4 -4
- data/ext/puma_http11/io_buffer.c +0 -0
- data/ext/puma_http11/mini_ssl.c +165 -34
- data/ext/puma_http11/org/jruby/puma/Http11.java +106 -114
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +85 -101
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +72 -0
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +30 -6
- data/ext/puma_http11/puma_http11.c +3 -0
- data/lib/puma/accept_nonblock.rb +7 -1
- data/lib/puma/app/status.rb +42 -26
- data/lib/puma/binder.rb +57 -74
- data/lib/puma/cli.rb +26 -7
- data/lib/puma/client.rb +307 -191
- data/lib/puma/cluster.rb +78 -34
- data/lib/puma/commonlogger.rb +2 -0
- data/lib/puma/configuration.rb +24 -16
- data/lib/puma/const.rb +41 -20
- data/lib/puma/control_cli.rb +46 -19
- data/lib/puma/detect.rb +2 -0
- data/lib/puma/dsl.rb +329 -68
- data/lib/puma/events.rb +6 -2
- data/lib/puma/io_buffer.rb +3 -6
- data/lib/puma/jruby_restart.rb +2 -1
- data/lib/puma/launcher.rb +125 -61
- data/lib/puma/minissl/context_builder.rb +76 -0
- data/lib/puma/minissl.rb +85 -28
- data/lib/puma/null_io.rb +2 -0
- data/lib/puma/plugin/tmp_restart.rb +2 -1
- data/lib/puma/plugin.rb +7 -2
- data/lib/puma/rack/builder.rb +4 -1
- data/lib/puma/rack/urlmap.rb +2 -0
- data/lib/puma/rack_default.rb +2 -0
- data/lib/puma/reactor.rb +224 -34
- data/lib/puma/runner.rb +27 -6
- data/lib/puma/server.rb +212 -68
- data/lib/puma/single.rb +16 -5
- data/lib/puma/state_file.rb +2 -0
- data/lib/puma/tcp_logger.rb +2 -0
- data/lib/puma/thread_pool.rb +67 -36
- data/lib/puma/util.rb +2 -6
- data/lib/puma.rb +16 -0
- data/lib/rack/handler/puma.rb +16 -5
- data/tools/docker/Dockerfile +16 -0
- data/tools/jungle/README.md +12 -2
- data/tools/jungle/init.d/README.md +2 -0
- data/tools/jungle/init.d/puma +8 -8
- data/tools/jungle/init.d/run-puma +1 -1
- data/tools/jungle/rc.d/README.md +74 -0
- data/tools/jungle/rc.d/puma +61 -0
- data/tools/jungle/rc.d/puma.conf +10 -0
- data/tools/jungle/upstart/README.md +0 -0
- data/tools/jungle/upstart/puma-manager.conf +0 -0
- data/tools/jungle/upstart/puma.conf +0 -0
- data/tools/trickletest.rb +1 -2
- metadata +32 -93
- data/.github/issue_template.md +0 -20
- data/Gemfile +0 -12
- data/Manifest.txt +0 -78
- data/Rakefile +0 -158
- data/Release.md +0 -9
- data/gemfiles/2.1-Gemfile +0 -12
- data/lib/puma/compat.rb +0 -14
- data/lib/puma/convenient.rb +0 -23
- data/lib/puma/daemon_ext.rb +0 -31
- data/lib/puma/delegation.rb +0 -11
- data/lib/puma/java_io_buffer.rb +0 -45
- data/lib/puma/rack/backports/uri/common_193.rb +0 -33
- data/puma.gemspec +0 -52
data/lib/puma/server.rb
CHANGED
@@ -1,32 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'stringio'
|
2
4
|
|
3
5
|
require 'puma/thread_pool'
|
4
6
|
require 'puma/const'
|
5
7
|
require 'puma/events'
|
6
8
|
require 'puma/null_io'
|
7
|
-
require 'puma/compat'
|
8
9
|
require 'puma/reactor'
|
9
10
|
require 'puma/client'
|
10
11
|
require 'puma/binder'
|
11
|
-
require 'puma/delegation'
|
12
12
|
require 'puma/accept_nonblock'
|
13
13
|
require 'puma/util'
|
14
14
|
|
15
15
|
require 'puma/puma_http11'
|
16
16
|
|
17
|
-
unless Puma.const_defined? "IOBuffer"
|
18
|
-
require 'puma/io_buffer'
|
19
|
-
end
|
20
|
-
|
21
17
|
require 'socket'
|
18
|
+
require 'forwardable'
|
22
19
|
|
23
20
|
module Puma
|
24
21
|
|
25
22
|
# The HTTP Server itself. Serves out a single Rack app.
|
23
|
+
#
|
24
|
+
# This class is used by the `Puma::Single` and `Puma::Cluster` classes
|
25
|
+
# to generate one or more `Puma::Server` instances capable of handling requests.
|
26
|
+
# Each Puma process will contain one `Puma::Server` instance.
|
27
|
+
#
|
28
|
+
# The `Puma::Server` instance pulls requests from the socket, adds them to a
|
29
|
+
# `Puma::Reactor` where they get eventually passed to a `Puma::ThreadPool`.
|
30
|
+
#
|
31
|
+
# Each `Puma::Server` will have one reactor and one thread pool.
|
26
32
|
class Server
|
27
33
|
|
28
34
|
include Puma::Const
|
29
|
-
extend
|
35
|
+
extend Forwardable
|
30
36
|
|
31
37
|
attr_reader :thread
|
32
38
|
attr_reader :events
|
@@ -62,13 +68,12 @@ module Puma
|
|
62
68
|
|
63
69
|
@thread = nil
|
64
70
|
@thread_pool = nil
|
71
|
+
@early_hints = nil
|
65
72
|
|
66
73
|
@persistent_timeout = options.fetch(:persistent_timeout, PERSISTENT_TIMEOUT)
|
74
|
+
@first_data_timeout = options.fetch(:first_data_timeout, FIRST_DATA_TIMEOUT)
|
67
75
|
|
68
76
|
@binder = Binder.new(events)
|
69
|
-
@own_binder = true
|
70
|
-
|
71
|
-
@first_data_timeout = FIRST_DATA_TIMEOUT
|
72
77
|
|
73
78
|
@leak_stack_on_error = true
|
74
79
|
|
@@ -82,16 +87,12 @@ module Puma
|
|
82
87
|
@precheck_closing = true
|
83
88
|
end
|
84
89
|
|
85
|
-
attr_accessor :binder, :leak_stack_on_error
|
90
|
+
attr_accessor :binder, :leak_stack_on_error, :early_hints
|
86
91
|
|
87
|
-
|
88
|
-
forward :add_ssl_listener, :@binder
|
89
|
-
forward :add_unix_listener, :@binder
|
90
|
-
forward :connected_port, :@binder
|
92
|
+
def_delegators :@binder, :add_tcp_listener, :add_ssl_listener, :add_unix_listener, :connected_port
|
91
93
|
|
92
94
|
def inherit_binder(bind)
|
93
95
|
@binder = bind
|
94
|
-
@own_binder = false
|
95
96
|
end
|
96
97
|
|
97
98
|
def tcp_mode!
|
@@ -111,6 +112,7 @@ module Puma
|
|
111
112
|
begin
|
112
113
|
socket.setsockopt(6, 3, 1) if socket.kind_of? TCPSocket
|
113
114
|
rescue IOError, SystemCallError
|
115
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
114
116
|
end
|
115
117
|
end
|
116
118
|
|
@@ -118,6 +120,7 @@ module Puma
|
|
118
120
|
begin
|
119
121
|
socket.setsockopt(6, 3, 0) if socket.kind_of? TCPSocket
|
120
122
|
rescue IOError, SystemCallError
|
123
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
121
124
|
end
|
122
125
|
end
|
123
126
|
|
@@ -128,6 +131,7 @@ module Puma
|
|
128
131
|
begin
|
129
132
|
tcp_info = socket.getsockopt(Socket::SOL_TCP, Socket::TCP_INFO)
|
130
133
|
rescue IOError, SystemCallError
|
134
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
131
135
|
@precheck_closing = false
|
132
136
|
false
|
133
137
|
else
|
@@ -156,6 +160,18 @@ module Puma
|
|
156
160
|
@thread_pool and @thread_pool.spawned
|
157
161
|
end
|
158
162
|
|
163
|
+
|
164
|
+
# This number represents the number of requests that
|
165
|
+
# the server is capable of taking right now.
|
166
|
+
#
|
167
|
+
# For example if the number is 5 then it means
|
168
|
+
# there are 5 threads sitting idle ready to take
|
169
|
+
# a request. If one request comes in, then the
|
170
|
+
# value would be 4 until it finishes processing.
|
171
|
+
def pool_capacity
|
172
|
+
@thread_pool and @thread_pool.pool_capacity
|
173
|
+
end
|
174
|
+
|
159
175
|
# Lopez Mode == raw tcp apps
|
160
176
|
|
161
177
|
def run_lopez_mode(background=true)
|
@@ -188,7 +204,10 @@ module Puma
|
|
188
204
|
@events.fire :state, :running
|
189
205
|
|
190
206
|
if background
|
191
|
-
@thread = Thread.new
|
207
|
+
@thread = Thread.new do
|
208
|
+
Puma.set_thread_name "server"
|
209
|
+
handle_servers_lopez_mode
|
210
|
+
end
|
192
211
|
return @thread
|
193
212
|
else
|
194
213
|
handle_servers_lopez_mode
|
@@ -217,7 +236,11 @@ module Puma
|
|
217
236
|
# nothing
|
218
237
|
rescue Errno::ECONNABORTED
|
219
238
|
# client closed the socket even before accept
|
220
|
-
|
239
|
+
begin
|
240
|
+
io.close
|
241
|
+
rescue
|
242
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
243
|
+
end
|
221
244
|
end
|
222
245
|
end
|
223
246
|
end
|
@@ -234,11 +257,17 @@ module Puma
|
|
234
257
|
STDERR.puts "Exception handling servers: #{e.message} (#{e.class})"
|
235
258
|
STDERR.puts e.backtrace
|
236
259
|
ensure
|
237
|
-
|
238
|
-
|
260
|
+
begin
|
261
|
+
@check.close
|
262
|
+
rescue
|
263
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
264
|
+
end
|
239
265
|
|
240
|
-
|
241
|
-
|
266
|
+
# Prevent can't modify frozen IOError (RuntimeError)
|
267
|
+
begin
|
268
|
+
@notify.close
|
269
|
+
rescue IOError
|
270
|
+
# no biggy
|
242
271
|
end
|
243
272
|
end
|
244
273
|
|
@@ -288,11 +317,15 @@ module Puma
|
|
288
317
|
|
289
318
|
@events.ssl_error self, addr, cert, e
|
290
319
|
rescue HttpParserError => e
|
291
|
-
client.
|
320
|
+
client.write_error(400)
|
292
321
|
client.close
|
293
322
|
|
294
323
|
@events.parse_error self, client.env, e
|
295
|
-
rescue
|
324
|
+
rescue HttpParserError501 => e
|
325
|
+
client.write_error(501)
|
326
|
+
client.close
|
327
|
+
@events.parse_error self, client.env, e
|
328
|
+
rescue ConnectionError, EOFError
|
296
329
|
client.close
|
297
330
|
else
|
298
331
|
if process_now
|
@@ -322,7 +355,10 @@ module Puma
|
|
322
355
|
@events.fire :state, :running
|
323
356
|
|
324
357
|
if background
|
325
|
-
@thread = Thread.new
|
358
|
+
@thread = Thread.new do
|
359
|
+
Puma.set_thread_name "server"
|
360
|
+
handle_servers
|
361
|
+
end
|
326
362
|
return @thread
|
327
363
|
else
|
328
364
|
handle_servers
|
@@ -363,13 +399,20 @@ module Puma
|
|
363
399
|
end
|
364
400
|
|
365
401
|
pool << client
|
366
|
-
pool.wait_until_not_full
|
402
|
+
busy_threads = pool.wait_until_not_full
|
403
|
+
if busy_threads == 0
|
404
|
+
@options[:out_of_band].each(&:call) if @options[:out_of_band]
|
405
|
+
end
|
367
406
|
end
|
368
407
|
rescue SystemCallError
|
369
408
|
# nothing
|
370
409
|
rescue Errno::ECONNABORTED
|
371
410
|
# client closed the socket even before accept
|
372
|
-
|
411
|
+
begin
|
412
|
+
io.close
|
413
|
+
rescue
|
414
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
415
|
+
end
|
373
416
|
end
|
374
417
|
end
|
375
418
|
end
|
@@ -391,10 +434,6 @@ module Puma
|
|
391
434
|
ensure
|
392
435
|
@check.close
|
393
436
|
@notify.close
|
394
|
-
|
395
|
-
if @status != :restart and @own_binder
|
396
|
-
@binder.close
|
397
|
-
end
|
398
437
|
end
|
399
438
|
|
400
439
|
@events.fire :state, :done
|
@@ -431,6 +470,8 @@ module Puma
|
|
431
470
|
clean_thread_locals = @options[:clean_thread_locals]
|
432
471
|
close_socket = true
|
433
472
|
|
473
|
+
requests = 0
|
474
|
+
|
434
475
|
while true
|
435
476
|
case handle_request(client, buffer)
|
436
477
|
when false
|
@@ -444,7 +485,24 @@ module Puma
|
|
444
485
|
|
445
486
|
ThreadPool.clean_thread_locals if clean_thread_locals
|
446
487
|
|
447
|
-
|
488
|
+
requests += 1
|
489
|
+
|
490
|
+
# Closing keepalive sockets after they've made a reasonable
|
491
|
+
# number of requests allows Puma to service many connections
|
492
|
+
# fairly, even when the number of concurrent connections exceeds
|
493
|
+
# the size of the threadpool. It also allows cluster mode Pumas
|
494
|
+
# to keep load evenly distributed across workers, because clients
|
495
|
+
# are randomly assigned a new worker when opening a new connection.
|
496
|
+
#
|
497
|
+
# Previously, Puma would kick connections in this conditional back
|
498
|
+
# to the reactor. However, because this causes the todo set to increase
|
499
|
+
# in size, the wait_until_full mutex would never unlock, leaving
|
500
|
+
# any additional connections unserviced.
|
501
|
+
break if requests >= MAX_FAST_INLINE
|
502
|
+
|
503
|
+
check_for_more_data = @status == :run
|
504
|
+
|
505
|
+
unless client.reset(check_for_more_data)
|
448
506
|
close_socket = false
|
449
507
|
client.set_timeout @persistent_timeout
|
450
508
|
@reactor.add client
|
@@ -473,15 +531,20 @@ module Puma
|
|
473
531
|
rescue HttpParserError => e
|
474
532
|
lowlevel_error(e, client.env)
|
475
533
|
|
476
|
-
client.
|
534
|
+
client.write_error(400)
|
477
535
|
|
478
536
|
@events.parse_error self, client.env, e
|
537
|
+
rescue HttpParserError501 => e
|
538
|
+
lowlevel_error(e, client.env)
|
539
|
+
|
540
|
+
client.write_error(501)
|
479
541
|
|
542
|
+
@events.parse_error self, client.env, e
|
480
543
|
# Server error
|
481
544
|
rescue StandardError => e
|
482
545
|
lowlevel_error(e, client.env)
|
483
546
|
|
484
|
-
client.
|
547
|
+
client.write_error(500)
|
485
548
|
|
486
549
|
@events.unknown_error self, e, "Read"
|
487
550
|
|
@@ -491,6 +554,7 @@ module Puma
|
|
491
554
|
begin
|
492
555
|
client.close if close_socket
|
493
556
|
rescue IOError, SystemCallError
|
557
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
494
558
|
# Already closed
|
495
559
|
rescue StandardError => e
|
496
560
|
@events.unknown_error self, e, "Client"
|
@@ -522,7 +586,9 @@ module Puma
|
|
522
586
|
|
523
587
|
raise "No REQUEST PATH" unless env[REQUEST_PATH]
|
524
588
|
|
525
|
-
env
|
589
|
+
# A nil env value will cause a LintError (and fatal errors elsewhere),
|
590
|
+
# so only set the env value if there actually is a value.
|
591
|
+
env[QUERY_STRING] = uri.query if uri.query
|
526
592
|
end
|
527
593
|
|
528
594
|
env[PATH_INFO] = env[REQUEST_PATH]
|
@@ -553,19 +619,26 @@ module Puma
|
|
553
619
|
end
|
554
620
|
|
555
621
|
def default_server_port(env)
|
556
|
-
|
557
|
-
|
622
|
+
if ['on', HTTPS].include?(env[HTTPS_KEY]) || env[HTTP_X_FORWARDED_PROTO].to_s[0...5] == HTTPS || env[HTTP_X_FORWARDED_SCHEME] == HTTPS || env[HTTP_X_FORWARDED_SSL] == "on"
|
623
|
+
PORT_443
|
624
|
+
else
|
625
|
+
PORT_80
|
626
|
+
end
|
558
627
|
end
|
559
628
|
|
560
|
-
#
|
561
|
-
#
|
562
|
-
#
|
563
|
-
# +
|
629
|
+
# Takes the request +req+, invokes the Rack application to construct
|
630
|
+
# the response and writes it back to +req.io+.
|
631
|
+
#
|
632
|
+
# The second parameter +lines+ is a IO-like object unique to this thread.
|
633
|
+
# This is normally an instance of Puma::IOBuffer.
|
634
|
+
#
|
635
|
+
# It'll return +false+ when the connection is closed, this doesn't mean
|
636
|
+
# that the response wasn't successful.
|
564
637
|
#
|
565
|
-
# +
|
566
|
-
#
|
567
|
-
# it up again.
|
638
|
+
# It'll return +:async+ if the connection remains open but will be handled
|
639
|
+
# elsewhere, i.e. the connection has been hijacked by the Rack application.
|
568
640
|
#
|
641
|
+
# Finally, it'll return +true+ on keep-alive connections.
|
569
642
|
def handle_request(req, lines)
|
570
643
|
env = req.env
|
571
644
|
client = req.io
|
@@ -588,7 +661,61 @@ module Puma
|
|
588
661
|
head = env[REQUEST_METHOD] == HEAD
|
589
662
|
|
590
663
|
env[RACK_INPUT] = body
|
591
|
-
env[RACK_URL_SCHEME] =
|
664
|
+
env[RACK_URL_SCHEME] = default_server_port(env) == PORT_443 ? HTTPS : HTTP
|
665
|
+
|
666
|
+
if @early_hints
|
667
|
+
env[EARLY_HINTS] = lambda { |headers|
|
668
|
+
begin
|
669
|
+
fast_write client, "HTTP/1.1 103 Early Hints\r\n".freeze
|
670
|
+
|
671
|
+
headers.each_pair do |k, vs|
|
672
|
+
if vs.respond_to?(:to_s) && !vs.to_s.empty?
|
673
|
+
vs.to_s.split(NEWLINE).each do |v|
|
674
|
+
next if possible_header_injection?(v)
|
675
|
+
fast_write client, "#{k}: #{v}\r\n"
|
676
|
+
end
|
677
|
+
else
|
678
|
+
fast_write client, "#{k}: #{vs}\r\n"
|
679
|
+
end
|
680
|
+
end
|
681
|
+
|
682
|
+
fast_write client, "\r\n".freeze
|
683
|
+
rescue ConnectionError
|
684
|
+
# noop, if we lost the socket we just won't send the early hints
|
685
|
+
end
|
686
|
+
}
|
687
|
+
end
|
688
|
+
|
689
|
+
# Fixup any headers with , in the name to have _ now. We emit
|
690
|
+
# headers with , in them during the parse phase to avoid ambiguity
|
691
|
+
# with the - to _ conversion for critical headers. But here for
|
692
|
+
# compatibility, we'll convert them back. This code is written to
|
693
|
+
# avoid allocation in the common case (ie there are no headers
|
694
|
+
# with , in their names), that's why it has the extra conditionals.
|
695
|
+
|
696
|
+
to_delete = nil
|
697
|
+
to_add = nil
|
698
|
+
|
699
|
+
env.each do |k,v|
|
700
|
+
if k.start_with?("HTTP_") and k.include?(",") and k != "HTTP_TRANSFER,ENCODING"
|
701
|
+
if to_delete
|
702
|
+
to_delete << k
|
703
|
+
else
|
704
|
+
to_delete = [k]
|
705
|
+
end
|
706
|
+
|
707
|
+
unless to_add
|
708
|
+
to_add = {}
|
709
|
+
end
|
710
|
+
|
711
|
+
to_add[k.tr(",", "_")] = v
|
712
|
+
end
|
713
|
+
end
|
714
|
+
|
715
|
+
if to_delete
|
716
|
+
to_delete.each { |k| env.delete(k) }
|
717
|
+
env.merge! to_add
|
718
|
+
end
|
592
719
|
|
593
720
|
# A rack extension. If the app writes #call'ables to this
|
594
721
|
# array, we will invoke them when the request is done.
|
@@ -677,6 +804,7 @@ module Puma
|
|
677
804
|
headers.each do |k, vs|
|
678
805
|
case k.downcase
|
679
806
|
when CONTENT_LENGTH2
|
807
|
+
next if possible_header_injection?(vs)
|
680
808
|
content_length = vs
|
681
809
|
next
|
682
810
|
when TRANSFER_ENCODING
|
@@ -687,8 +815,9 @@ module Puma
|
|
687
815
|
next
|
688
816
|
end
|
689
817
|
|
690
|
-
if vs.respond_to?(:to_s)
|
818
|
+
if vs.respond_to?(:to_s) && !vs.to_s.empty?
|
691
819
|
vs.to_s.split(NEWLINE).each do |v|
|
820
|
+
next if possible_header_injection?(v)
|
692
821
|
lines.append k, colon, v, line_ending
|
693
822
|
end
|
694
823
|
else
|
@@ -731,8 +860,8 @@ module Puma
|
|
731
860
|
|
732
861
|
begin
|
733
862
|
res_body.each do |part|
|
863
|
+
next if part.bytesize.zero?
|
734
864
|
if chunked
|
735
|
-
next if part.bytesize.zero?
|
736
865
|
fast_write client, part.bytesize.to_s(16)
|
737
866
|
fast_write client, line_ending
|
738
867
|
fast_write client, part
|
@@ -753,11 +882,14 @@ module Puma
|
|
753
882
|
end
|
754
883
|
|
755
884
|
ensure
|
756
|
-
|
885
|
+
begin
|
886
|
+
uncork_socket client
|
757
887
|
|
758
|
-
|
759
|
-
|
760
|
-
|
888
|
+
body.close
|
889
|
+
req.tempfile.unlink if req.tempfile
|
890
|
+
ensure
|
891
|
+
res_body.close if res_body.respond_to? :close
|
892
|
+
end
|
761
893
|
|
762
894
|
after_reply.each { |o| o.call }
|
763
895
|
end
|
@@ -882,6 +1014,10 @@ module Puma
|
|
882
1014
|
@events.debug "Drained #{count} additional connections."
|
883
1015
|
end
|
884
1016
|
|
1017
|
+
if @status != :restart
|
1018
|
+
@binder.close
|
1019
|
+
end
|
1020
|
+
|
885
1021
|
if @thread_pool
|
886
1022
|
if timeout = @options[:force_shutdown_after]
|
887
1023
|
@thread_pool.shutdown timeout.to_i
|
@@ -891,35 +1027,38 @@ module Puma
|
|
891
1027
|
end
|
892
1028
|
end
|
893
1029
|
|
894
|
-
|
895
|
-
# off the request queue before finally exiting.
|
896
|
-
#
|
897
|
-
def stop(sync=false)
|
1030
|
+
def notify_safely(message)
|
898
1031
|
begin
|
899
|
-
@notify <<
|
1032
|
+
@notify << message
|
900
1033
|
rescue IOError
|
901
|
-
|
1034
|
+
# The server, in another thread, is shutting down
|
1035
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
1036
|
+
rescue RuntimeError => e
|
1037
|
+
# Temporary workaround for https://bugs.ruby-lang.org/issues/13239
|
1038
|
+
if e.message.include?('IOError')
|
1039
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
1040
|
+
else
|
1041
|
+
raise e
|
1042
|
+
end
|
902
1043
|
end
|
1044
|
+
end
|
1045
|
+
private :notify_safely
|
903
1046
|
|
1047
|
+
# Stops the acceptor thread and then causes the worker threads to finish
|
1048
|
+
# off the request queue before finally exiting.
|
1049
|
+
|
1050
|
+
def stop(sync=false)
|
1051
|
+
notify_safely(STOP_COMMAND)
|
904
1052
|
@thread.join if @thread && sync
|
905
1053
|
end
|
906
1054
|
|
907
1055
|
def halt(sync=false)
|
908
|
-
|
909
|
-
@notify << HALT_COMMAND
|
910
|
-
rescue IOError
|
911
|
-
# The server, in another thread, is shutting down
|
912
|
-
end
|
913
|
-
|
1056
|
+
notify_safely(HALT_COMMAND)
|
914
1057
|
@thread.join if @thread && sync
|
915
1058
|
end
|
916
1059
|
|
917
1060
|
def begin_restart
|
918
|
-
|
919
|
-
@notify << RESTART_COMMAND
|
920
|
-
rescue IOError
|
921
|
-
# The server, in another thread, is shutting down
|
922
|
-
end
|
1061
|
+
notify_safely(RESTART_COMMAND)
|
923
1062
|
end
|
924
1063
|
|
925
1064
|
def fast_write(io, str)
|
@@ -952,5 +1091,10 @@ module Puma
|
|
952
1091
|
def shutting_down?
|
953
1092
|
@status == :stop || @status == :restart
|
954
1093
|
end
|
1094
|
+
|
1095
|
+
def possible_header_injection?(header_value)
|
1096
|
+
HTTP_INJECTION_REGEX =~ header_value.to_s
|
1097
|
+
end
|
1098
|
+
private :possible_header_injection?
|
955
1099
|
end
|
956
1100
|
end
|
data/lib/puma/single.rb
CHANGED
@@ -1,13 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'puma/runner'
|
2
4
|
require 'puma/detect'
|
3
5
|
require 'puma/plugin'
|
4
6
|
|
5
7
|
module Puma
|
8
|
+
# This class is instantiated by the `Puma::Launcher` and used
|
9
|
+
# to boot and serve a Ruby application when no puma "workers" are needed
|
10
|
+
# i.e. only using "threaded" mode. For example `$ puma -t 1:5`
|
11
|
+
#
|
12
|
+
# At the core of this class is running an instance of `Puma::Server` which
|
13
|
+
# gets created via the `start_server` method from the `Puma::Runner` class
|
14
|
+
# that this inherits from.
|
6
15
|
class Single < Runner
|
7
16
|
def stats
|
8
|
-
b = @server.backlog
|
9
|
-
r = @server.running
|
10
|
-
|
17
|
+
b = @server.backlog || 0
|
18
|
+
r = @server.running || 0
|
19
|
+
t = @server.pool_capacity || 0
|
20
|
+
m = @server.max_threads || 0
|
21
|
+
%Q!{ "started_at": "#{@started_at.utc.iso8601}", "backlog": #{b}, "running": #{r}, "pool_capacity": #{t}, "max_threads": #{m} }!
|
11
22
|
end
|
12
23
|
|
13
24
|
def restart
|
@@ -15,7 +26,7 @@ module Puma
|
|
15
26
|
end
|
16
27
|
|
17
28
|
def stop
|
18
|
-
@server.stop
|
29
|
+
@server.stop(false) if @server
|
19
30
|
end
|
20
31
|
|
21
32
|
def halt
|
@@ -25,7 +36,7 @@ module Puma
|
|
25
36
|
def stop_blocked
|
26
37
|
log "- Gracefully stopping, waiting for requests to finish"
|
27
38
|
@control.stop(true) if @control
|
28
|
-
@server.stop(true)
|
39
|
+
@server.stop(true) if @server
|
29
40
|
end
|
30
41
|
|
31
42
|
def jruby_daemon?
|
data/lib/puma/state_file.rb
CHANGED