puma 5.0.2-java → 5.2.0-java
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 +4 -4
- data/History.md +667 -567
- data/README.md +51 -21
- data/bin/puma-wild +3 -9
- data/docs/compile_options.md +19 -0
- data/docs/deployment.md +6 -7
- data/docs/fork_worker.md +2 -0
- data/docs/jungle/README.md +0 -4
- data/docs/jungle/rc.d/puma +2 -2
- data/docs/kubernetes.md +66 -0
- data/docs/nginx.md +1 -1
- data/docs/plugins.md +1 -1
- data/docs/restart.md +46 -23
- data/docs/stats.md +142 -0
- data/docs/systemd.md +25 -3
- data/ext/puma_http11/ext_help.h +1 -1
- data/ext/puma_http11/extconf.rb +18 -5
- data/ext/puma_http11/http11_parser.c +45 -47
- data/ext/puma_http11/http11_parser.java.rl +1 -1
- data/ext/puma_http11/http11_parser.rl +1 -1
- data/ext/puma_http11/mini_ssl.c +199 -119
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +5 -7
- data/ext/puma_http11/puma_http11.c +25 -12
- data/lib/puma.rb +2 -2
- data/lib/puma/app/status.rb +44 -46
- data/lib/puma/binder.rb +69 -25
- data/lib/puma/cli.rb +4 -0
- data/lib/puma/client.rb +26 -79
- data/lib/puma/cluster.rb +37 -202
- data/lib/puma/cluster/worker.rb +176 -0
- data/lib/puma/cluster/worker_handle.rb +86 -0
- data/lib/puma/configuration.rb +21 -8
- data/lib/puma/const.rb +11 -3
- data/lib/puma/control_cli.rb +73 -70
- data/lib/puma/dsl.rb +100 -22
- data/lib/puma/error_logger.rb +10 -3
- data/lib/puma/events.rb +18 -3
- data/lib/puma/json.rb +96 -0
- data/lib/puma/launcher.rb +57 -15
- data/lib/puma/minissl.rb +47 -16
- data/lib/puma/minissl/context_builder.rb +6 -0
- data/lib/puma/null_io.rb +4 -0
- data/lib/puma/puma_http11.jar +0 -0
- data/lib/puma/queue_close.rb +26 -0
- data/lib/puma/reactor.rb +85 -363
- data/lib/puma/request.rb +451 -0
- data/lib/puma/runner.rb +17 -23
- data/lib/puma/server.rb +164 -553
- data/lib/puma/single.rb +2 -2
- data/lib/puma/state_file.rb +5 -3
- data/lib/puma/systemd.rb +46 -0
- data/lib/puma/util.rb +11 -0
- metadata +11 -6
- data/docs/jungle/upstart/README.md +0 -61
- data/docs/jungle/upstart/puma-manager.conf +0 -31
- data/docs/jungle/upstart/puma.conf +0 -69
- data/lib/puma/accept_nonblock.rb +0 -29
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Puma
|
|
4
|
+
class Cluster < Puma::Runner
|
|
5
|
+
# This class is instantiated by the `Puma::Cluster` and represents a single
|
|
6
|
+
# worker process.
|
|
7
|
+
#
|
|
8
|
+
# At the core of this class is running an instance of `Puma::Server` which
|
|
9
|
+
# gets created via the `start_server` method from the `Puma::Runner` class
|
|
10
|
+
# that this inherits from.
|
|
11
|
+
class Worker < Puma::Runner
|
|
12
|
+
attr_reader :index, :master
|
|
13
|
+
|
|
14
|
+
def initialize(index:, master:, launcher:, pipes:, server: nil)
|
|
15
|
+
super launcher, launcher.events
|
|
16
|
+
|
|
17
|
+
@index = index
|
|
18
|
+
@master = master
|
|
19
|
+
@launcher = launcher
|
|
20
|
+
@options = launcher.options
|
|
21
|
+
@check_pipe = pipes[:check_pipe]
|
|
22
|
+
@worker_write = pipes[:worker_write]
|
|
23
|
+
@fork_pipe = pipes[:fork_pipe]
|
|
24
|
+
@wakeup = pipes[:wakeup]
|
|
25
|
+
@server = server
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def run
|
|
29
|
+
title = "puma: cluster worker #{index}: #{master}"
|
|
30
|
+
title += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
|
|
31
|
+
$0 = title
|
|
32
|
+
|
|
33
|
+
Signal.trap "SIGINT", "IGNORE"
|
|
34
|
+
Signal.trap "SIGCHLD", "DEFAULT"
|
|
35
|
+
|
|
36
|
+
Thread.new do
|
|
37
|
+
Puma.set_thread_name "worker check pipe"
|
|
38
|
+
IO.select [@check_pipe]
|
|
39
|
+
log "! Detected parent died, dying"
|
|
40
|
+
exit! 1
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# If we're not running under a Bundler context, then
|
|
44
|
+
# report the info about the context we will be using
|
|
45
|
+
if !ENV['BUNDLE_GEMFILE']
|
|
46
|
+
if File.exist?("Gemfile")
|
|
47
|
+
log "+ Gemfile in context: #{File.expand_path("Gemfile")}"
|
|
48
|
+
elsif File.exist?("gems.rb")
|
|
49
|
+
log "+ Gemfile in context: #{File.expand_path("gems.rb")}"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Invoke any worker boot hooks so they can get
|
|
54
|
+
# things in shape before booting the app.
|
|
55
|
+
@launcher.config.run_hooks :before_worker_boot, index, @launcher.events
|
|
56
|
+
|
|
57
|
+
server = @server ||= start_server
|
|
58
|
+
restart_server = Queue.new << true << false
|
|
59
|
+
|
|
60
|
+
fork_worker = @options[:fork_worker] && index == 0
|
|
61
|
+
|
|
62
|
+
if fork_worker
|
|
63
|
+
restart_server.clear
|
|
64
|
+
worker_pids = []
|
|
65
|
+
Signal.trap "SIGCHLD" do
|
|
66
|
+
wakeup! if worker_pids.reject! do |p|
|
|
67
|
+
Process.wait(p, Process::WNOHANG) rescue true
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
Thread.new do
|
|
72
|
+
Puma.set_thread_name "worker fork pipe"
|
|
73
|
+
while (idx = @fork_pipe.gets)
|
|
74
|
+
idx = idx.to_i
|
|
75
|
+
if idx == -1 # stop server
|
|
76
|
+
if restart_server.length > 0
|
|
77
|
+
restart_server.clear
|
|
78
|
+
server.begin_restart(true)
|
|
79
|
+
@launcher.config.run_hooks :before_refork, nil, @launcher.events
|
|
80
|
+
Puma::Util.nakayoshi_gc @events if @options[:nakayoshi_fork]
|
|
81
|
+
end
|
|
82
|
+
elsif idx == 0 # restart server
|
|
83
|
+
restart_server << true << false
|
|
84
|
+
else # fork worker
|
|
85
|
+
worker_pids << pid = spawn_worker(idx)
|
|
86
|
+
@worker_write << "f#{pid}:#{idx}\n" rescue nil
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
Signal.trap "SIGTERM" do
|
|
93
|
+
@worker_write << "e#{Process.pid}\n" rescue nil
|
|
94
|
+
restart_server.clear
|
|
95
|
+
server.stop
|
|
96
|
+
restart_server << false
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
begin
|
|
100
|
+
@worker_write << "b#{Process.pid}:#{index}\n"
|
|
101
|
+
rescue SystemCallError, IOError
|
|
102
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
|
103
|
+
STDERR.puts "Master seems to have exited, exiting."
|
|
104
|
+
return
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
while restart_server.pop
|
|
108
|
+
server_thread = server.run
|
|
109
|
+
stat_thread ||= Thread.new(@worker_write) do |io|
|
|
110
|
+
Puma.set_thread_name "stat payload"
|
|
111
|
+
base_payload = "p#{Process.pid}"
|
|
112
|
+
|
|
113
|
+
while true
|
|
114
|
+
begin
|
|
115
|
+
b = server.backlog || 0
|
|
116
|
+
r = server.running || 0
|
|
117
|
+
t = server.pool_capacity || 0
|
|
118
|
+
m = server.max_threads || 0
|
|
119
|
+
rc = server.requests_count || 0
|
|
120
|
+
payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads": #{m}, "requests_count": #{rc} }\n!
|
|
121
|
+
io << payload
|
|
122
|
+
rescue IOError
|
|
123
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
|
124
|
+
break
|
|
125
|
+
end
|
|
126
|
+
sleep Const::WORKER_CHECK_INTERVAL
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
server_thread.join
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Invoke any worker shutdown hooks so they can prevent the worker
|
|
133
|
+
# exiting until any background operations are completed
|
|
134
|
+
@launcher.config.run_hooks :before_worker_shutdown, index, @launcher.events
|
|
135
|
+
ensure
|
|
136
|
+
@worker_write << "t#{Process.pid}\n" rescue nil
|
|
137
|
+
@worker_write.close
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
private
|
|
141
|
+
|
|
142
|
+
def spawn_worker(idx)
|
|
143
|
+
@launcher.config.run_hooks :before_worker_fork, idx, @launcher.events
|
|
144
|
+
|
|
145
|
+
pid = fork do
|
|
146
|
+
new_worker = Worker.new index: idx,
|
|
147
|
+
master: master,
|
|
148
|
+
launcher: @launcher,
|
|
149
|
+
pipes: { check_pipe: @check_pipe,
|
|
150
|
+
worker_write: @worker_write },
|
|
151
|
+
server: @server
|
|
152
|
+
new_worker.run
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
if !pid
|
|
156
|
+
log "! Complete inability to spawn new workers detected"
|
|
157
|
+
log "! Seppuku is the only choice."
|
|
158
|
+
exit! 1
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
@launcher.config.run_hooks :after_worker_fork, idx, @launcher.events
|
|
162
|
+
pid
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def wakeup!
|
|
166
|
+
return unless @wakeup
|
|
167
|
+
|
|
168
|
+
begin
|
|
169
|
+
@wakeup.write "!" unless @wakeup.closed?
|
|
170
|
+
rescue SystemCallError, IOError
|
|
171
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Puma
|
|
4
|
+
class Cluster < Runner
|
|
5
|
+
# This class represents a worker process from the perspective of the puma
|
|
6
|
+
# master process. It contains information about the process and its health
|
|
7
|
+
# and it exposes methods to control the process via IPC. It does not
|
|
8
|
+
# include the actual logic executed by the worker process itself. For that,
|
|
9
|
+
# see Puma::Cluster::Worker.
|
|
10
|
+
class WorkerHandle
|
|
11
|
+
def initialize(idx, pid, phase, options)
|
|
12
|
+
@index = idx
|
|
13
|
+
@pid = pid
|
|
14
|
+
@phase = phase
|
|
15
|
+
@stage = :started
|
|
16
|
+
@signal = "TERM"
|
|
17
|
+
@options = options
|
|
18
|
+
@first_term_sent = nil
|
|
19
|
+
@started_at = Time.now
|
|
20
|
+
@last_checkin = Time.now
|
|
21
|
+
@last_status = {}
|
|
22
|
+
@term = false
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
attr_reader :index, :pid, :phase, :signal, :last_checkin, :last_status, :started_at
|
|
26
|
+
|
|
27
|
+
# @version 5.0.0
|
|
28
|
+
attr_writer :pid, :phase
|
|
29
|
+
|
|
30
|
+
def booted?
|
|
31
|
+
@stage == :booted
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def boot!
|
|
35
|
+
@last_checkin = Time.now
|
|
36
|
+
@stage = :booted
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def term?
|
|
40
|
+
@term
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def ping!(status)
|
|
44
|
+
@last_checkin = Time.now
|
|
45
|
+
captures = status.match(/{ "backlog":(?<backlog>\d*), "running":(?<running>\d*), "pool_capacity":(?<pool_capacity>\d*), "max_threads": (?<max_threads>\d*), "requests_count": (?<requests_count>\d*) }/)
|
|
46
|
+
@last_status = captures.names.inject({}) do |hash, key|
|
|
47
|
+
hash[key.to_sym] = captures[key].to_i
|
|
48
|
+
hash
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @see Puma::Cluster#check_workers
|
|
53
|
+
# @version 5.0.0
|
|
54
|
+
def ping_timeout
|
|
55
|
+
@last_checkin +
|
|
56
|
+
(booted? ?
|
|
57
|
+
@options[:worker_timeout] :
|
|
58
|
+
@options[:worker_boot_timeout]
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def term
|
|
63
|
+
begin
|
|
64
|
+
if @first_term_sent && (Time.now - @first_term_sent) > @options[:worker_shutdown_timeout]
|
|
65
|
+
@signal = "KILL"
|
|
66
|
+
else
|
|
67
|
+
@term ||= true
|
|
68
|
+
@first_term_sent ||= Time.now
|
|
69
|
+
end
|
|
70
|
+
Process.kill @signal, @pid if @pid
|
|
71
|
+
rescue Errno::ESRCH
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def kill
|
|
76
|
+
@signal = 'KILL'
|
|
77
|
+
term
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def hup
|
|
81
|
+
Process.kill "HUP", @pid
|
|
82
|
+
rescue Errno::ESRCH
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
data/lib/puma/configuration.rb
CHANGED
|
@@ -92,6 +92,12 @@ module Puma
|
|
|
92
92
|
end
|
|
93
93
|
end
|
|
94
94
|
end
|
|
95
|
+
|
|
96
|
+
def final_options
|
|
97
|
+
default_options
|
|
98
|
+
.merge(file_options)
|
|
99
|
+
.merge(user_options)
|
|
100
|
+
end
|
|
95
101
|
end
|
|
96
102
|
|
|
97
103
|
# The main configuration class of Puma.
|
|
@@ -108,16 +114,17 @@ module Puma
|
|
|
108
114
|
#
|
|
109
115
|
# It also handles loading plugins.
|
|
110
116
|
#
|
|
111
|
-
#
|
|
117
|
+
# [Note:]
|
|
118
|
+
# `:port` and `:host` are not valid keys. By the time they make it to the
|
|
112
119
|
# configuration options they are expected to be incorporated into a `:binds` key.
|
|
113
120
|
# Under the hood the DSL maps `port` and `host` calls to `:binds`
|
|
114
121
|
#
|
|
115
|
-
#
|
|
116
|
-
#
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
#
|
|
122
|
+
# config = Configuration.new({}) do |user_config, file_config, default_config|
|
|
123
|
+
# user_config.port 3003
|
|
124
|
+
# end
|
|
125
|
+
# config.load
|
|
126
|
+
# puts config.options[:port]
|
|
127
|
+
# # => 3003
|
|
121
128
|
#
|
|
122
129
|
# It is expected that `load` is called on the configuration instance after setting
|
|
123
130
|
# config. This method expands any values in `config_file` and puts them into the
|
|
@@ -197,7 +204,9 @@ module Puma
|
|
|
197
204
|
:logger => STDOUT,
|
|
198
205
|
:persistent_timeout => Const::PERSISTENT_TIMEOUT,
|
|
199
206
|
:first_data_timeout => Const::FIRST_DATA_TIMEOUT,
|
|
200
|
-
:raise_exception_on_sigterm => true
|
|
207
|
+
:raise_exception_on_sigterm => true,
|
|
208
|
+
:max_fast_inline => Const::MAX_FAST_INLINE,
|
|
209
|
+
:io_selector_backend => :auto
|
|
201
210
|
}
|
|
202
211
|
end
|
|
203
212
|
|
|
@@ -288,6 +297,10 @@ module Puma
|
|
|
288
297
|
end
|
|
289
298
|
end
|
|
290
299
|
|
|
300
|
+
def final_options
|
|
301
|
+
@options.final_options
|
|
302
|
+
end
|
|
303
|
+
|
|
291
304
|
def self.temp_path
|
|
292
305
|
require 'tmpdir'
|
|
293
306
|
|
data/lib/puma/const.rb
CHANGED
|
@@ -100,8 +100,8 @@ module Puma
|
|
|
100
100
|
# too taxing on performance.
|
|
101
101
|
module Const
|
|
102
102
|
|
|
103
|
-
PUMA_VERSION = VERSION = "5.0
|
|
104
|
-
CODE_NAME = "
|
|
103
|
+
PUMA_VERSION = VERSION = "5.2.0".freeze
|
|
104
|
+
CODE_NAME = "Fettisdagsbulle".freeze
|
|
105
105
|
|
|
106
106
|
PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
|
|
107
107
|
|
|
@@ -228,7 +228,6 @@ module Puma
|
|
|
228
228
|
COLON = ": ".freeze
|
|
229
229
|
|
|
230
230
|
NEWLINE = "\n".freeze
|
|
231
|
-
HTTP_INJECTION_REGEX = /[\r\n]/.freeze
|
|
232
231
|
|
|
233
232
|
HIJACK_P = "rack.hijack?".freeze
|
|
234
233
|
HIJACK = "rack.hijack".freeze
|
|
@@ -239,5 +238,14 @@ module Puma
|
|
|
239
238
|
# Mininum interval to checks worker health
|
|
240
239
|
WORKER_CHECK_INTERVAL = 5
|
|
241
240
|
|
|
241
|
+
# Illegal character in the key or value of response header
|
|
242
|
+
DQUOTE = "\"".freeze
|
|
243
|
+
HTTP_HEADER_DELIMITER = Regexp.escape("(),/:;<=>?@[]{}\\").freeze
|
|
244
|
+
ILLEGAL_HEADER_KEY_REGEX = /[\x00-\x20#{DQUOTE}#{HTTP_HEADER_DELIMITER}]/.freeze
|
|
245
|
+
# header values can contain HTAB?
|
|
246
|
+
ILLEGAL_HEADER_VALUE_REGEX = /[\x00-\x08\x0A-\x1F]/.freeze
|
|
247
|
+
|
|
248
|
+
# Banned keys of response header
|
|
249
|
+
BANNED_HEADER_KEY = /\A(rack\.|status\z)/.freeze
|
|
242
250
|
end
|
|
243
251
|
end
|
data/lib/puma/control_cli.rb
CHANGED
|
@@ -11,10 +11,32 @@ require 'socket'
|
|
|
11
11
|
module Puma
|
|
12
12
|
class ControlCLI
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
# values must be string or nil
|
|
15
|
+
# value of `nil` means command cannot be processed via signal
|
|
16
|
+
# @version 5.0.3
|
|
17
|
+
CMD_PATH_SIG_MAP = {
|
|
18
|
+
'gc' => nil,
|
|
19
|
+
'gc-stats' => nil,
|
|
20
|
+
'halt' => 'SIGQUIT',
|
|
21
|
+
'phased-restart' => 'SIGUSR1',
|
|
22
|
+
'refork' => 'SIGURG',
|
|
23
|
+
'reload-worker-directory' => nil,
|
|
24
|
+
'restart' => 'SIGUSR2',
|
|
25
|
+
'start' => nil,
|
|
26
|
+
'stats' => nil,
|
|
27
|
+
'status' => '',
|
|
28
|
+
'stop' => 'SIGTERM',
|
|
29
|
+
'thread-backtraces' => nil
|
|
30
|
+
}.freeze
|
|
31
|
+
|
|
32
|
+
# @deprecated 6.0.0
|
|
33
|
+
COMMANDS = CMD_PATH_SIG_MAP.keys.freeze
|
|
34
|
+
|
|
35
|
+
# commands that cannot be used in a request
|
|
36
|
+
NO_REQ_COMMANDS = %w{refork}.freeze
|
|
15
37
|
|
|
16
38
|
# @version 5.0.0
|
|
17
|
-
PRINTABLE_COMMANDS = %w{gc-stats stats thread-backtraces}
|
|
39
|
+
PRINTABLE_COMMANDS = %w{gc-stats stats thread-backtraces}.freeze
|
|
18
40
|
|
|
19
41
|
def initialize(argv, stdout=STDOUT, stderr=STDERR)
|
|
20
42
|
@state = nil
|
|
@@ -33,7 +55,7 @@ module Puma
|
|
|
33
55
|
@cli_options = {}
|
|
34
56
|
|
|
35
57
|
opts = OptionParser.new do |o|
|
|
36
|
-
o.banner = "Usage: pumactl (-p PID | -P pidfile | -S status_file | -C url -T token | -F config.rb) (#{
|
|
58
|
+
o.banner = "Usage: pumactl (-p PID | -P pidfile | -S status_file | -C url -T token | -F config.rb) (#{CMD_PATH_SIG_MAP.keys.join("|")})"
|
|
37
59
|
|
|
38
60
|
o.on "-S", "--state PATH", "Where the state file to use is" do |arg|
|
|
39
61
|
@state = arg
|
|
@@ -74,7 +96,7 @@ module Puma
|
|
|
74
96
|
end
|
|
75
97
|
|
|
76
98
|
o.on_tail("-V", "--version", "Show version") do
|
|
77
|
-
puts Const::PUMA_VERSION
|
|
99
|
+
@stdout.puts Const::PUMA_VERSION
|
|
78
100
|
exit
|
|
79
101
|
end
|
|
80
102
|
end
|
|
@@ -86,10 +108,10 @@ module Puma
|
|
|
86
108
|
|
|
87
109
|
# check presence of command
|
|
88
110
|
unless @command
|
|
89
|
-
raise "Available commands: #{
|
|
111
|
+
raise "Available commands: #{CMD_PATH_SIG_MAP.keys.join(", ")}"
|
|
90
112
|
end
|
|
91
113
|
|
|
92
|
-
unless
|
|
114
|
+
unless CMD_PATH_SIG_MAP.key? @command
|
|
93
115
|
raise "Invalid command: #{@command}"
|
|
94
116
|
end
|
|
95
117
|
|
|
@@ -134,7 +156,7 @@ module Puma
|
|
|
134
156
|
@pid = sf.pid
|
|
135
157
|
elsif @pidfile
|
|
136
158
|
# get pid from pid_file
|
|
137
|
-
File.
|
|
159
|
+
@pid = File.read(@pidfile, mode: 'rb:UTF-8').to_i
|
|
138
160
|
end
|
|
139
161
|
end
|
|
140
162
|
|
|
@@ -142,24 +164,27 @@ module Puma
|
|
|
142
164
|
uri = URI.parse @control_url
|
|
143
165
|
|
|
144
166
|
# create server object by scheme
|
|
145
|
-
server =
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
167
|
+
server =
|
|
168
|
+
case uri.scheme
|
|
169
|
+
when 'ssl'
|
|
170
|
+
require 'openssl'
|
|
171
|
+
OpenSSL::SSL::SSLSocket.new(
|
|
172
|
+
TCPSocket.new(uri.host, uri.port),
|
|
173
|
+
OpenSSL::SSL::SSLContext.new)
|
|
174
|
+
.tap { |ssl| ssl.sync_close = true } # default is false
|
|
175
|
+
.tap(&:connect)
|
|
176
|
+
when 'tcp'
|
|
177
|
+
TCPSocket.new uri.host, uri.port
|
|
178
|
+
when 'unix'
|
|
179
|
+
UNIXSocket.new "#{uri.host}#{uri.path}"
|
|
180
|
+
else
|
|
181
|
+
raise "Invalid scheme: #{uri.scheme}"
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
if @command == 'status'
|
|
185
|
+
message 'Puma is started'
|
|
186
|
+
elsif NO_REQ_COMMANDS.include? @command
|
|
187
|
+
raise "Invalid request command: #{@command}"
|
|
163
188
|
else
|
|
164
189
|
url = "/#{@command}"
|
|
165
190
|
|
|
@@ -167,10 +192,10 @@ module Puma
|
|
|
167
192
|
url = url + "?token=#{@control_auth_token}"
|
|
168
193
|
end
|
|
169
194
|
|
|
170
|
-
server
|
|
195
|
+
server.syswrite "GET #{url} HTTP/1.0\r\n\r\n"
|
|
171
196
|
|
|
172
197
|
unless data = server.read
|
|
173
|
-
raise
|
|
198
|
+
raise 'Server closed connection before responding'
|
|
174
199
|
end
|
|
175
200
|
|
|
176
201
|
response = data.split("\r\n")
|
|
@@ -179,13 +204,13 @@ module Puma
|
|
|
179
204
|
raise "Server sent empty response"
|
|
180
205
|
end
|
|
181
206
|
|
|
182
|
-
|
|
207
|
+
@http, @code, @message = response.first.split(' ',3)
|
|
183
208
|
|
|
184
|
-
if @code ==
|
|
185
|
-
raise
|
|
186
|
-
elsif @code ==
|
|
209
|
+
if @code == '403'
|
|
210
|
+
raise 'Unauthorized access to server (wrong auth token)'
|
|
211
|
+
elsif @code == '404'
|
|
187
212
|
raise "Command error: #{response.last}"
|
|
188
|
-
elsif @code !=
|
|
213
|
+
elsif @code != '200'
|
|
189
214
|
raise "Bad response from server: #{@code}"
|
|
190
215
|
end
|
|
191
216
|
|
|
@@ -194,7 +219,7 @@ module Puma
|
|
|
194
219
|
end
|
|
195
220
|
ensure
|
|
196
221
|
if server
|
|
197
|
-
if uri.scheme ==
|
|
222
|
+
if uri.scheme == 'ssl'
|
|
198
223
|
server.sysclose
|
|
199
224
|
else
|
|
200
225
|
server.close unless server.closed?
|
|
@@ -204,51 +229,30 @@ module Puma
|
|
|
204
229
|
|
|
205
230
|
def send_signal
|
|
206
231
|
unless @pid
|
|
207
|
-
raise
|
|
232
|
+
raise 'Neither pid nor control url available'
|
|
208
233
|
end
|
|
209
234
|
|
|
210
235
|
begin
|
|
236
|
+
sig = CMD_PATH_SIG_MAP[@command]
|
|
211
237
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
when "halt"
|
|
217
|
-
Process.kill "QUIT", @pid
|
|
218
|
-
|
|
219
|
-
when "stop"
|
|
220
|
-
Process.kill "SIGTERM", @pid
|
|
221
|
-
|
|
222
|
-
when "stats"
|
|
223
|
-
puts "Stats not available via pid only"
|
|
224
|
-
return
|
|
225
|
-
|
|
226
|
-
when "reload-worker-directory"
|
|
227
|
-
puts "reload-worker-directory not available via pid only"
|
|
238
|
+
if sig.nil?
|
|
239
|
+
@stdout.puts "'#{@command}' not available via pid only"
|
|
240
|
+
@stdout.flush unless @stdout.sync
|
|
228
241
|
return
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
when "status"
|
|
242
|
+
elsif sig.start_with? 'SIG'
|
|
243
|
+
Process.kill sig, @pid
|
|
244
|
+
elsif @command == 'status'
|
|
234
245
|
begin
|
|
235
246
|
Process.kill 0, @pid
|
|
236
|
-
puts
|
|
247
|
+
@stdout.puts 'Puma is started'
|
|
248
|
+
@stdout.flush unless @stdout.sync
|
|
237
249
|
rescue Errno::ESRCH
|
|
238
|
-
raise
|
|
250
|
+
raise 'Puma is not running'
|
|
239
251
|
end
|
|
240
|
-
|
|
241
|
-
return
|
|
242
|
-
|
|
243
|
-
when "refork"
|
|
244
|
-
Process.kill "SIGURG", @pid
|
|
245
|
-
|
|
246
|
-
else
|
|
247
252
|
return
|
|
248
253
|
end
|
|
249
|
-
|
|
250
254
|
rescue SystemCallError
|
|
251
|
-
if @command ==
|
|
255
|
+
if @command == 'restart'
|
|
252
256
|
start
|
|
253
257
|
else
|
|
254
258
|
raise "No pid '#{@pid}' found"
|
|
@@ -259,14 +263,13 @@ module Puma
|
|
|
259
263
|
end
|
|
260
264
|
|
|
261
265
|
def run
|
|
262
|
-
return start if @command ==
|
|
263
|
-
|
|
266
|
+
return start if @command == 'start'
|
|
264
267
|
prepare_configuration
|
|
265
268
|
|
|
266
|
-
if Puma.windows?
|
|
269
|
+
if Puma.windows? || @control_url
|
|
267
270
|
send_request
|
|
268
271
|
else
|
|
269
|
-
|
|
272
|
+
send_signal
|
|
270
273
|
end
|
|
271
274
|
|
|
272
275
|
rescue => e
|