puma 2.16.0 → 3.11.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/{History.txt → History.md} +489 -70
- data/README.md +143 -174
- data/docs/architecture.md +36 -0
- data/{DEPLOYMENT.md → docs/deployment.md} +1 -1
- 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 +2 -2
- data/docs/plugins.md +28 -0
- data/docs/restart.md +39 -0
- data/docs/signals.md +56 -3
- data/docs/systemd.md +272 -0
- data/ext/puma_http11/extconf.rb +2 -0
- data/ext/puma_http11/http11_parser.c +291 -447
- data/ext/puma_http11/http11_parser.h +1 -0
- data/ext/puma_http11/http11_parser.java.rl +5 -5
- data/ext/puma_http11/http11_parser.rl +10 -9
- data/ext/puma_http11/http11_parser_common.rl +1 -1
- data/ext/puma_http11/io_buffer.c +8 -8
- data/ext/puma_http11/mini_ssl.c +64 -6
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +113 -131
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +9 -2
- data/ext/puma_http11/puma_http11.c +1 -0
- data/lib/puma/app/status.rb +9 -1
- data/lib/puma/binder.rb +90 -38
- data/lib/puma/cli.rb +134 -491
- data/lib/puma/client.rb +142 -4
- data/lib/puma/cluster.rb +132 -76
- data/lib/puma/commonlogger.rb +19 -20
- data/lib/puma/compat.rb +3 -7
- data/lib/puma/configuration.rb +206 -67
- data/lib/puma/const.rb +21 -31
- data/lib/puma/control_cli.rb +92 -103
- data/lib/puma/convenient.rb +23 -0
- data/lib/puma/daemon_ext.rb +6 -0
- data/lib/puma/detect.rb +10 -1
- data/lib/puma/dsl.rb +203 -45
- data/lib/puma/events.rb +22 -13
- data/lib/puma/io_buffer.rb +1 -1
- data/lib/puma/jruby_restart.rb +1 -2
- data/lib/puma/launcher.rb +431 -0
- data/lib/puma/minissl.rb +83 -4
- data/lib/puma/null_io.rb +19 -11
- data/lib/puma/plugin/tmp_restart.rb +34 -0
- data/lib/puma/plugin.rb +115 -0
- data/lib/puma/rack/backports/uri/common_193.rb +17 -13
- data/lib/puma/rack/builder.rb +3 -0
- data/lib/puma/rack/urlmap.rb +9 -8
- data/lib/puma/reactor.rb +18 -0
- data/lib/puma/runner.rb +43 -15
- data/lib/puma/server.rb +141 -35
- data/lib/puma/single.rb +16 -6
- data/lib/puma/state_file.rb +29 -0
- data/lib/puma/tcp_logger.rb +8 -1
- data/lib/puma/thread_pool.rb +60 -10
- data/lib/puma/util.rb +1 -5
- data/lib/puma.rb +13 -4
- data/lib/rack/handler/puma.rb +76 -29
- data/tools/jungle/README.md +12 -2
- data/tools/jungle/init.d/README.md +9 -2
- data/tools/jungle/init.d/puma +86 -59
- data/tools/jungle/init.d/run-puma +16 -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/puma.conf +1 -1
- data/tools/trickletest.rb +1 -1
- metadata +28 -95
- data/COPYING +0 -55
- data/Gemfile +0 -13
- data/Manifest.txt +0 -74
- data/Rakefile +0 -158
- data/docs/config.md +0 -0
- data/lib/puma/capistrano.rb +0 -94
- data/lib/puma/rack/backports/uri/common_18.rb +0 -56
- data/lib/puma/rack/backports/uri/common_192.rb +0 -52
- data/puma.gemspec +0 -52
data/lib/puma/server.rb
CHANGED
|
@@ -57,19 +57,19 @@ module Puma
|
|
|
57
57
|
|
|
58
58
|
@min_threads = 0
|
|
59
59
|
@max_threads = 16
|
|
60
|
-
@auto_trim_time =
|
|
60
|
+
@auto_trim_time = 30
|
|
61
61
|
@reaping_time = 1
|
|
62
62
|
|
|
63
63
|
@thread = nil
|
|
64
64
|
@thread_pool = nil
|
|
65
|
+
@early_hints = nil
|
|
65
66
|
|
|
66
|
-
@persistent_timeout = PERSISTENT_TIMEOUT
|
|
67
|
+
@persistent_timeout = options.fetch(:persistent_timeout, PERSISTENT_TIMEOUT)
|
|
68
|
+
@first_data_timeout = options.fetch(:first_data_timeout, FIRST_DATA_TIMEOUT)
|
|
67
69
|
|
|
68
70
|
@binder = Binder.new(events)
|
|
69
71
|
@own_binder = true
|
|
70
72
|
|
|
71
|
-
@first_data_timeout = FIRST_DATA_TIMEOUT
|
|
72
|
-
|
|
73
73
|
@leak_stack_on_error = true
|
|
74
74
|
|
|
75
75
|
@options = options
|
|
@@ -78,13 +78,16 @@ module Puma
|
|
|
78
78
|
ENV['RACK_ENV'] ||= "development"
|
|
79
79
|
|
|
80
80
|
@mode = :http
|
|
81
|
+
|
|
82
|
+
@precheck_closing = true
|
|
81
83
|
end
|
|
82
84
|
|
|
83
|
-
attr_accessor :binder, :leak_stack_on_error
|
|
85
|
+
attr_accessor :binder, :leak_stack_on_error, :early_hints
|
|
84
86
|
|
|
85
87
|
forward :add_tcp_listener, :@binder
|
|
86
88
|
forward :add_ssl_listener, :@binder
|
|
87
89
|
forward :add_unix_listener, :@binder
|
|
90
|
+
forward :connected_port, :@binder
|
|
88
91
|
|
|
89
92
|
def inherit_binder(bind)
|
|
90
93
|
@binder = bind
|
|
@@ -99,6 +102,8 @@ module Puma
|
|
|
99
102
|
# packetizes our stream. This improves both latency and throughput.
|
|
100
103
|
#
|
|
101
104
|
if RUBY_PLATFORM =~ /linux/
|
|
105
|
+
UNPACK_TCP_STATE_FROM_TCP_INFO = "C".freeze
|
|
106
|
+
|
|
102
107
|
# 6 == Socket::IPPROTO_TCP
|
|
103
108
|
# 3 == TCP_CORK
|
|
104
109
|
# 1/0 == turn on/off
|
|
@@ -106,6 +111,7 @@ module Puma
|
|
|
106
111
|
begin
|
|
107
112
|
socket.setsockopt(6, 3, 1) if socket.kind_of? TCPSocket
|
|
108
113
|
rescue IOError, SystemCallError
|
|
114
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
|
109
115
|
end
|
|
110
116
|
end
|
|
111
117
|
|
|
@@ -113,6 +119,24 @@ module Puma
|
|
|
113
119
|
begin
|
|
114
120
|
socket.setsockopt(6, 3, 0) if socket.kind_of? TCPSocket
|
|
115
121
|
rescue IOError, SystemCallError
|
|
122
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def closed_socket?(socket)
|
|
127
|
+
return false unless socket.kind_of? TCPSocket
|
|
128
|
+
return false unless @precheck_closing
|
|
129
|
+
|
|
130
|
+
begin
|
|
131
|
+
tcp_info = socket.getsockopt(Socket::SOL_TCP, Socket::TCP_INFO)
|
|
132
|
+
rescue IOError, SystemCallError
|
|
133
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
|
134
|
+
@precheck_closing = false
|
|
135
|
+
false
|
|
136
|
+
else
|
|
137
|
+
state = tcp_info.unpack(UNPACK_TCP_STATE_FROM_TCP_INFO)[0]
|
|
138
|
+
# TIME_WAIT: 6, CLOSE: 7, CLOSE_WAIT: 8, LAST_ACK: 9, CLOSING: 11
|
|
139
|
+
(state >= 6 && state <= 9) || state == 11
|
|
116
140
|
end
|
|
117
141
|
end
|
|
118
142
|
else
|
|
@@ -121,6 +145,10 @@ module Puma
|
|
|
121
145
|
|
|
122
146
|
def uncork_socket(socket)
|
|
123
147
|
end
|
|
148
|
+
|
|
149
|
+
def closed_socket?(socket)
|
|
150
|
+
false
|
|
151
|
+
end
|
|
124
152
|
end
|
|
125
153
|
|
|
126
154
|
def backlog
|
|
@@ -192,7 +220,11 @@ module Puma
|
|
|
192
220
|
# nothing
|
|
193
221
|
rescue Errno::ECONNABORTED
|
|
194
222
|
# client closed the socket even before accept
|
|
195
|
-
|
|
223
|
+
begin
|
|
224
|
+
io.close
|
|
225
|
+
rescue
|
|
226
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
|
227
|
+
end
|
|
196
228
|
end
|
|
197
229
|
end
|
|
198
230
|
end
|
|
@@ -241,6 +273,10 @@ module Puma
|
|
|
241
273
|
@thread_pool = ThreadPool.new(@min_threads,
|
|
242
274
|
@max_threads,
|
|
243
275
|
IOBuffer) do |client, buffer|
|
|
276
|
+
|
|
277
|
+
# Advertise this server into the thread
|
|
278
|
+
Thread.current[ThreadLocalKey] = self
|
|
279
|
+
|
|
244
280
|
process_now = false
|
|
245
281
|
|
|
246
282
|
begin
|
|
@@ -263,7 +299,7 @@ module Puma
|
|
|
263
299
|
client.close
|
|
264
300
|
|
|
265
301
|
@events.parse_error self, client.env, e
|
|
266
|
-
rescue ConnectionError
|
|
302
|
+
rescue ConnectionError, EOFError
|
|
267
303
|
client.close
|
|
268
304
|
else
|
|
269
305
|
if process_now
|
|
@@ -334,13 +370,17 @@ module Puma
|
|
|
334
370
|
end
|
|
335
371
|
|
|
336
372
|
pool << client
|
|
337
|
-
pool.wait_until_not_full
|
|
373
|
+
pool.wait_until_not_full
|
|
338
374
|
end
|
|
339
375
|
rescue SystemCallError
|
|
340
376
|
# nothing
|
|
341
377
|
rescue Errno::ECONNABORTED
|
|
342
378
|
# client closed the socket even before accept
|
|
343
|
-
|
|
379
|
+
begin
|
|
380
|
+
io.close
|
|
381
|
+
rescue
|
|
382
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
|
383
|
+
end
|
|
344
384
|
end
|
|
345
385
|
end
|
|
346
386
|
end
|
|
@@ -353,7 +393,7 @@ module Puma
|
|
|
353
393
|
|
|
354
394
|
graceful_shutdown if @status == :stop || @status == :restart
|
|
355
395
|
if queue_requests
|
|
356
|
-
@reactor.clear!
|
|
396
|
+
@reactor.clear!
|
|
357
397
|
@reactor.shutdown
|
|
358
398
|
end
|
|
359
399
|
rescue Exception => e
|
|
@@ -398,6 +438,8 @@ module Puma
|
|
|
398
438
|
#
|
|
399
439
|
def process_client(client, buffer)
|
|
400
440
|
begin
|
|
441
|
+
|
|
442
|
+
clean_thread_locals = @options[:clean_thread_locals]
|
|
401
443
|
close_socket = true
|
|
402
444
|
|
|
403
445
|
while true
|
|
@@ -411,6 +453,8 @@ module Puma
|
|
|
411
453
|
return unless @queue_requests
|
|
412
454
|
buffer.reset
|
|
413
455
|
|
|
456
|
+
ThreadPool.clean_thread_locals if clean_thread_locals
|
|
457
|
+
|
|
414
458
|
unless client.reset(@status == :run)
|
|
415
459
|
close_socket = false
|
|
416
460
|
client.set_timeout @persistent_timeout
|
|
@@ -426,6 +470,8 @@ module Puma
|
|
|
426
470
|
|
|
427
471
|
# SSL handshake error
|
|
428
472
|
rescue MiniSSL::SSLError => e
|
|
473
|
+
lowlevel_error(e, client.env)
|
|
474
|
+
|
|
429
475
|
ssl_socket = client.io
|
|
430
476
|
addr = ssl_socket.peeraddr.last
|
|
431
477
|
cert = ssl_socket.peercert
|
|
@@ -436,12 +482,16 @@ module Puma
|
|
|
436
482
|
|
|
437
483
|
# The client doesn't know HTTP well
|
|
438
484
|
rescue HttpParserError => e
|
|
485
|
+
lowlevel_error(e, client.env)
|
|
486
|
+
|
|
439
487
|
client.write_400
|
|
440
488
|
|
|
441
489
|
@events.parse_error self, client.env, e
|
|
442
490
|
|
|
443
491
|
# Server error
|
|
444
492
|
rescue StandardError => e
|
|
493
|
+
lowlevel_error(e, client.env)
|
|
494
|
+
|
|
445
495
|
client.write_500
|
|
446
496
|
|
|
447
497
|
@events.unknown_error self, e, "Read"
|
|
@@ -452,6 +502,7 @@ module Puma
|
|
|
452
502
|
begin
|
|
453
503
|
client.close if close_socket
|
|
454
504
|
rescue IOError, SystemCallError
|
|
505
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
|
455
506
|
# Already closed
|
|
456
507
|
rescue StandardError => e
|
|
457
508
|
@events.unknown_error self, e, "Client"
|
|
@@ -482,6 +533,10 @@ module Puma
|
|
|
482
533
|
env[REQUEST_PATH] = uri.path
|
|
483
534
|
|
|
484
535
|
raise "No REQUEST PATH" unless env[REQUEST_PATH]
|
|
536
|
+
|
|
537
|
+
# A nil env value will cause a LintError (and fatal errors elsewhere),
|
|
538
|
+
# so only set the env value if there actually is a value.
|
|
539
|
+
env[QUERY_STRING] = uri.query if uri.query
|
|
485
540
|
end
|
|
486
541
|
|
|
487
542
|
env[PATH_INFO] = env[REQUEST_PATH]
|
|
@@ -529,6 +584,8 @@ module Puma
|
|
|
529
584
|
env = req.env
|
|
530
585
|
client = req.io
|
|
531
586
|
|
|
587
|
+
return false if closed_socket?(client)
|
|
588
|
+
|
|
532
589
|
normalize_env env, req
|
|
533
590
|
|
|
534
591
|
env[PUMA_SOCKET] = client
|
|
@@ -547,6 +604,24 @@ module Puma
|
|
|
547
604
|
env[RACK_INPUT] = body
|
|
548
605
|
env[RACK_URL_SCHEME] = env[HTTPS_KEY] ? HTTPS : HTTP
|
|
549
606
|
|
|
607
|
+
if @early_hints
|
|
608
|
+
env[EARLY_HINTS] = lambda { |headers|
|
|
609
|
+
fast_write client, "HTTP/1.1 103 Early Hints\r\n".freeze
|
|
610
|
+
|
|
611
|
+
headers.each_pair do |k, vs|
|
|
612
|
+
if vs.respond_to?(:to_s) && !vs.to_s.empty?
|
|
613
|
+
vs.to_s.split(NEWLINE).each do |v|
|
|
614
|
+
fast_write client, "#{k}: #{v}\r\n"
|
|
615
|
+
end
|
|
616
|
+
else
|
|
617
|
+
fast_write client, "#{k}: #{vs}\r\n"
|
|
618
|
+
end
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
fast_write client, "\r\n".freeze
|
|
622
|
+
}
|
|
623
|
+
end
|
|
624
|
+
|
|
550
625
|
# A rack extension. If the app writes #call'ables to this
|
|
551
626
|
# array, we will invoke them when the request is done.
|
|
552
627
|
#
|
|
@@ -567,10 +642,18 @@ module Puma
|
|
|
567
642
|
|
|
568
643
|
return :async
|
|
569
644
|
end
|
|
570
|
-
rescue
|
|
645
|
+
rescue ThreadPool::ForceShutdown => e
|
|
646
|
+
@events.log "Detected force shutdown of a thread, returning 503"
|
|
571
647
|
@events.unknown_error self, e, "Rack app"
|
|
572
648
|
|
|
573
|
-
status
|
|
649
|
+
status = 503
|
|
650
|
+
headers = {}
|
|
651
|
+
res_body = ["Request was internally terminated early\n"]
|
|
652
|
+
|
|
653
|
+
rescue Exception => e
|
|
654
|
+
@events.unknown_error self, e, "Rack app", env
|
|
655
|
+
|
|
656
|
+
status, headers, res_body = lowlevel_error(e, env)
|
|
574
657
|
end
|
|
575
658
|
|
|
576
659
|
content_length = nil
|
|
@@ -636,7 +719,7 @@ module Puma
|
|
|
636
719
|
next
|
|
637
720
|
end
|
|
638
721
|
|
|
639
|
-
if vs.respond_to?(:to_s)
|
|
722
|
+
if vs.respond_to?(:to_s) && !vs.to_s.empty?
|
|
640
723
|
vs.to_s.split(NEWLINE).each do |v|
|
|
641
724
|
lines.append k, colon, v, line_ending
|
|
642
725
|
end
|
|
@@ -680,8 +763,8 @@ module Puma
|
|
|
680
763
|
|
|
681
764
|
begin
|
|
682
765
|
res_body.each do |part|
|
|
766
|
+
next if part.bytesize.zero?
|
|
683
767
|
if chunked
|
|
684
|
-
next if part.bytesize.zero?
|
|
685
768
|
fast_write client, part.bytesize.to_s(16)
|
|
686
769
|
fast_write client, line_ending
|
|
687
770
|
fast_write client, part
|
|
@@ -719,7 +802,7 @@ module Puma
|
|
|
719
802
|
end
|
|
720
803
|
private :fetch_status_code
|
|
721
804
|
|
|
722
|
-
# Given the
|
|
805
|
+
# Given the request +env+ from +client+ and the partial body +body+
|
|
723
806
|
# plus a potential Content-Length value +cl+, finish reading
|
|
724
807
|
# the body and return it.
|
|
725
808
|
#
|
|
@@ -775,9 +858,13 @@ module Puma
|
|
|
775
858
|
|
|
776
859
|
# A fallback rack response if +@app+ raises as exception.
|
|
777
860
|
#
|
|
778
|
-
def lowlevel_error(e)
|
|
861
|
+
def lowlevel_error(e, env)
|
|
779
862
|
if handler = @options[:lowlevel_error_handler]
|
|
780
|
-
|
|
863
|
+
if handler.arity == 1
|
|
864
|
+
return handler.call(e)
|
|
865
|
+
else
|
|
866
|
+
return handler.call(e, env)
|
|
867
|
+
end
|
|
781
868
|
end
|
|
782
869
|
|
|
783
870
|
if @leak_stack_on_error
|
|
@@ -827,38 +914,47 @@ module Puma
|
|
|
827
914
|
@events.debug "Drained #{count} additional connections."
|
|
828
915
|
end
|
|
829
916
|
|
|
830
|
-
|
|
917
|
+
if @thread_pool
|
|
918
|
+
if timeout = @options[:force_shutdown_after]
|
|
919
|
+
@thread_pool.shutdown timeout.to_i
|
|
920
|
+
else
|
|
921
|
+
@thread_pool.shutdown
|
|
922
|
+
end
|
|
923
|
+
end
|
|
831
924
|
end
|
|
832
925
|
|
|
833
|
-
|
|
834
|
-
# off the request queue before finally exiting.
|
|
835
|
-
#
|
|
836
|
-
def stop(sync=false)
|
|
926
|
+
def notify_safely(message)
|
|
837
927
|
begin
|
|
838
|
-
@notify <<
|
|
928
|
+
@notify << message
|
|
839
929
|
rescue IOError
|
|
840
|
-
|
|
930
|
+
# The server, in another thread, is shutting down
|
|
931
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
|
932
|
+
rescue RuntimeError => e
|
|
933
|
+
# Temporary workaround for https://bugs.ruby-lang.org/issues/13239
|
|
934
|
+
if e.message.include?('IOError')
|
|
935
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
|
936
|
+
else
|
|
937
|
+
raise e
|
|
938
|
+
end
|
|
841
939
|
end
|
|
940
|
+
end
|
|
941
|
+
private :notify_safely
|
|
842
942
|
|
|
943
|
+
# Stops the acceptor thread and then causes the worker threads to finish
|
|
944
|
+
# off the request queue before finally exiting.
|
|
945
|
+
|
|
946
|
+
def stop(sync=false)
|
|
947
|
+
notify_safely(STOP_COMMAND)
|
|
843
948
|
@thread.join if @thread && sync
|
|
844
949
|
end
|
|
845
950
|
|
|
846
951
|
def halt(sync=false)
|
|
847
|
-
|
|
848
|
-
@notify << HALT_COMMAND
|
|
849
|
-
rescue IOError
|
|
850
|
-
# The server, in another thread, is shutting down
|
|
851
|
-
end
|
|
852
|
-
|
|
952
|
+
notify_safely(HALT_COMMAND)
|
|
853
953
|
@thread.join if @thread && sync
|
|
854
954
|
end
|
|
855
955
|
|
|
856
956
|
def begin_restart
|
|
857
|
-
|
|
858
|
-
@notify << RESTART_COMMAND
|
|
859
|
-
rescue IOError
|
|
860
|
-
# The server, in another thread, is shutting down
|
|
861
|
-
end
|
|
957
|
+
notify_safely(RESTART_COMMAND)
|
|
862
958
|
end
|
|
863
959
|
|
|
864
960
|
def fast_write(io, str)
|
|
@@ -881,5 +977,15 @@ module Puma
|
|
|
881
977
|
end
|
|
882
978
|
end
|
|
883
979
|
private :fast_write
|
|
980
|
+
|
|
981
|
+
ThreadLocalKey = :puma_server
|
|
982
|
+
|
|
983
|
+
def self.current
|
|
984
|
+
Thread.current[ThreadLocalKey]
|
|
985
|
+
end
|
|
986
|
+
|
|
987
|
+
def shutting_down?
|
|
988
|
+
@status == :stop || @status == :restart
|
|
989
|
+
end
|
|
884
990
|
end
|
|
885
991
|
end
|
data/lib/puma/single.rb
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
require 'puma/runner'
|
|
2
|
+
require 'puma/detect'
|
|
3
|
+
require 'puma/plugin'
|
|
2
4
|
|
|
3
5
|
module Puma
|
|
4
6
|
class Single < Runner
|
|
5
7
|
def stats
|
|
6
|
-
b = @server.backlog
|
|
7
|
-
r = @server.running
|
|
8
|
+
b = @server.backlog || 0
|
|
9
|
+
r = @server.running || 0
|
|
8
10
|
%Q!{ "backlog": #{b}, "running": #{r} }!
|
|
9
11
|
end
|
|
10
12
|
|
|
@@ -27,7 +29,12 @@ module Puma
|
|
|
27
29
|
end
|
|
28
30
|
|
|
29
31
|
def jruby_daemon?
|
|
30
|
-
daemon? and
|
|
32
|
+
daemon? and Puma.jruby?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def jruby_daemon_start
|
|
36
|
+
require 'puma/jruby_restart'
|
|
37
|
+
JRubyRestart.daemon_start(@restart_dir, @launcher.restart_args)
|
|
31
38
|
end
|
|
32
39
|
|
|
33
40
|
def run
|
|
@@ -39,6 +46,7 @@ module Puma
|
|
|
39
46
|
if JRubyRestart.daemon?
|
|
40
47
|
# load and bind before redirecting IO so errors show up on stdout/stderr
|
|
41
48
|
load_and_bind
|
|
49
|
+
redirect_io
|
|
42
50
|
end
|
|
43
51
|
|
|
44
52
|
already_daemon = JRubyRestart.daemon_init
|
|
@@ -66,7 +74,7 @@ module Puma
|
|
|
66
74
|
exit 1
|
|
67
75
|
end
|
|
68
76
|
|
|
69
|
-
|
|
77
|
+
jruby_daemon_start
|
|
70
78
|
sleep
|
|
71
79
|
end
|
|
72
80
|
else
|
|
@@ -79,7 +87,9 @@ module Puma
|
|
|
79
87
|
load_and_bind
|
|
80
88
|
end
|
|
81
89
|
|
|
82
|
-
|
|
90
|
+
Plugins.fire_background
|
|
91
|
+
|
|
92
|
+
@launcher.write_state
|
|
83
93
|
|
|
84
94
|
start_control
|
|
85
95
|
|
|
@@ -90,7 +100,7 @@ module Puma
|
|
|
90
100
|
redirect_io
|
|
91
101
|
end
|
|
92
102
|
|
|
93
|
-
@
|
|
103
|
+
@launcher.events.fire_on_booted!
|
|
94
104
|
|
|
95
105
|
begin
|
|
96
106
|
server.run.join
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
|
|
3
|
+
module Puma
|
|
4
|
+
class StateFile
|
|
5
|
+
def initialize
|
|
6
|
+
@options = {}
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def save(path)
|
|
10
|
+
File.write path, YAML.dump(@options)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def load(path)
|
|
14
|
+
@options = YAML.load File.read(path)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
FIELDS = %w!control_url control_auth_token pid!
|
|
18
|
+
|
|
19
|
+
FIELDS.each do |f|
|
|
20
|
+
define_method f do
|
|
21
|
+
@options[f]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
define_method "#{f}=" do |v|
|
|
25
|
+
@options[f] = v
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
data/lib/puma/tcp_logger.rb
CHANGED
|
@@ -11,7 +11,14 @@ module Puma
|
|
|
11
11
|
def log(who, str)
|
|
12
12
|
now = Time.now.strftime("%d/%b/%Y %H:%M:%S")
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
log_str = "#{now} - #{who} - #{str}"
|
|
15
|
+
|
|
16
|
+
case @logger
|
|
17
|
+
when IO
|
|
18
|
+
@logger.puts log_str
|
|
19
|
+
when Events
|
|
20
|
+
@logger.log log_str
|
|
21
|
+
end
|
|
15
22
|
end
|
|
16
23
|
|
|
17
24
|
def call(env, socket)
|
data/lib/puma/thread_pool.rb
CHANGED
|
@@ -4,6 +4,13 @@ module Puma
|
|
|
4
4
|
# A simple thread pool management object.
|
|
5
5
|
#
|
|
6
6
|
class ThreadPool
|
|
7
|
+
class ForceShutdown < RuntimeError
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# How long, after raising the ForceShutdown of a thread during
|
|
11
|
+
# forced shutdown mode, to wait for the thread to try and finish
|
|
12
|
+
# up its work before leaving the thread to die on the vine.
|
|
13
|
+
SHUTDOWN_GRACE_TIME = 5 # seconds
|
|
7
14
|
|
|
8
15
|
# Maintain a minimum of +min+ and maximum of +max+ threads
|
|
9
16
|
# in the pool.
|
|
@@ -45,6 +52,12 @@ module Puma
|
|
|
45
52
|
attr_reader :spawned, :trim_requested
|
|
46
53
|
attr_accessor :clean_thread_locals
|
|
47
54
|
|
|
55
|
+
def self.clean_thread_locals
|
|
56
|
+
Thread.current.keys.each do |key| # rubocop: disable Performance/HashEachMethods
|
|
57
|
+
Thread.current[key] = nil unless key == :__recursive_key__
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
48
61
|
# How many objects have yet to be processed by the pool?
|
|
49
62
|
#
|
|
50
63
|
def backlog
|
|
@@ -58,7 +71,9 @@ module Puma
|
|
|
58
71
|
def spawn_thread
|
|
59
72
|
@spawned += 1
|
|
60
73
|
|
|
61
|
-
th = Thread.new do
|
|
74
|
+
th = Thread.new(@spawned) do |spawned|
|
|
75
|
+
# Thread name is new in Ruby 2.3
|
|
76
|
+
Thread.current.name = 'puma %03i' % spawned if Thread.current.respond_to?(:name=)
|
|
62
77
|
todo = @todo
|
|
63
78
|
block = @block
|
|
64
79
|
mutex = @mutex
|
|
@@ -77,6 +92,7 @@ module Puma
|
|
|
77
92
|
if @trim_requested > 0
|
|
78
93
|
@trim_requested -= 1
|
|
79
94
|
continue = false
|
|
95
|
+
not_full.signal
|
|
80
96
|
break
|
|
81
97
|
end
|
|
82
98
|
|
|
@@ -97,14 +113,13 @@ module Puma
|
|
|
97
113
|
break unless continue
|
|
98
114
|
|
|
99
115
|
if @clean_thread_locals
|
|
100
|
-
|
|
101
|
-
Thread.current[key] = nil unless key == :__recursive_key__
|
|
102
|
-
end
|
|
116
|
+
ThreadPool.clean_thread_locals
|
|
103
117
|
end
|
|
104
118
|
|
|
105
119
|
begin
|
|
106
120
|
block.call(work, *extra)
|
|
107
|
-
rescue Exception
|
|
121
|
+
rescue Exception => e
|
|
122
|
+
STDERR.puts "Error reached top of thread-pool: #{e.message} (#{e.class})"
|
|
108
123
|
end
|
|
109
124
|
end
|
|
110
125
|
|
|
@@ -140,7 +155,15 @@ module Puma
|
|
|
140
155
|
|
|
141
156
|
def wait_until_not_full
|
|
142
157
|
@mutex.synchronize do
|
|
143
|
-
|
|
158
|
+
while true
|
|
159
|
+
return if @shutdown
|
|
160
|
+
return if @waiting > 0
|
|
161
|
+
|
|
162
|
+
# If we can still spin up new threads and there
|
|
163
|
+
# is work queued, then accept more work until we would
|
|
164
|
+
# spin up the max number of threads.
|
|
165
|
+
return if @todo.size < @max - @spawned
|
|
166
|
+
|
|
144
167
|
@not_full.wait @mutex
|
|
145
168
|
end
|
|
146
169
|
end
|
|
@@ -170,7 +193,9 @@ module Puma
|
|
|
170
193
|
@spawned -= 1
|
|
171
194
|
end
|
|
172
195
|
|
|
173
|
-
@workers
|
|
196
|
+
@workers.delete_if do |w|
|
|
197
|
+
dead_workers.include?(w)
|
|
198
|
+
end
|
|
174
199
|
end
|
|
175
200
|
end
|
|
176
201
|
|
|
@@ -198,7 +223,7 @@ module Puma
|
|
|
198
223
|
end
|
|
199
224
|
end
|
|
200
225
|
|
|
201
|
-
def auto_trim!(timeout=
|
|
226
|
+
def auto_trim!(timeout=30)
|
|
202
227
|
@auto_trim = AutoTrim.new(self, timeout)
|
|
203
228
|
@auto_trim.start!
|
|
204
229
|
end
|
|
@@ -234,7 +259,7 @@ module Puma
|
|
|
234
259
|
|
|
235
260
|
# Tell all threads in the pool to exit and wait for them to finish.
|
|
236
261
|
#
|
|
237
|
-
def shutdown
|
|
262
|
+
def shutdown(timeout=-1)
|
|
238
263
|
threads = @mutex.synchronize do
|
|
239
264
|
@shutdown = true
|
|
240
265
|
@not_empty.broadcast
|
|
@@ -246,7 +271,32 @@ module Puma
|
|
|
246
271
|
@workers.dup
|
|
247
272
|
end
|
|
248
273
|
|
|
249
|
-
|
|
274
|
+
if timeout == -1
|
|
275
|
+
# Wait for threads to finish without force shutdown.
|
|
276
|
+
threads.each(&:join)
|
|
277
|
+
else
|
|
278
|
+
# Wait for threads to finish after n attempts (+timeout+).
|
|
279
|
+
# If threads are still running, it will forcefully kill them.
|
|
280
|
+
timeout.times do
|
|
281
|
+
threads.delete_if do |t|
|
|
282
|
+
t.join 1
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
if threads.empty?
|
|
286
|
+
break
|
|
287
|
+
else
|
|
288
|
+
sleep 1
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
threads.each do |t|
|
|
293
|
+
t.raise ForceShutdown
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
threads.each do |t|
|
|
297
|
+
t.join SHUTDOWN_GRACE_TIME
|
|
298
|
+
end
|
|
299
|
+
end
|
|
250
300
|
|
|
251
301
|
@spawned = 0
|
|
252
302
|
@workers = []
|
data/lib/puma/util.rb
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
major, minor, patch = RUBY_VERSION.split('.').map { |v| v.to_i }
|
|
2
2
|
|
|
3
|
-
if major == 1 && minor <
|
|
4
|
-
require 'puma/rack/backports/uri/common_18'
|
|
5
|
-
elsif major == 1 && minor == 9 && patch == 2 && RUBY_PATCHLEVEL <= 328 && RUBY_ENGINE != 'jruby'
|
|
6
|
-
require 'puma/rack/backports/uri/common_192'
|
|
7
|
-
elsif major == 1 && minor == 9 && patch == 3 && RUBY_PATCHLEVEL < 125
|
|
3
|
+
if major == 1 && minor == 9 && patch == 3 && RUBY_PATCHLEVEL < 125
|
|
8
4
|
require 'puma/rack/backports/uri/common_193'
|
|
9
5
|
else
|
|
10
6
|
require 'uri/common'
|
data/lib/puma.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# Standard libraries
|
|
2
2
|
require 'socket'
|
|
3
3
|
require 'tempfile'
|
|
4
|
-
require 'yaml'
|
|
5
4
|
require 'time'
|
|
6
5
|
require 'etc'
|
|
7
6
|
require 'uri'
|
|
@@ -9,6 +8,16 @@ require 'stringio'
|
|
|
9
8
|
|
|
10
9
|
require 'thread'
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
module Puma
|
|
12
|
+
autoload :Const, 'puma/const'
|
|
13
|
+
autoload :Server, 'puma/server'
|
|
14
|
+
autoload :Launcher, 'puma/launcher'
|
|
15
|
+
|
|
16
|
+
def self.stats_object=(val)
|
|
17
|
+
@get_stats = val
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.stats
|
|
21
|
+
@get_stats.stats
|
|
22
|
+
end
|
|
23
|
+
end
|