puma 4.3.12 → 6.0.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 +1618 -521
- data/LICENSE +23 -20
- data/README.md +130 -42
- data/bin/puma-wild +3 -9
- data/docs/architecture.md +63 -26
- data/docs/compile_options.md +55 -0
- data/docs/deployment.md +60 -69
- data/docs/fork_worker.md +31 -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 +15 -15
- data/docs/rails_dev_mode.md +28 -0
- data/docs/restart.md +46 -23
- data/docs/signals.md +13 -11
- data/docs/stats.md +142 -0
- data/docs/systemd.md +85 -128
- 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 -4
- data/ext/puma_http11/ext_help.h +1 -1
- data/ext/puma_http11/extconf.rb +49 -12
- data/ext/puma_http11/http11_parser.c +46 -48
- data/ext/puma_http11/http11_parser.h +2 -2
- data/ext/puma_http11/http11_parser.java.rl +3 -3
- data/ext/puma_http11/http11_parser.rl +3 -3
- data/ext/puma_http11/http11_parser_common.rl +2 -2
- data/ext/puma_http11/mini_ssl.c +250 -93
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +6 -6
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +4 -6
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +241 -96
- data/ext/puma_http11/puma_http11.c +46 -57
- data/lib/puma/app/status.rb +52 -38
- data/lib/puma/binder.rb +232 -119
- data/lib/puma/cli.rb +33 -33
- data/lib/puma/client.rb +129 -88
- data/lib/puma/cluster/worker.rb +175 -0
- data/lib/puma/cluster/worker_handle.rb +97 -0
- data/lib/puma/cluster.rb +224 -231
- data/lib/puma/commonlogger.rb +2 -2
- data/lib/puma/configuration.rb +112 -87
- data/lib/puma/const.rb +86 -91
- data/lib/puma/control_cli.rb +99 -79
- data/lib/puma/detect.rb +31 -2
- data/lib/puma/dsl.rb +426 -110
- data/lib/puma/error_logger.rb +112 -0
- data/lib/puma/events.rb +16 -115
- data/lib/puma/io_buffer.rb +44 -2
- 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 +170 -148
- data/lib/puma/log_writer.rb +137 -0
- data/lib/puma/minissl/context_builder.rb +35 -19
- data/lib/puma/minissl.rb +213 -55
- data/lib/puma/null_io.rb +18 -1
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/plugin.rb +3 -12
- data/lib/puma/rack/builder.rb +5 -9
- data/lib/puma/rack_default.rb +1 -1
- data/lib/puma/reactor.rb +85 -369
- data/lib/puma/request.rb +644 -0
- data/lib/puma/runner.rb +86 -76
- data/lib/puma/server.rb +306 -793
- data/lib/puma/single.rb +18 -74
- data/lib/puma/state_file.rb +45 -8
- data/lib/puma/systemd.rb +47 -0
- data/lib/puma/thread_pool.rb +136 -68
- data/lib/puma/util.rb +21 -4
- data/lib/puma.rb +54 -7
- data/lib/rack/handler/puma.rb +11 -12
- data/tools/{docker/Dockerfile → Dockerfile} +1 -1
- metadata +31 -23
- data/docs/tcp_mode.md +0 -96
- data/ext/puma_http11/io_buffer.c +0 -155
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
- data/lib/puma/accept_nonblock.rb +0 -29
- 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,7 +48,7 @@ module Puma
|
|
22
48
|
@control_auth_token = nil
|
23
49
|
@config_file = nil
|
24
50
|
@command = nil
|
25
|
-
@environment = ENV['RACK_ENV']
|
51
|
+
@environment = ENV['APP_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV']
|
26
52
|
|
27
53
|
@argv = argv.dup
|
28
54
|
@stdout = stdout
|
@@ -30,7 +56,7 @@ module Puma
|
|
30
56
|
@cli_options = {}
|
31
57
|
|
32
58
|
opts = OptionParser.new do |o|
|
33
|
-
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("|")})"
|
34
60
|
|
35
61
|
o.on "-S", "--state PATH", "Where the state file to use is" do |arg|
|
36
62
|
@state = arg
|
@@ -71,7 +97,7 @@ module Puma
|
|
71
97
|
end
|
72
98
|
|
73
99
|
o.on_tail("-V", "--version", "Show version") do
|
74
|
-
puts Const::PUMA_VERSION
|
100
|
+
@stdout.puts Const::PUMA_VERSION
|
75
101
|
exit
|
76
102
|
end
|
77
103
|
end
|
@@ -81,6 +107,15 @@ module Puma
|
|
81
107
|
|
82
108
|
@command = argv.shift
|
83
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
|
+
|
84
119
|
unless @config_file == '-'
|
85
120
|
environment = @environment || 'development'
|
86
121
|
|
@@ -99,16 +134,6 @@ module Puma
|
|
99
134
|
@pidfile ||= config.options[:pidfile]
|
100
135
|
end
|
101
136
|
end
|
102
|
-
|
103
|
-
# check present of command
|
104
|
-
unless @command
|
105
|
-
raise "Available commands: #{COMMANDS.join(", ")}"
|
106
|
-
end
|
107
|
-
|
108
|
-
unless COMMANDS.include? @command
|
109
|
-
raise "Invalid command: #{@command}"
|
110
|
-
end
|
111
|
-
|
112
137
|
rescue => e
|
113
138
|
@stdout.puts e.message
|
114
139
|
exit 1
|
@@ -132,7 +157,7 @@ module Puma
|
|
132
157
|
@pid = sf.pid
|
133
158
|
elsif @pidfile
|
134
159
|
# get pid from pid_file
|
135
|
-
File.
|
160
|
+
@pid = File.read(@pidfile, mode: 'rb:UTF-8').to_i
|
136
161
|
end
|
137
162
|
end
|
138
163
|
|
@@ -140,23 +165,27 @@ module Puma
|
|
140
165
|
uri = URI.parse @control_url
|
141
166
|
|
142
167
|
# create server object by scheme
|
143
|
-
server =
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
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'
|
160
189
|
else
|
161
190
|
url = "/#{@command}"
|
162
191
|
|
@@ -164,10 +193,10 @@ module Puma
|
|
164
193
|
url = url + "?token=#{@control_auth_token}"
|
165
194
|
end
|
166
195
|
|
167
|
-
server
|
196
|
+
server.syswrite "GET #{url} HTTP/1.0\r\n\r\n"
|
168
197
|
|
169
198
|
unless data = server.read
|
170
|
-
raise
|
199
|
+
raise 'Server closed connection before responding'
|
171
200
|
end
|
172
201
|
|
173
202
|
response = data.split("\r\n")
|
@@ -176,67 +205,59 @@ module Puma
|
|
176
205
|
raise "Server sent empty response"
|
177
206
|
end
|
178
207
|
|
179
|
-
|
208
|
+
@http, @code, @message = response.first.split(' ',3)
|
180
209
|
|
181
|
-
if @code ==
|
182
|
-
raise
|
183
|
-
elsif @code ==
|
210
|
+
if @code == '403'
|
211
|
+
raise 'Unauthorized access to server (wrong auth token)'
|
212
|
+
elsif @code == '404'
|
184
213
|
raise "Command error: #{response.last}"
|
185
|
-
elsif @code !=
|
214
|
+
elsif @code != '200'
|
186
215
|
raise "Bad response from server: #{@code}"
|
187
216
|
end
|
188
217
|
|
189
218
|
message "Command #{@command} sent success"
|
190
|
-
message response.last if @command
|
219
|
+
message response.last if PRINTABLE_COMMANDS.include?(@command)
|
191
220
|
end
|
192
221
|
ensure
|
193
|
-
|
222
|
+
if server
|
223
|
+
if uri.scheme == 'ssl'
|
224
|
+
server.sysclose
|
225
|
+
else
|
226
|
+
server.close unless server.closed?
|
227
|
+
end
|
228
|
+
end
|
194
229
|
end
|
195
230
|
|
196
231
|
def send_signal
|
197
232
|
unless @pid
|
198
|
-
raise
|
233
|
+
raise 'Neither pid nor control url available'
|
199
234
|
end
|
200
235
|
|
201
236
|
begin
|
237
|
+
sig = CMD_PATH_SIG_MAP[@command]
|
202
238
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
when "halt"
|
208
|
-
Process.kill "QUIT", @pid
|
209
|
-
|
210
|
-
when "stop"
|
211
|
-
Process.kill "SIGTERM", @pid
|
212
|
-
|
213
|
-
when "stats"
|
214
|
-
puts "Stats not available via pid only"
|
215
|
-
return
|
216
|
-
|
217
|
-
when "reload-worker-directory"
|
218
|
-
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
|
219
242
|
return
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
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'
|
225
250
|
begin
|
226
251
|
Process.kill 0, @pid
|
227
|
-
puts
|
252
|
+
@stdout.puts 'Puma is started'
|
253
|
+
@stdout.flush unless @stdout.sync
|
228
254
|
rescue Errno::ESRCH
|
229
|
-
raise
|
255
|
+
raise 'Puma is not running'
|
230
256
|
end
|
231
|
-
|
232
|
-
return
|
233
|
-
|
234
|
-
else
|
235
257
|
return
|
236
258
|
end
|
237
|
-
|
238
259
|
rescue SystemCallError
|
239
|
-
if @command ==
|
260
|
+
if @command == 'restart'
|
240
261
|
start
|
241
262
|
else
|
242
263
|
raise "No pid '#{@pid}' found"
|
@@ -247,14 +268,13 @@ module Puma
|
|
247
268
|
end
|
248
269
|
|
249
270
|
def run
|
250
|
-
return start if @command ==
|
251
|
-
|
271
|
+
return start if @command == 'start'
|
252
272
|
prepare_configuration
|
253
273
|
|
254
|
-
if Puma.windows?
|
274
|
+
if Puma.windows? || @control_url && !NO_REQ_COMMANDS.include?(@command)
|
255
275
|
send_request
|
256
276
|
else
|
257
|
-
|
277
|
+
send_signal
|
258
278
|
end
|
259
279
|
|
260
280
|
rescue => e
|
@@ -262,9 +282,9 @@ module Puma
|
|
262
282
|
exit 1
|
263
283
|
end
|
264
284
|
|
265
|
-
|
285
|
+
private
|
266
286
|
def start
|
267
|
-
|
287
|
+
require_relative 'cli'
|
268
288
|
|
269
289
|
run_args = []
|
270
290
|
|
@@ -276,13 +296,13 @@ module Puma
|
|
276
296
|
run_args += ["-C", @config_file] if @config_file
|
277
297
|
run_args += ["-e", @environment] if @environment
|
278
298
|
|
279
|
-
|
299
|
+
log_writer = Puma::LogWriter.new(@stdout, @stderr)
|
280
300
|
|
281
301
|
# replace $0 because puma use it to generate restart command
|
282
302
|
puma_cmd = $0.gsub(/pumactl$/, 'puma')
|
283
303
|
$0 = puma_cmd if File.exist?(puma_cmd)
|
284
304
|
|
285
|
-
cli = Puma::CLI.new run_args,
|
305
|
+
cli = Puma::CLI.new run_args, log_writer
|
286
306
|
cli.run
|
287
307
|
end
|
288
308
|
end
|
data/lib/puma/detect.rb
CHANGED
@@ -1,15 +1,44 @@
|
|
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
|
+
# @version 5.2.0
|
21
|
+
IS_MRI = (RUBY_ENGINE == 'ruby' || RUBY_ENGINE.nil?)
|
5
22
|
|
6
23
|
def self.jruby?
|
7
24
|
IS_JRUBY
|
8
25
|
end
|
9
26
|
|
10
|
-
|
27
|
+
def self.osx?
|
28
|
+
IS_OSX
|
29
|
+
end
|
11
30
|
|
12
31
|
def self.windows?
|
13
32
|
IS_WINDOWS
|
14
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
|
15
44
|
end
|