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