puma 6.0.0 → 6.4.0
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 +184 -6
- data/LICENSE +0 -0
- data/README.md +58 -13
- data/bin/puma-wild +0 -0
- data/docs/architecture.md +0 -0
- data/docs/compile_options.md +0 -0
- data/docs/deployment.md +0 -0
- data/docs/fork_worker.md +0 -0
- 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/jungle/README.md +0 -0
- data/docs/jungle/rc.d/README.md +0 -0
- data/docs/jungle/rc.d/puma.conf +0 -0
- data/docs/kubernetes.md +12 -0
- data/docs/nginx.md +1 -1
- data/docs/plugins.md +0 -0
- data/docs/rails_dev_mode.md +0 -0
- data/docs/restart.md +0 -0
- data/docs/signals.md +0 -0
- data/docs/stats.md +0 -0
- data/docs/systemd.md +1 -2
- data/docs/testing_benchmarks_local_files.md +0 -0
- data/docs/testing_test_rackup_ci_files.md +0 -0
- data/ext/puma_http11/PumaHttp11Service.java +0 -0
- data/ext/puma_http11/ext_help.h +0 -0
- data/ext/puma_http11/extconf.rb +0 -0
- data/ext/puma_http11/http11_parser.c +0 -0
- data/ext/puma_http11/http11_parser.h +0 -0
- data/ext/puma_http11/http11_parser.java.rl +0 -0
- data/ext/puma_http11/http11_parser.rl +0 -0
- data/ext/puma_http11/http11_parser_common.rl +0 -0
- data/ext/puma_http11/mini_ssl.c +91 -8
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +0 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +0 -0
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +0 -0
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +0 -0
- data/ext/puma_http11/puma_http11.c +0 -0
- data/lib/puma/app/status.rb +1 -1
- data/lib/puma/binder.rb +14 -11
- data/lib/puma/cli.rb +5 -1
- data/lib/puma/client.rb +53 -16
- data/lib/puma/cluster/worker.rb +5 -0
- data/lib/puma/cluster/worker_handle.rb +0 -0
- data/lib/puma/cluster.rb +5 -5
- data/lib/puma/commonlogger.rb +21 -14
- data/lib/puma/configuration.rb +5 -1
- data/lib/puma/const.rb +129 -88
- data/lib/puma/control_cli.rb +12 -5
- data/lib/puma/detect.rb +2 -0
- data/lib/puma/dsl.rb +147 -7
- data/lib/puma/error_logger.rb +2 -1
- data/lib/puma/events.rb +0 -0
- data/lib/puma/io_buffer.rb +10 -0
- data/lib/puma/jruby_restart.rb +0 -0
- data/lib/puma/json_serialization.rb +0 -0
- data/lib/puma/launcher/bundle_pruner.rb +0 -0
- data/lib/puma/launcher.rb +9 -22
- data/lib/puma/log_writer.rb +14 -4
- data/lib/puma/minissl/context_builder.rb +1 -0
- data/lib/puma/minissl.rb +17 -0
- data/lib/puma/plugin/systemd.rb +90 -0
- data/lib/puma/plugin/tmp_restart.rb +0 -0
- data/lib/puma/plugin.rb +0 -0
- data/lib/puma/rack/builder.rb +2 -2
- data/lib/puma/rack/urlmap.rb +1 -1
- data/lib/puma/rack_default.rb +18 -3
- data/lib/puma/reactor.rb +17 -8
- data/lib/puma/request.rb +189 -125
- data/lib/puma/runner.rb +16 -1
- data/lib/puma/sd_notify.rb +149 -0
- data/lib/puma/server.rb +74 -34
- data/lib/puma/single.rb +3 -1
- data/lib/puma/state_file.rb +0 -0
- data/lib/puma/thread_pool.rb +42 -7
- data/lib/puma/util.rb +0 -0
- data/lib/puma.rb +1 -3
- data/lib/rack/handler/puma.rb +113 -86
- data/tools/Dockerfile +0 -0
- data/tools/trickletest.rb +0 -0
- metadata +4 -3
- data/lib/puma/systemd.rb +0 -47
@@ -0,0 +1,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "socket"
|
4
|
+
|
5
|
+
module Puma
|
6
|
+
# The MIT License
|
7
|
+
#
|
8
|
+
# Copyright (c) 2017-2022 Agis Anastasopoulos
|
9
|
+
#
|
10
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
11
|
+
# this software and associated documentation files (the "Software"), to deal in
|
12
|
+
# the Software without restriction, including without limitation the rights to
|
13
|
+
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
14
|
+
# the Software, and to permit persons to whom the Software is furnished to do so,
|
15
|
+
# subject to the following conditions:
|
16
|
+
#
|
17
|
+
# The above copyright notice and this permission notice shall be included in all
|
18
|
+
# copies or substantial portions of the Software.
|
19
|
+
#
|
20
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
21
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
22
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
23
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
24
|
+
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
25
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
26
|
+
#
|
27
|
+
# This is a copy of https://github.com/agis/ruby-sdnotify as of commit cca575c
|
28
|
+
# The only changes made was "rehoming" it within the Puma module to avoid
|
29
|
+
# namespace collisions and applying standard's code formatting style.
|
30
|
+
#
|
31
|
+
# SdNotify is a pure-Ruby implementation of sd_notify(3). It can be used to
|
32
|
+
# notify systemd about state changes. Methods of this package are no-op on
|
33
|
+
# non-systemd systems (eg. Darwin).
|
34
|
+
#
|
35
|
+
# The API maps closely to the original implementation of sd_notify(3),
|
36
|
+
# therefore be sure to check the official man pages prior to using SdNotify.
|
37
|
+
#
|
38
|
+
# @see https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
39
|
+
module SdNotify
|
40
|
+
# Exception raised when there's an error writing to the notification socket
|
41
|
+
class NotifyError < RuntimeError; end
|
42
|
+
|
43
|
+
READY = "READY=1"
|
44
|
+
RELOADING = "RELOADING=1"
|
45
|
+
STOPPING = "STOPPING=1"
|
46
|
+
STATUS = "STATUS="
|
47
|
+
ERRNO = "ERRNO="
|
48
|
+
MAINPID = "MAINPID="
|
49
|
+
WATCHDOG = "WATCHDOG=1"
|
50
|
+
FDSTORE = "FDSTORE=1"
|
51
|
+
|
52
|
+
def self.ready(unset_env=false)
|
53
|
+
notify(READY, unset_env)
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.reloading(unset_env=false)
|
57
|
+
notify(RELOADING, unset_env)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.stopping(unset_env=false)
|
61
|
+
notify(STOPPING, unset_env)
|
62
|
+
end
|
63
|
+
|
64
|
+
# @param status [String] a custom status string that describes the current
|
65
|
+
# state of the service
|
66
|
+
def self.status(status, unset_env=false)
|
67
|
+
notify("#{STATUS}#{status}", unset_env)
|
68
|
+
end
|
69
|
+
|
70
|
+
# @param errno [Integer]
|
71
|
+
def self.errno(errno, unset_env=false)
|
72
|
+
notify("#{ERRNO}#{errno}", unset_env)
|
73
|
+
end
|
74
|
+
|
75
|
+
# @param pid [Integer]
|
76
|
+
def self.mainpid(pid, unset_env=false)
|
77
|
+
notify("#{MAINPID}#{pid}", unset_env)
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.watchdog(unset_env=false)
|
81
|
+
notify(WATCHDOG, unset_env)
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.fdstore(unset_env=false)
|
85
|
+
notify(FDSTORE, unset_env)
|
86
|
+
end
|
87
|
+
|
88
|
+
# @param [Boolean] true if the service manager expects watchdog keep-alive
|
89
|
+
# notification messages to be sent from this process.
|
90
|
+
#
|
91
|
+
# If the $WATCHDOG_USEC environment variable is set,
|
92
|
+
# and the $WATCHDOG_PID variable is unset or set to the PID of the current
|
93
|
+
# process
|
94
|
+
#
|
95
|
+
# @note Unlike sd_watchdog_enabled(3), this method does not mutate the
|
96
|
+
# environment.
|
97
|
+
def self.watchdog?
|
98
|
+
wd_usec = ENV["WATCHDOG_USEC"]
|
99
|
+
wd_pid = ENV["WATCHDOG_PID"]
|
100
|
+
|
101
|
+
return false if !wd_usec
|
102
|
+
|
103
|
+
begin
|
104
|
+
wd_usec = Integer(wd_usec)
|
105
|
+
rescue
|
106
|
+
return false
|
107
|
+
end
|
108
|
+
|
109
|
+
return false if wd_usec <= 0
|
110
|
+
return true if !wd_pid || wd_pid == $$.to_s
|
111
|
+
|
112
|
+
false
|
113
|
+
end
|
114
|
+
|
115
|
+
# Notify systemd with the provided state, via the notification socket, if
|
116
|
+
# any.
|
117
|
+
#
|
118
|
+
# Generally this method will be used indirectly through the other methods
|
119
|
+
# of the library.
|
120
|
+
#
|
121
|
+
# @param state [String]
|
122
|
+
# @param unset_env [Boolean]
|
123
|
+
#
|
124
|
+
# @return [Fixnum, nil] the number of bytes written to the notification
|
125
|
+
# socket or nil if there was no socket to report to (eg. the program wasn't
|
126
|
+
# started by systemd)
|
127
|
+
#
|
128
|
+
# @raise [NotifyError] if there was an error communicating with the systemd
|
129
|
+
# socket
|
130
|
+
#
|
131
|
+
# @see https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
132
|
+
def self.notify(state, unset_env=false)
|
133
|
+
sock = ENV["NOTIFY_SOCKET"]
|
134
|
+
|
135
|
+
return nil if !sock
|
136
|
+
|
137
|
+
ENV.delete("NOTIFY_SOCKET") if unset_env
|
138
|
+
|
139
|
+
begin
|
140
|
+
Addrinfo.unix(sock, :DGRAM).connect do |s|
|
141
|
+
s.close_on_exec = true
|
142
|
+
s.write(state)
|
143
|
+
end
|
144
|
+
rescue StandardError => e
|
145
|
+
raise NotifyError, "#{e.class}: #{e.message}", e.backtrace
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
data/lib/puma/server.rb
CHANGED
@@ -11,12 +11,10 @@ require_relative 'reactor'
|
|
11
11
|
require_relative 'client'
|
12
12
|
require_relative 'binder'
|
13
13
|
require_relative 'util'
|
14
|
-
require_relative 'io_buffer'
|
15
14
|
require_relative 'request'
|
16
15
|
|
17
16
|
require 'socket'
|
18
17
|
require 'io/wait' unless Puma::HAS_NATIVE_IO_WAIT
|
19
|
-
require 'forwardable'
|
20
18
|
|
21
19
|
module Puma
|
22
20
|
|
@@ -33,7 +31,6 @@ module Puma
|
|
33
31
|
class Server
|
34
32
|
include Puma::Const
|
35
33
|
include Request
|
36
|
-
extend Forwardable
|
37
34
|
|
38
35
|
attr_reader :thread
|
39
36
|
attr_reader :log_writer
|
@@ -49,10 +46,7 @@ module Puma
|
|
49
46
|
attr_accessor :app
|
50
47
|
attr_accessor :binder
|
51
48
|
|
52
|
-
|
53
|
-
:add_unix_listener, :connected_ports
|
54
|
-
|
55
|
-
ThreadLocalKey = :puma_server
|
49
|
+
THREAD_LOCAL_KEY = :puma_server
|
56
50
|
|
57
51
|
# Create a server for the rack app +app+.
|
58
52
|
#
|
@@ -87,15 +81,29 @@ module Puma
|
|
87
81
|
UserFileDefaultOptions.new(options, Configuration::DEFAULTS)
|
88
82
|
end
|
89
83
|
|
90
|
-
@log_writer
|
91
|
-
@early_hints
|
92
|
-
@first_data_timeout
|
93
|
-
@
|
94
|
-
@
|
95
|
-
@
|
96
|
-
@
|
97
|
-
@
|
98
|
-
@
|
84
|
+
@log_writer = @options.fetch :log_writer, LogWriter.stdio
|
85
|
+
@early_hints = @options[:early_hints]
|
86
|
+
@first_data_timeout = @options[:first_data_timeout]
|
87
|
+
@persistent_timeout = @options[:persistent_timeout]
|
88
|
+
@idle_timeout = @options[:idle_timeout]
|
89
|
+
@min_threads = @options[:min_threads]
|
90
|
+
@max_threads = @options[:max_threads]
|
91
|
+
@queue_requests = @options[:queue_requests]
|
92
|
+
@max_fast_inline = @options[:max_fast_inline]
|
93
|
+
@io_selector_backend = @options[:io_selector_backend]
|
94
|
+
@http_content_length_limit = @options[:http_content_length_limit]
|
95
|
+
|
96
|
+
# make this a hash, since we prefer `key?` over `include?`
|
97
|
+
@supported_http_methods =
|
98
|
+
if @options[:supported_http_methods] == :any
|
99
|
+
:any
|
100
|
+
else
|
101
|
+
if (ary = @options[:supported_http_methods])
|
102
|
+
ary
|
103
|
+
else
|
104
|
+
SUPPORTED_HTTP_METHODS
|
105
|
+
end.sort.product([nil]).to_h.freeze
|
106
|
+
end
|
99
107
|
|
100
108
|
temp = !!(@options[:environment] =~ /\A(development|test)\z/)
|
101
109
|
@leak_stack_on_error = @options[:environment] ? temp : true
|
@@ -118,7 +126,7 @@ module Puma
|
|
118
126
|
class << self
|
119
127
|
# @!attribute [r] current
|
120
128
|
def current
|
121
|
-
Thread.current[
|
129
|
+
Thread.current[THREAD_LOCAL_KEY]
|
122
130
|
end
|
123
131
|
|
124
132
|
# :nodoc:
|
@@ -230,7 +238,7 @@ module Puma
|
|
230
238
|
|
231
239
|
@status = :run
|
232
240
|
|
233
|
-
@thread_pool = ThreadPool.new(thread_name, @options) { |
|
241
|
+
@thread_pool = ThreadPool.new(thread_name, @options) { |client| process_client client }
|
234
242
|
|
235
243
|
if @queue_requests
|
236
244
|
@reactor = Reactor.new(@io_selector_backend) { |c| reactor_wakeup c }
|
@@ -318,8 +326,12 @@ module Puma
|
|
318
326
|
|
319
327
|
while @status == :run || (drain && shutting_down?)
|
320
328
|
begin
|
321
|
-
ios = IO.select sockets, nil, nil, (shutting_down? ? 0 :
|
322
|
-
|
329
|
+
ios = IO.select sockets, nil, nil, (shutting_down? ? 0 : @idle_timeout)
|
330
|
+
unless ios
|
331
|
+
@status = :stop unless shutting_down?
|
332
|
+
break
|
333
|
+
end
|
334
|
+
|
323
335
|
ios.first.each do |sock|
|
324
336
|
if sock == check
|
325
337
|
break if handle_check
|
@@ -335,6 +347,7 @@ module Puma
|
|
335
347
|
drain += 1 if shutting_down?
|
336
348
|
pool << Client.new(io, @binder.env(sock)).tap { |c|
|
337
349
|
c.listener = sock
|
350
|
+
c.http_content_length_limit = @http_content_length_limit
|
338
351
|
c.send(addr_send_name, addr_value) if addr_value
|
339
352
|
}
|
340
353
|
end
|
@@ -401,9 +414,9 @@ module Puma
|
|
401
414
|
# returning.
|
402
415
|
#
|
403
416
|
# Return true if one or more requests were processed.
|
404
|
-
def process_client(client
|
417
|
+
def process_client(client)
|
405
418
|
# Advertise this server into the thread
|
406
|
-
Thread.current[
|
419
|
+
Thread.current[THREAD_LOCAL_KEY] = self
|
407
420
|
|
408
421
|
clean_thread_locals = @options[:clean_thread_locals]
|
409
422
|
close_socket = true
|
@@ -427,15 +440,13 @@ module Puma
|
|
427
440
|
|
428
441
|
while true
|
429
442
|
@requests_count += 1
|
430
|
-
case handle_request(client,
|
443
|
+
case handle_request(client, requests + 1)
|
431
444
|
when false
|
432
445
|
break
|
433
446
|
when :async
|
434
447
|
close_socket = false
|
435
448
|
break
|
436
449
|
when true
|
437
|
-
buffer.reset
|
438
|
-
|
439
450
|
ThreadPool.clean_thread_locals if clean_thread_locals
|
440
451
|
|
441
452
|
requests += 1
|
@@ -465,11 +476,11 @@ module Puma
|
|
465
476
|
end
|
466
477
|
true
|
467
478
|
rescue StandardError => e
|
468
|
-
client_error(e, client)
|
479
|
+
client_error(e, client, requests)
|
469
480
|
# The ensure tries to close +client+ down
|
470
481
|
requests > 0
|
471
482
|
ensure
|
472
|
-
|
483
|
+
client.io_buffer.reset
|
473
484
|
|
474
485
|
begin
|
475
486
|
client.close if close_socket
|
@@ -493,22 +504,22 @@ module Puma
|
|
493
504
|
# :nocov:
|
494
505
|
|
495
506
|
# Handle various error types thrown by Client I/O operations.
|
496
|
-
def client_error(e, client)
|
507
|
+
def client_error(e, client, requests = 1)
|
497
508
|
# Swallow, do not log
|
498
509
|
return if [ConnectionError, EOFError].include?(e.class)
|
499
510
|
|
500
|
-
lowlevel_error(e, client.env)
|
501
511
|
case e
|
502
512
|
when MiniSSL::SSLError
|
513
|
+
lowlevel_error(e, client.env)
|
503
514
|
@log_writer.ssl_error e, client.io
|
504
515
|
when HttpParserError
|
505
|
-
client
|
516
|
+
response_to_error(client, requests, e, 400)
|
506
517
|
@log_writer.parse_error e, client
|
507
518
|
when HttpParserError501
|
508
|
-
client
|
519
|
+
response_to_error(client, requests, e, 501)
|
509
520
|
@log_writer.parse_error e, client
|
510
521
|
else
|
511
|
-
client
|
522
|
+
response_to_error(client, requests, e, 500)
|
512
523
|
@log_writer.unknown_error e, nil, "Read"
|
513
524
|
end
|
514
525
|
end
|
@@ -530,10 +541,17 @@ module Puma
|
|
530
541
|
backtrace = e.backtrace.nil? ? '<no backtrace available>' : e.backtrace.join("\n")
|
531
542
|
[status, {}, ["Puma caught this error: #{e.message} (#{e.class})\n#{backtrace}"]]
|
532
543
|
else
|
533
|
-
[status, {}, ["
|
544
|
+
[status, {}, [""]]
|
534
545
|
end
|
535
546
|
end
|
536
547
|
|
548
|
+
def response_to_error(client, requests, err, status_code)
|
549
|
+
status, headers, res_body = lowlevel_error(err, client.env, status_code)
|
550
|
+
prepare_response(status, headers, res_body, requests, client)
|
551
|
+
client.write_error(status_code)
|
552
|
+
end
|
553
|
+
private :response_to_error
|
554
|
+
|
537
555
|
# Wait for all outstanding requests to finish.
|
538
556
|
#
|
539
557
|
def graceful_shutdown
|
@@ -567,7 +585,7 @@ module Puma
|
|
567
585
|
|
568
586
|
def notify_safely(message)
|
569
587
|
@notify << message
|
570
|
-
rescue IOError, NoMethodError, Errno::EPIPE
|
588
|
+
rescue IOError, NoMethodError, Errno::EPIPE, Errno::EBADF
|
571
589
|
# The server, in another thread, is shutting down
|
572
590
|
Puma::Util.purge_interrupt_queue
|
573
591
|
rescue RuntimeError => e
|
@@ -612,5 +630,27 @@ module Puma
|
|
612
630
|
def stats
|
613
631
|
STAT_METHODS.map {|name| [name, send(name) || 0]}.to_h
|
614
632
|
end
|
633
|
+
|
634
|
+
# below are 'delegations' to binder
|
635
|
+
# remove in Puma 7?
|
636
|
+
|
637
|
+
|
638
|
+
def add_tcp_listener(host, port, optimize_for_latency = true, backlog = 1024)
|
639
|
+
@binder.add_tcp_listener host, port, optimize_for_latency, backlog
|
640
|
+
end
|
641
|
+
|
642
|
+
def add_ssl_listener(host, port, ctx, optimize_for_latency = true,
|
643
|
+
backlog = 1024)
|
644
|
+
@binder.add_ssl_listener host, port, ctx, optimize_for_latency, backlog
|
645
|
+
end
|
646
|
+
|
647
|
+
def add_unix_listener(path, umask = nil, mode = nil, backlog = 1024)
|
648
|
+
@binder.add_unix_listener path, umask, mode, backlog
|
649
|
+
end
|
650
|
+
|
651
|
+
# @!attribute [r] connected_ports
|
652
|
+
def connected_ports
|
653
|
+
@binder.connected_ports
|
654
|
+
end
|
615
655
|
end
|
616
656
|
end
|
data/lib/puma/single.rb
CHANGED
@@ -16,7 +16,7 @@ module Puma
|
|
16
16
|
# @!attribute [r] stats
|
17
17
|
def stats
|
18
18
|
{
|
19
|
-
started_at: @started_at
|
19
|
+
started_at: utc_iso8601(@started_at)
|
20
20
|
}.merge(@server.stats).merge(super)
|
21
21
|
end
|
22
22
|
|
@@ -57,6 +57,8 @@ module Puma
|
|
57
57
|
|
58
58
|
@events.fire_on_booted!
|
59
59
|
|
60
|
+
debug_loaded_extensions("Loaded Extensions:") if @log_writer.debug?
|
61
|
+
|
60
62
|
begin
|
61
63
|
server_thread.join
|
62
64
|
rescue Interrupt
|
data/lib/puma/state_file.rb
CHANGED
File without changes
|
data/lib/puma/thread_pool.rb
CHANGED
@@ -44,10 +44,15 @@ module Puma
|
|
44
44
|
@name = name
|
45
45
|
@min = Integer(options[:min_threads])
|
46
46
|
@max = Integer(options[:max_threads])
|
47
|
+
# Not an 'exposed' option, options[:pool_shutdown_grace_time] is used in CI
|
48
|
+
# to shorten @shutdown_grace_time from SHUTDOWN_GRACE_TIME. Parallel CI
|
49
|
+
# makes stubbing constants difficult.
|
50
|
+
@shutdown_grace_time = Float(options[:pool_shutdown_grace_time] || SHUTDOWN_GRACE_TIME)
|
47
51
|
@block = block
|
48
|
-
@extra = [::Puma::IOBuffer]
|
49
52
|
@out_of_band = options[:out_of_band]
|
50
53
|
@clean_thread_locals = options[:clean_thread_locals]
|
54
|
+
@before_thread_start = options[:before_thread_start]
|
55
|
+
@before_thread_exit = options[:before_thread_exit]
|
51
56
|
@reaping_time = options[:reaping_time]
|
52
57
|
@auto_trim_time = options[:auto_trim_time]
|
53
58
|
|
@@ -104,6 +109,7 @@ module Puma
|
|
104
109
|
def spawn_thread
|
105
110
|
@spawned += 1
|
106
111
|
|
112
|
+
trigger_before_thread_start_hooks
|
107
113
|
th = Thread.new(@spawned) do |spawned|
|
108
114
|
Puma.set_thread_name '%s tp %03i' % [@name, spawned]
|
109
115
|
todo = @todo
|
@@ -112,8 +118,6 @@ module Puma
|
|
112
118
|
not_empty = @not_empty
|
113
119
|
not_full = @not_full
|
114
120
|
|
115
|
-
extra = @extra.map { |i| i.new }
|
116
|
-
|
117
121
|
while true
|
118
122
|
work = nil
|
119
123
|
|
@@ -124,6 +128,7 @@ module Puma
|
|
124
128
|
@spawned -= 1
|
125
129
|
@workers.delete th
|
126
130
|
not_full.signal
|
131
|
+
trigger_before_thread_exit_hooks
|
127
132
|
Thread.exit
|
128
133
|
end
|
129
134
|
|
@@ -147,7 +152,7 @@ module Puma
|
|
147
152
|
end
|
148
153
|
|
149
154
|
begin
|
150
|
-
@out_of_band_pending = true if block.call(work
|
155
|
+
@out_of_band_pending = true if block.call(work)
|
151
156
|
rescue Exception => e
|
152
157
|
STDERR.puts "Error reached top of thread-pool: #{e.message} (#{e.class})"
|
153
158
|
end
|
@@ -161,6 +166,36 @@ module Puma
|
|
161
166
|
|
162
167
|
private :spawn_thread
|
163
168
|
|
169
|
+
def trigger_before_thread_start_hooks
|
170
|
+
return unless @before_thread_start&.any?
|
171
|
+
|
172
|
+
@before_thread_start.each do |b|
|
173
|
+
begin
|
174
|
+
b.call
|
175
|
+
rescue Exception => e
|
176
|
+
STDERR.puts "WARNING before_thread_start hook failed with exception (#{e.class}) #{e.message}"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
nil
|
180
|
+
end
|
181
|
+
|
182
|
+
private :trigger_before_thread_start_hooks
|
183
|
+
|
184
|
+
def trigger_before_thread_exit_hooks
|
185
|
+
return unless @before_thread_exit&.any?
|
186
|
+
|
187
|
+
@before_thread_exit.each do |b|
|
188
|
+
begin
|
189
|
+
b.call
|
190
|
+
rescue Exception => e
|
191
|
+
STDERR.puts "WARNING before_thread_exit hook failed with exception (#{e.class}) #{e.message}"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
nil
|
195
|
+
end
|
196
|
+
|
197
|
+
private :trigger_before_thread_exit_hooks
|
198
|
+
|
164
199
|
# @version 5.0.0
|
165
200
|
def trigger_out_of_band_hook
|
166
201
|
return false unless @out_of_band&.any?
|
@@ -347,8 +382,8 @@ module Puma
|
|
347
382
|
|
348
383
|
# Tell all threads in the pool to exit and wait for them to finish.
|
349
384
|
# Wait +timeout+ seconds then raise +ForceShutdown+ in remaining threads.
|
350
|
-
# Next, wait an extra +
|
351
|
-
# Finally, wait
|
385
|
+
# Next, wait an extra +@shutdown_grace_time+ seconds then force-kill remaining
|
386
|
+
# threads. Finally, wait 1 second for remaining threads to exit.
|
352
387
|
#
|
353
388
|
def shutdown(timeout=-1)
|
354
389
|
threads = with_mutex do
|
@@ -385,7 +420,7 @@ module Puma
|
|
385
420
|
t.raise ForceShutdown if t[:with_force_shutdown]
|
386
421
|
end
|
387
422
|
end
|
388
|
-
join.call(
|
423
|
+
join.call(@shutdown_grace_time)
|
389
424
|
|
390
425
|
# If threads are _still_ running, forcefully kill them and wait to finish.
|
391
426
|
threads.each(&:kill)
|
data/lib/puma/util.rb
CHANGED
File without changes
|
data/lib/puma.rb
CHANGED
@@ -3,8 +3,6 @@
|
|
3
3
|
# Standard libraries
|
4
4
|
require 'socket'
|
5
5
|
require 'tempfile'
|
6
|
-
require 'time'
|
7
|
-
require 'etc'
|
8
6
|
require 'uri'
|
9
7
|
require 'stringio'
|
10
8
|
|
@@ -28,7 +26,7 @@ module Puma
|
|
28
26
|
# not in minissl.rb
|
29
27
|
HAS_SSL = const_defined?(:MiniSSL, false) && MiniSSL.const_defined?(:Engine, false)
|
30
28
|
|
31
|
-
HAS_UNIX_SOCKET = Object.const_defined?
|
29
|
+
HAS_UNIX_SOCKET = Object.const_defined?(:UNIXSocket) && !IS_WINDOWS
|
32
30
|
|
33
31
|
if HAS_SSL
|
34
32
|
require_relative 'puma/minissl'
|