puma 3.11.4 → 6.0.1
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 +1717 -432
- data/LICENSE +23 -20
- data/README.md +190 -64
- 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 +95 -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 +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 +376 -93
- 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 +250 -88
- data/ext/puma_http11/puma_http11.c +49 -57
- data/lib/puma/app/status.rb +71 -49
- data/lib/puma/binder.rb +243 -148
- data/lib/puma/cli.rb +50 -36
- data/lib/puma/client.rb +373 -233
- data/lib/puma/cluster/worker.rb +175 -0
- data/lib/puma/cluster/worker_handle.rb +97 -0
- data/lib/puma/cluster.rb +268 -235
- data/lib/puma/commonlogger.rb +4 -2
- data/lib/puma/configuration.rb +116 -88
- data/lib/puma/const.rb +49 -30
- data/lib/puma/control_cli.rb +123 -76
- data/lib/puma/detect.rb +33 -2
- data/lib/puma/dsl.rb +685 -135
- data/lib/puma/error_logger.rb +112 -0
- data/lib/puma/events.rb +17 -111
- data/lib/puma/io_buffer.rb +44 -5
- data/lib/puma/jruby_restart.rb +4 -59
- data/lib/puma/json_serialization.rb +96 -0
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +196 -130
- data/lib/puma/log_writer.rb +137 -0
- data/lib/puma/minissl/context_builder.rb +92 -0
- data/lib/puma/minissl.rb +249 -69
- data/lib/puma/null_io.rb +20 -1
- data/lib/puma/plugin/tmp_restart.rb +3 -1
- data/lib/puma/plugin.rb +9 -13
- data/lib/puma/rack/builder.rb +8 -9
- data/lib/puma/rack/urlmap.rb +2 -0
- data/lib/puma/rack_default.rb +3 -1
- data/lib/puma/reactor.rb +90 -187
- data/lib/puma/request.rb +644 -0
- data/lib/puma/runner.rb +94 -71
- data/lib/puma/server.rb +337 -715
- data/lib/puma/single.rb +27 -72
- data/lib/puma/state_file.rb +46 -7
- data/lib/puma/systemd.rb +47 -0
- data/lib/puma/thread_pool.rb +184 -93
- data/lib/puma/util.rb +23 -10
- data/lib/puma.rb +60 -3
- data/lib/rack/handler/puma.rb +17 -15
- data/tools/Dockerfile +16 -0
- data/tools/trickletest.rb +0 -1
- metadata +53 -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 -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
@@ -1,15 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'optparse'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
4
|
+
require_relative 'state_file'
|
5
|
+
require_relative 'const'
|
6
|
+
require_relative 'detect'
|
7
|
+
require_relative 'configuration'
|
6
8
|
require 'uri'
|
7
9
|
require 'socket'
|
8
10
|
|
9
11
|
module Puma
|
10
12
|
class ControlCLI
|
11
13
|
|
12
|
-
|
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
|
13
41
|
|
14
42
|
def initialize(argv, stdout=STDOUT, stderr=STDERR)
|
15
43
|
@state = nil
|
@@ -20,6 +48,7 @@ module Puma
|
|
20
48
|
@control_auth_token = nil
|
21
49
|
@config_file = nil
|
22
50
|
@command = nil
|
51
|
+
@environment = ENV['APP_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV']
|
23
52
|
|
24
53
|
@argv = argv.dup
|
25
54
|
@stdout = stdout
|
@@ -27,7 +56,7 @@ module Puma
|
|
27
56
|
@cli_options = {}
|
28
57
|
|
29
58
|
opts = OptionParser.new do |o|
|
30
|
-
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("|")})"
|
31
60
|
|
32
61
|
o.on "-S", "--state PATH", "Where the state file to use is" do |arg|
|
33
62
|
@state = arg
|
@@ -57,13 +86,18 @@ module Puma
|
|
57
86
|
@config_file = arg
|
58
87
|
end
|
59
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
|
+
|
60
94
|
o.on_tail("-H", "--help", "Show this message") do
|
61
95
|
@stdout.puts o
|
62
96
|
exit
|
63
97
|
end
|
64
98
|
|
65
99
|
o.on_tail("-V", "--version", "Show version") do
|
66
|
-
puts Const::PUMA_VERSION
|
100
|
+
@stdout.puts Const::PUMA_VERSION
|
67
101
|
exit
|
68
102
|
end
|
69
103
|
end
|
@@ -73,9 +107,22 @@ module Puma
|
|
73
107
|
|
74
108
|
@command = argv.shift
|
75
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
|
+
|
76
119
|
unless @config_file == '-'
|
77
|
-
|
78
|
-
|
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
|
79
126
|
end
|
80
127
|
|
81
128
|
if @config_file
|
@@ -87,19 +134,8 @@ module Puma
|
|
87
134
|
@pidfile ||= config.options[:pidfile]
|
88
135
|
end
|
89
136
|
end
|
90
|
-
|
91
|
-
# check present of command
|
92
|
-
unless @command
|
93
|
-
raise "Available commands: #{COMMANDS.join(", ")}"
|
94
|
-
end
|
95
|
-
|
96
|
-
unless COMMANDS.include? @command
|
97
|
-
raise "Invalid command: #{@command}"
|
98
|
-
end
|
99
|
-
|
100
137
|
rescue => e
|
101
138
|
@stdout.puts e.message
|
102
|
-
@stdout.puts e.backtrace
|
103
139
|
exit 1
|
104
140
|
end
|
105
141
|
|
@@ -121,7 +157,7 @@ module Puma
|
|
121
157
|
@pid = sf.pid
|
122
158
|
elsif @pidfile
|
123
159
|
# get pid from pid_file
|
124
|
-
@pid = File.
|
160
|
+
@pid = File.read(@pidfile, mode: 'rb:UTF-8').to_i
|
125
161
|
end
|
126
162
|
end
|
127
163
|
|
@@ -129,17 +165,27 @@ module Puma
|
|
129
165
|
uri = URI.parse @control_url
|
130
166
|
|
131
167
|
# create server object by scheme
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
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'
|
143
189
|
else
|
144
190
|
url = "/#{@command}"
|
145
191
|
|
@@ -147,10 +193,10 @@ module Puma
|
|
147
193
|
url = url + "?token=#{@control_auth_token}"
|
148
194
|
end
|
149
195
|
|
150
|
-
|
196
|
+
server.syswrite "GET #{url} HTTP/1.0\r\n\r\n"
|
151
197
|
|
152
|
-
unless data =
|
153
|
-
raise
|
198
|
+
unless data = server.read
|
199
|
+
raise 'Server closed connection before responding'
|
154
200
|
end
|
155
201
|
|
156
202
|
response = data.split("\r\n")
|
@@ -159,57 +205,59 @@ module Puma
|
|
159
205
|
raise "Server sent empty response"
|
160
206
|
end
|
161
207
|
|
162
|
-
|
208
|
+
@http, @code, @message = response.first.split(' ',3)
|
163
209
|
|
164
|
-
if @code ==
|
165
|
-
raise
|
166
|
-
elsif @code ==
|
210
|
+
if @code == '403'
|
211
|
+
raise 'Unauthorized access to server (wrong auth token)'
|
212
|
+
elsif @code == '404'
|
167
213
|
raise "Command error: #{response.last}"
|
168
|
-
elsif @code !=
|
214
|
+
elsif @code != '200'
|
169
215
|
raise "Bad response from server: #{@code}"
|
170
216
|
end
|
171
217
|
|
172
218
|
message "Command #{@command} sent success"
|
173
|
-
message response.last if @command
|
219
|
+
message response.last if PRINTABLE_COMMANDS.include?(@command)
|
220
|
+
end
|
221
|
+
ensure
|
222
|
+
if server
|
223
|
+
if uri.scheme == 'ssl'
|
224
|
+
server.sysclose
|
225
|
+
else
|
226
|
+
server.close unless server.closed?
|
227
|
+
end
|
174
228
|
end
|
175
|
-
|
176
|
-
@server.close
|
177
229
|
end
|
178
230
|
|
179
231
|
def send_signal
|
180
232
|
unless @pid
|
181
|
-
raise
|
233
|
+
raise 'Neither pid nor control url available'
|
182
234
|
end
|
183
235
|
|
184
236
|
begin
|
237
|
+
sig = CMD_PATH_SIG_MAP[@command]
|
185
238
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
when "halt"
|
191
|
-
Process.kill "QUIT", @pid
|
192
|
-
|
193
|
-
when "stop"
|
194
|
-
Process.kill "SIGTERM", @pid
|
195
|
-
|
196
|
-
when "stats"
|
197
|
-
puts "Stats not available via pid only"
|
198
|
-
return
|
199
|
-
|
200
|
-
when "reload-worker-directory"
|
201
|
-
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
|
202
242
|
return
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
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
|
208
257
|
return
|
209
258
|
end
|
210
|
-
|
211
259
|
rescue SystemCallError
|
212
|
-
if @command ==
|
260
|
+
if @command == 'restart'
|
213
261
|
start
|
214
262
|
else
|
215
263
|
raise "No pid '#{@pid}' found"
|
@@ -220,25 +268,23 @@ module Puma
|
|
220
268
|
end
|
221
269
|
|
222
270
|
def run
|
223
|
-
start if @command ==
|
224
|
-
|
271
|
+
return start if @command == 'start'
|
225
272
|
prepare_configuration
|
226
273
|
|
227
|
-
if Puma.windows?
|
274
|
+
if Puma.windows? || @control_url && !NO_REQ_COMMANDS.include?(@command)
|
228
275
|
send_request
|
229
276
|
else
|
230
|
-
|
277
|
+
send_signal
|
231
278
|
end
|
232
279
|
|
233
280
|
rescue => e
|
234
281
|
message e.message
|
235
|
-
message e.backtrace
|
236
282
|
exit 1
|
237
283
|
end
|
238
284
|
|
239
|
-
|
285
|
+
private
|
240
286
|
def start
|
241
|
-
|
287
|
+
require_relative 'cli'
|
242
288
|
|
243
289
|
run_args = []
|
244
290
|
|
@@ -248,14 +294,15 @@ module Puma
|
|
248
294
|
run_args += ["--control-url", @control_url] if @control_url
|
249
295
|
run_args += ["--control-token", @control_auth_token] if @control_auth_token
|
250
296
|
run_args += ["-C", @config_file] if @config_file
|
297
|
+
run_args += ["-e", @environment] if @environment
|
251
298
|
|
252
|
-
|
299
|
+
log_writer = Puma::LogWriter.new(@stdout, @stderr)
|
253
300
|
|
254
301
|
# replace $0 because puma use it to generate restart command
|
255
302
|
puma_cmd = $0.gsub(/pumactl$/, 'puma')
|
256
303
|
$0 = puma_cmd if File.exist?(puma_cmd)
|
257
304
|
|
258
|
-
cli = Puma::CLI.new run_args,
|
305
|
+
cli = Puma::CLI.new run_args, log_writer
|
259
306
|
cli.run
|
260
307
|
end
|
261
308
|
end
|
data/lib/puma/detect.rb
CHANGED
@@ -1,13 +1,44 @@
|
|
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_PLATFORM.include? 'darwin'
|
16
|
+
|
17
|
+
IS_WINDOWS = !!(RUBY_PLATFORM =~ /mswin|ming|cygwin/) ||
|
18
|
+
IS_JRUBY && RUBY_DESCRIPTION.include?('mswin')
|
19
|
+
|
20
|
+
# @version 5.2.0
|
21
|
+
IS_MRI = (RUBY_ENGINE == 'ruby' || RUBY_ENGINE.nil?)
|
3
22
|
|
4
23
|
def self.jruby?
|
5
24
|
IS_JRUBY
|
6
25
|
end
|
7
26
|
|
8
|
-
|
27
|
+
def self.osx?
|
28
|
+
IS_OSX
|
29
|
+
end
|
9
30
|
|
10
31
|
def self.windows?
|
11
32
|
IS_WINDOWS
|
12
33
|
end
|
34
|
+
|
35
|
+
# @version 5.0.0
|
36
|
+
def self.mri?
|
37
|
+
IS_MRI
|
38
|
+
end
|
39
|
+
|
40
|
+
# @version 5.0.0
|
41
|
+
def self.forkable?
|
42
|
+
HAS_FORK
|
43
|
+
end
|
13
44
|
end
|