puma 3.11.1 → 6.6.0
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.md +2092 -422
- data/LICENSE +23 -20
- data/README.md +301 -69
- data/bin/puma-wild +3 -9
- data/docs/architecture.md +59 -21
- data/docs/compile_options.md +55 -0
- data/docs/deployment.md +69 -58
- data/docs/fork_worker.md +41 -0
- data/docs/java_options.md +54 -0
- data/docs/jungle/README.md +9 -0
- data/docs/jungle/rc.d/README.md +74 -0
- data/docs/jungle/rc.d/puma +61 -0
- data/docs/jungle/rc.d/puma.conf +10 -0
- data/docs/kubernetes.md +78 -0
- data/docs/nginx.md +2 -2
- data/docs/plugins.md +26 -12
- data/docs/rails_dev_mode.md +28 -0
- data/docs/restart.md +48 -22
- data/docs/signals.md +13 -11
- data/docs/stats.md +147 -0
- data/docs/systemd.md +108 -117
- data/docs/testing_benchmarks_local_files.md +150 -0
- data/docs/testing_test_rackup_ci_files.md +36 -0
- data/ext/puma_http11/PumaHttp11Service.java +2 -2
- data/ext/puma_http11/ext_help.h +1 -1
- data/ext/puma_http11/extconf.rb +68 -3
- data/ext/puma_http11/http11_parser.c +106 -118
- data/ext/puma_http11/http11_parser.h +2 -2
- data/ext/puma_http11/http11_parser.java.rl +22 -38
- data/ext/puma_http11/http11_parser.rl +6 -4
- data/ext/puma_http11/http11_parser_common.rl +6 -6
- data/ext/puma_http11/mini_ssl.c +474 -94
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +136 -121
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +84 -99
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +251 -88
- data/ext/puma_http11/puma_http11.c +53 -58
- data/lib/puma/app/status.rb +71 -49
- data/lib/puma/binder.rb +257 -151
- data/lib/puma/cli.rb +61 -38
- data/lib/puma/client.rb +464 -224
- data/lib/puma/cluster/worker.rb +183 -0
- data/lib/puma/cluster/worker_handle.rb +96 -0
- data/lib/puma/cluster.rb +343 -239
- data/lib/puma/commonlogger.rb +23 -14
- data/lib/puma/configuration.rb +144 -96
- data/lib/puma/const.rb +194 -115
- data/lib/puma/control_cli.rb +135 -81
- data/lib/puma/detect.rb +34 -2
- data/lib/puma/dsl.rb +1092 -153
- data/lib/puma/error_logger.rb +113 -0
- data/lib/puma/events.rb +17 -111
- data/lib/puma/io_buffer.rb +44 -5
- data/lib/puma/jruby_restart.rb +2 -73
- data/lib/puma/json_serialization.rb +96 -0
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +205 -138
- data/lib/puma/log_writer.rb +147 -0
- data/lib/puma/minissl/context_builder.rb +96 -0
- data/lib/puma/minissl.rb +279 -70
- data/lib/puma/null_io.rb +61 -2
- data/lib/puma/plugin/systemd.rb +90 -0
- data/lib/puma/plugin/tmp_restart.rb +3 -1
- data/lib/puma/plugin.rb +9 -13
- data/lib/puma/rack/builder.rb +10 -11
- data/lib/puma/rack/urlmap.rb +3 -1
- data/lib/puma/rack_default.rb +21 -4
- data/lib/puma/reactor.rb +97 -185
- data/lib/puma/request.rb +688 -0
- data/lib/puma/runner.rb +114 -69
- data/lib/puma/sd_notify.rb +146 -0
- data/lib/puma/server.rb +409 -704
- data/lib/puma/single.rb +29 -72
- data/lib/puma/state_file.rb +48 -9
- data/lib/puma/thread_pool.rb +234 -93
- data/lib/puma/util.rb +23 -10
- data/lib/puma.rb +68 -5
- data/lib/rack/handler/puma.rb +119 -86
- data/tools/Dockerfile +16 -0
- data/tools/trickletest.rb +0 -1
- metadata +55 -33
- data/ext/puma_http11/io_buffer.c +0 -155
- data/lib/puma/accept_nonblock.rb +0 -23
- 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/lib/puma/tcp_logger.rb +0 -39
- data/tools/jungle/README.md +0 -13
- data/tools/jungle/init.d/README.md +0 -59
- data/tools/jungle/init.d/puma +0 -421
- data/tools/jungle/init.d/run-puma +0 -18
- data/tools/jungle/upstart/README.md +0 -61
- data/tools/jungle/upstart/puma-manager.conf +0 -31
- data/tools/jungle/upstart/puma.conf +0 -69
data/lib/puma/control_cli.rb
CHANGED
@@ -1,17 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'optparse'
|
2
|
-
|
3
|
-
|
4
|
-
require 'puma/detect'
|
5
|
-
require 'puma/configuration'
|
4
|
+
require_relative 'const'
|
5
|
+
require_relative 'detect'
|
6
6
|
require 'uri'
|
7
7
|
require 'socket'
|
8
8
|
|
9
9
|
module Puma
|
10
10
|
class ControlCLI
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
# values must be string or nil
|
13
|
+
# value of `nil` means command cannot be processed via signal
|
14
|
+
# @version 5.0.3
|
15
|
+
CMD_PATH_SIG_MAP = {
|
16
|
+
'gc' => nil,
|
17
|
+
'gc-stats' => nil,
|
18
|
+
'halt' => 'SIGQUIT',
|
19
|
+
'info' => 'SIGINFO',
|
20
|
+
'phased-restart' => 'SIGUSR1',
|
21
|
+
'refork' => 'SIGURG',
|
22
|
+
'reload-worker-directory' => nil,
|
23
|
+
'reopen-log' => 'SIGHUP',
|
24
|
+
'restart' => 'SIGUSR2',
|
25
|
+
'start' => nil,
|
26
|
+
'stats' => nil,
|
27
|
+
'status' => '',
|
28
|
+
'stop' => 'SIGTERM',
|
29
|
+
'thread-backtraces' => nil,
|
30
|
+
'worker-count-down' => 'SIGTTOU',
|
31
|
+
'worker-count-up' => 'SIGTTIN'
|
32
|
+
}.freeze
|
33
|
+
|
34
|
+
# commands that cannot be used in a request
|
35
|
+
NO_REQ_COMMANDS = %w[info reopen-log worker-count-down worker-count-up].freeze
|
36
|
+
|
37
|
+
# @version 5.0.0
|
38
|
+
PRINTABLE_COMMANDS = %w[gc-stats stats thread-backtraces].freeze
|
39
|
+
|
40
|
+
def initialize(argv, stdout=STDOUT, stderr=STDERR, env: ENV)
|
15
41
|
@state = nil
|
16
42
|
@quiet = false
|
17
43
|
@pidfile = nil
|
@@ -20,6 +46,7 @@ module Puma
|
|
20
46
|
@control_auth_token = nil
|
21
47
|
@config_file = nil
|
22
48
|
@command = nil
|
49
|
+
@environment = env['APP_ENV'] || env['RACK_ENV'] || env['RAILS_ENV']
|
23
50
|
|
24
51
|
@argv = argv.dup
|
25
52
|
@stdout = stdout
|
@@ -27,13 +54,13 @@ module Puma
|
|
27
54
|
@cli_options = {}
|
28
55
|
|
29
56
|
opts = OptionParser.new do |o|
|
30
|
-
o.banner = "Usage: pumactl (-p PID | -P pidfile | -S status_file | -C url -T token | -F config.rb) (#{
|
57
|
+
o.banner = "Usage: pumactl (-p PID | -P pidfile | -S status_file | -C url -T token | -F config.rb) (#{CMD_PATH_SIG_MAP.keys.join("|")})"
|
31
58
|
|
32
59
|
o.on "-S", "--state PATH", "Where the state file to use is" do |arg|
|
33
60
|
@state = arg
|
34
61
|
end
|
35
62
|
|
36
|
-
o.on "-Q", "--quiet", "
|
63
|
+
o.on "-Q", "--quiet", "Do not display messages" do |arg|
|
37
64
|
@quiet = true
|
38
65
|
end
|
39
66
|
|
@@ -57,28 +84,50 @@ module Puma
|
|
57
84
|
@config_file = arg
|
58
85
|
end
|
59
86
|
|
87
|
+
o.on "-e", "--environment ENVIRONMENT",
|
88
|
+
"The environment to run the Rack app on (default development)" do |arg|
|
89
|
+
@environment = arg
|
90
|
+
end
|
91
|
+
|
60
92
|
o.on_tail("-H", "--help", "Show this message") do
|
61
93
|
@stdout.puts o
|
62
94
|
exit
|
63
95
|
end
|
64
96
|
|
65
97
|
o.on_tail("-V", "--version", "Show version") do
|
66
|
-
puts Const::PUMA_VERSION
|
98
|
+
@stdout.puts Const::PUMA_VERSION
|
67
99
|
exit
|
68
100
|
end
|
69
101
|
end
|
70
102
|
|
71
103
|
opts.order!(argv) { |a| opts.terminate a }
|
104
|
+
opts.parse!
|
72
105
|
|
73
106
|
@command = argv.shift
|
74
107
|
|
108
|
+
# check presence of command
|
109
|
+
unless @command
|
110
|
+
raise "Available commands: #{CMD_PATH_SIG_MAP.keys.join(", ")}"
|
111
|
+
end
|
112
|
+
|
113
|
+
unless CMD_PATH_SIG_MAP.key? @command
|
114
|
+
raise "Invalid command: #{@command}"
|
115
|
+
end
|
116
|
+
|
75
117
|
unless @config_file == '-'
|
76
|
-
|
77
|
-
|
118
|
+
environment = @environment || 'development'
|
119
|
+
|
120
|
+
if @config_file.nil?
|
121
|
+
@config_file = %W(config/puma/#{environment}.rb config/puma.rb).find do |f|
|
122
|
+
File.exist?(f)
|
123
|
+
end
|
78
124
|
end
|
79
125
|
|
80
126
|
if @config_file
|
81
|
-
|
127
|
+
require_relative 'configuration'
|
128
|
+
require_relative 'log_writer'
|
129
|
+
|
130
|
+
config = Puma::Configuration.new({ config_files: [@config_file] }, {} , env)
|
82
131
|
config.load
|
83
132
|
@state ||= config.options[:state]
|
84
133
|
@control_url ||= config.options[:control_url]
|
@@ -86,19 +135,8 @@ module Puma
|
|
86
135
|
@pidfile ||= config.options[:pidfile]
|
87
136
|
end
|
88
137
|
end
|
89
|
-
|
90
|
-
# check present of command
|
91
|
-
unless @command
|
92
|
-
raise "Available commands: #{COMMANDS.join(", ")}"
|
93
|
-
end
|
94
|
-
|
95
|
-
unless COMMANDS.include? @command
|
96
|
-
raise "Invalid command: #{@command}"
|
97
|
-
end
|
98
|
-
|
99
138
|
rescue => e
|
100
139
|
@stdout.puts e.message
|
101
|
-
@stdout.puts e.backtrace
|
102
140
|
exit 1
|
103
141
|
end
|
104
142
|
|
@@ -112,6 +150,8 @@ module Puma
|
|
112
150
|
raise "State file not found: #{@state}"
|
113
151
|
end
|
114
152
|
|
153
|
+
require_relative 'state_file'
|
154
|
+
|
115
155
|
sf = Puma::StateFile.new
|
116
156
|
sf.load @state
|
117
157
|
|
@@ -120,25 +160,39 @@ module Puma
|
|
120
160
|
@pid = sf.pid
|
121
161
|
elsif @pidfile
|
122
162
|
# get pid from pid_file
|
123
|
-
@pid = File.
|
163
|
+
@pid = File.read(@pidfile, mode: 'rb:UTF-8').to_i
|
124
164
|
end
|
125
165
|
end
|
126
166
|
|
127
167
|
def send_request
|
128
168
|
uri = URI.parse @control_url
|
129
169
|
|
170
|
+
host = uri.host
|
171
|
+
|
130
172
|
# create server object by scheme
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
173
|
+
server =
|
174
|
+
case uri.scheme
|
175
|
+
when 'ssl'
|
176
|
+
require 'openssl'
|
177
|
+
host = host[1..-2] if host&.start_with? '['
|
178
|
+
OpenSSL::SSL::SSLSocket.new(
|
179
|
+
TCPSocket.new(host, uri.port),
|
180
|
+
OpenSSL::SSL::SSLContext.new)
|
181
|
+
.tap { |ssl| ssl.sync_close = true } # default is false
|
182
|
+
.tap(&:connect)
|
183
|
+
when 'tcp'
|
184
|
+
host = host[1..-2] if host&.start_with? '['
|
185
|
+
TCPSocket.new host, uri.port
|
186
|
+
when 'unix'
|
187
|
+
# check for abstract UNIXSocket
|
188
|
+
UNIXSocket.new(@control_url.start_with?('unix://@') ?
|
189
|
+
"\0#{host}#{uri.path}" : "#{host}#{uri.path}")
|
190
|
+
else
|
191
|
+
raise "Invalid scheme: #{uri.scheme}"
|
192
|
+
end
|
193
|
+
|
194
|
+
if @command == 'status'
|
195
|
+
message 'Puma is started'
|
142
196
|
else
|
143
197
|
url = "/#{@command}"
|
144
198
|
|
@@ -146,10 +200,10 @@ module Puma
|
|
146
200
|
url = url + "?token=#{@control_auth_token}"
|
147
201
|
end
|
148
202
|
|
149
|
-
|
203
|
+
server.syswrite "GET #{url} HTTP/1.0\r\n\r\n"
|
150
204
|
|
151
|
-
unless data =
|
152
|
-
raise
|
205
|
+
unless data = server.read
|
206
|
+
raise 'Server closed connection before responding'
|
153
207
|
end
|
154
208
|
|
155
209
|
response = data.split("\r\n")
|
@@ -158,58 +212,59 @@ module Puma
|
|
158
212
|
raise "Server sent empty response"
|
159
213
|
end
|
160
214
|
|
161
|
-
|
215
|
+
@http, @code, @message = response.first.split(' ',3)
|
162
216
|
|
163
|
-
if @code ==
|
164
|
-
raise
|
165
|
-
elsif @code ==
|
217
|
+
if @code == '403'
|
218
|
+
raise 'Unauthorized access to server (wrong auth token)'
|
219
|
+
elsif @code == '404'
|
166
220
|
raise "Command error: #{response.last}"
|
167
|
-
elsif @code !=
|
221
|
+
elsif @code != '200'
|
168
222
|
raise "Bad response from server: #{@code}"
|
169
223
|
end
|
170
224
|
|
171
225
|
message "Command #{@command} sent success"
|
172
|
-
message response.last if @command
|
226
|
+
message response.last if PRINTABLE_COMMANDS.include?(@command)
|
227
|
+
end
|
228
|
+
ensure
|
229
|
+
if server
|
230
|
+
if uri.scheme == 'ssl'
|
231
|
+
server.sysclose
|
232
|
+
else
|
233
|
+
server.close unless server.closed?
|
234
|
+
end
|
173
235
|
end
|
174
|
-
|
175
|
-
@server.close
|
176
236
|
end
|
177
237
|
|
178
238
|
def send_signal
|
179
239
|
unless @pid
|
180
|
-
raise
|
240
|
+
raise 'Neither pid nor control url available'
|
181
241
|
end
|
182
242
|
|
183
243
|
begin
|
244
|
+
sig = CMD_PATH_SIG_MAP[@command]
|
184
245
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
when "halt"
|
190
|
-
Process.kill "QUIT", @pid
|
191
|
-
|
192
|
-
when "stop"
|
193
|
-
Process.kill "SIGTERM", @pid
|
194
|
-
|
195
|
-
when "stats"
|
196
|
-
puts "Stats not available via pid only"
|
246
|
+
if sig.nil?
|
247
|
+
@stdout.puts "'#{@command}' not available via pid only"
|
248
|
+
@stdout.flush unless @stdout.sync
|
197
249
|
return
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
250
|
+
elsif sig.start_with? 'SIG'
|
251
|
+
if Signal.list.key? sig.sub(/\ASIG/, '')
|
252
|
+
Process.kill sig, @pid
|
253
|
+
else
|
254
|
+
raise "Signal '#{sig}' not available'"
|
255
|
+
end
|
256
|
+
elsif @command == 'status'
|
257
|
+
begin
|
258
|
+
Process.kill 0, @pid
|
259
|
+
@stdout.puts 'Puma is started'
|
260
|
+
@stdout.flush unless @stdout.sync
|
261
|
+
rescue Errno::ESRCH
|
262
|
+
raise 'Puma is not running'
|
263
|
+
end
|
208
264
|
return
|
209
265
|
end
|
210
|
-
|
211
266
|
rescue SystemCallError
|
212
|
-
if @command ==
|
267
|
+
if @command == 'restart'
|
213
268
|
start
|
214
269
|
else
|
215
270
|
raise "No pid '#{@pid}' found"
|
@@ -220,25 +275,23 @@ module Puma
|
|
220
275
|
end
|
221
276
|
|
222
277
|
def run
|
223
|
-
start if @command ==
|
224
|
-
|
278
|
+
return start if @command == 'start'
|
225
279
|
prepare_configuration
|
226
280
|
|
227
|
-
if Puma.windows?
|
281
|
+
if Puma.windows? || @control_url && !NO_REQ_COMMANDS.include?(@command)
|
228
282
|
send_request
|
229
283
|
else
|
230
|
-
|
284
|
+
send_signal
|
231
285
|
end
|
232
286
|
|
233
287
|
rescue => e
|
234
288
|
message e.message
|
235
|
-
message e.backtrace
|
236
289
|
exit 1
|
237
290
|
end
|
238
291
|
|
239
|
-
|
292
|
+
private
|
240
293
|
def start
|
241
|
-
|
294
|
+
require_relative 'cli'
|
242
295
|
|
243
296
|
run_args = []
|
244
297
|
|
@@ -248,14 +301,15 @@ module Puma
|
|
248
301
|
run_args += ["--control-url", @control_url] if @control_url
|
249
302
|
run_args += ["--control-token", @control_auth_token] if @control_auth_token
|
250
303
|
run_args += ["-C", @config_file] if @config_file
|
304
|
+
run_args += ["-e", @environment] if @environment
|
251
305
|
|
252
|
-
|
306
|
+
log_writer = Puma::LogWriter.new(@stdout, @stderr)
|
253
307
|
|
254
308
|
# replace $0 because puma use it to generate restart command
|
255
309
|
puma_cmd = $0.gsub(/pumactl$/, 'puma')
|
256
310
|
$0 = puma_cmd if File.exist?(puma_cmd)
|
257
311
|
|
258
|
-
cli = Puma::CLI.new run_args,
|
312
|
+
cli = Puma::CLI.new run_args, log_writer
|
259
313
|
cli.run
|
260
314
|
end
|
261
315
|
end
|
data/lib/puma/detect.rb
CHANGED
@@ -1,13 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This file can be loaded independently of puma.rb, so it cannot have any code
|
4
|
+
# that assumes puma.rb is loaded.
|
5
|
+
|
6
|
+
|
1
7
|
module Puma
|
2
|
-
|
8
|
+
# @version 5.2.1
|
9
|
+
HAS_FORK = ::Process.respond_to? :fork
|
10
|
+
|
11
|
+
HAS_NATIVE_IO_WAIT = ::IO.public_instance_methods(false).include? :wait_readable
|
12
|
+
|
13
|
+
IS_JRUBY = Object.const_defined? :JRUBY_VERSION
|
14
|
+
|
15
|
+
IS_OSX = RUBY_DESCRIPTION.include? 'darwin'
|
16
|
+
|
17
|
+
IS_WINDOWS = RUBY_DESCRIPTION.match?(/mswin|ming|cygwin/)
|
18
|
+
|
19
|
+
IS_LINUX = !(IS_OSX || IS_WINDOWS)
|
20
|
+
|
21
|
+
# @version 5.2.0
|
22
|
+
IS_MRI = RUBY_ENGINE == 'ruby'
|
3
23
|
|
4
24
|
def self.jruby?
|
5
25
|
IS_JRUBY
|
6
26
|
end
|
7
27
|
|
8
|
-
|
28
|
+
def self.osx?
|
29
|
+
IS_OSX
|
30
|
+
end
|
9
31
|
|
10
32
|
def self.windows?
|
11
33
|
IS_WINDOWS
|
12
34
|
end
|
35
|
+
|
36
|
+
# @version 5.0.0
|
37
|
+
def self.mri?
|
38
|
+
IS_MRI
|
39
|
+
end
|
40
|
+
|
41
|
+
# @version 5.0.0
|
42
|
+
def self.forkable?
|
43
|
+
HAS_FORK
|
44
|
+
end
|
13
45
|
end
|