puma 3.11.2 → 6.0.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 +5 -5
- data/History.md +1708 -422
- 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/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 +66 -0
- data/docs/nginx.md +1 -1
- 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 +100 -115
- 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 -35
- data/lib/puma/client.rb +369 -232
- 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 +124 -77
- data/lib/puma/detect.rb +33 -2
- data/lib/puma/dsl.rb +685 -138
- data/lib/puma/error_logger.rb +112 -0
- data/lib/puma/events.rb +17 -111
- data/lib/puma/io_buffer.rb +34 -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 +197 -130
- data/lib/puma/log_writer.rb +137 -0
- data/lib/puma/minissl/context_builder.rb +92 -0
- data/lib/puma/minissl.rb +256 -70
- 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 +607 -0
- data/lib/puma/runner.rb +94 -71
- data/lib/puma/server.rb +336 -703
- 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 +185 -91
- data/lib/puma/util.rb +23 -10
- data/lib/puma.rb +68 -3
- data/lib/rack/handler/puma.rb +17 -14
- data/tools/Dockerfile +16 -0
- data/tools/trickletest.rb +0 -1
- metadata +53 -30
- 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,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,24 +86,43 @@ 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
|
70
104
|
|
71
105
|
opts.order!(argv) { |a| opts.terminate a }
|
106
|
+
opts.parse!
|
72
107
|
|
73
108
|
@command = argv.shift
|
74
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
|
+
|
75
119
|
unless @config_file == '-'
|
76
|
-
|
77
|
-
|
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
|
78
126
|
end
|
79
127
|
|
80
128
|
if @config_file
|
@@ -86,19 +134,8 @@ module Puma
|
|
86
134
|
@pidfile ||= config.options[:pidfile]
|
87
135
|
end
|
88
136
|
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
137
|
rescue => e
|
100
138
|
@stdout.puts e.message
|
101
|
-
@stdout.puts e.backtrace
|
102
139
|
exit 1
|
103
140
|
end
|
104
141
|
|
@@ -120,7 +157,7 @@ module Puma
|
|
120
157
|
@pid = sf.pid
|
121
158
|
elsif @pidfile
|
122
159
|
# get pid from pid_file
|
123
|
-
@pid = File.
|
160
|
+
@pid = File.read(@pidfile, mode: 'rb:UTF-8').to_i
|
124
161
|
end
|
125
162
|
end
|
126
163
|
|
@@ -128,17 +165,27 @@ module Puma
|
|
128
165
|
uri = URI.parse @control_url
|
129
166
|
|
130
167
|
# create server object by scheme
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
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'
|
142
189
|
else
|
143
190
|
url = "/#{@command}"
|
144
191
|
|
@@ -146,10 +193,10 @@ module Puma
|
|
146
193
|
url = url + "?token=#{@control_auth_token}"
|
147
194
|
end
|
148
195
|
|
149
|
-
|
196
|
+
server.syswrite "GET #{url} HTTP/1.0\r\n\r\n"
|
150
197
|
|
151
|
-
unless data =
|
152
|
-
raise
|
198
|
+
unless data = server.read
|
199
|
+
raise 'Server closed connection before responding'
|
153
200
|
end
|
154
201
|
|
155
202
|
response = data.split("\r\n")
|
@@ -158,58 +205,59 @@ module Puma
|
|
158
205
|
raise "Server sent empty response"
|
159
206
|
end
|
160
207
|
|
161
|
-
|
208
|
+
@http, @code, @message = response.first.split(' ',3)
|
162
209
|
|
163
|
-
if @code ==
|
164
|
-
raise
|
165
|
-
elsif @code ==
|
210
|
+
if @code == '403'
|
211
|
+
raise 'Unauthorized access to server (wrong auth token)'
|
212
|
+
elsif @code == '404'
|
166
213
|
raise "Command error: #{response.last}"
|
167
|
-
elsif @code !=
|
214
|
+
elsif @code != '200'
|
168
215
|
raise "Bad response from server: #{@code}"
|
169
216
|
end
|
170
217
|
|
171
218
|
message "Command #{@command} sent success"
|
172
|
-
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
|
173
228
|
end
|
174
|
-
|
175
|
-
@server.close
|
176
229
|
end
|
177
230
|
|
178
231
|
def send_signal
|
179
232
|
unless @pid
|
180
|
-
raise
|
233
|
+
raise 'Neither pid nor control url available'
|
181
234
|
end
|
182
235
|
|
183
236
|
begin
|
237
|
+
sig = CMD_PATH_SIG_MAP[@command]
|
184
238
|
|
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"
|
197
|
-
return
|
198
|
-
|
199
|
-
when "reload-worker-directory"
|
200
|
-
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
|
201
242
|
return
|
202
|
-
|
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
|