puma 3.12.0 → 5.3.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 +1413 -439
- data/LICENSE +23 -20
- data/README.md +131 -60
- data/bin/puma-wild +3 -9
- data/docs/architecture.md +24 -19
- data/docs/compile_options.md +19 -0
- data/docs/deployment.md +38 -13
- data/docs/fork_worker.md +33 -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 +1 -1
- data/docs/plugins.md +20 -10
- data/docs/rails_dev_mode.md +29 -0
- data/docs/restart.md +47 -22
- data/docs/signals.md +7 -6
- data/docs/stats.md +142 -0
- data/docs/systemd.md +48 -70
- data/ext/puma_http11/PumaHttp11Service.java +2 -2
- data/ext/puma_http11/ext_help.h +1 -1
- data/ext/puma_http11/extconf.rb +27 -0
- data/ext/puma_http11/http11_parser.c +84 -109
- data/ext/puma_http11/http11_parser.h +1 -1
- data/ext/puma_http11/http11_parser.java.rl +22 -38
- data/ext/puma_http11/http11_parser.rl +4 -2
- data/ext/puma_http11/http11_parser_common.rl +3 -3
- data/ext/puma_http11/mini_ssl.c +262 -87
- 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 +89 -106
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +92 -22
- data/ext/puma_http11/puma_http11.c +34 -50
- data/lib/puma/app/status.rb +68 -49
- data/lib/puma/binder.rb +197 -144
- data/lib/puma/cli.rb +17 -15
- data/lib/puma/client.rb +257 -226
- data/lib/puma/cluster/worker.rb +176 -0
- data/lib/puma/cluster/worker_handle.rb +90 -0
- data/lib/puma/cluster.rb +223 -212
- data/lib/puma/commonlogger.rb +4 -2
- data/lib/puma/configuration.rb +58 -51
- data/lib/puma/const.rb +41 -19
- data/lib/puma/control_cli.rb +117 -73
- data/lib/puma/detect.rb +26 -3
- data/lib/puma/dsl.rb +531 -123
- data/lib/puma/error_logger.rb +104 -0
- data/lib/puma/events.rb +57 -31
- data/lib/puma/io_buffer.rb +9 -5
- data/lib/puma/jruby_restart.rb +2 -58
- data/lib/puma/json.rb +96 -0
- data/lib/puma/launcher.rb +182 -70
- data/lib/puma/minissl/context_builder.rb +79 -0
- data/lib/puma/minissl.rb +149 -48
- data/lib/puma/null_io.rb +15 -1
- data/lib/puma/plugin/tmp_restart.rb +2 -0
- data/lib/puma/plugin.rb +8 -12
- data/lib/puma/queue_close.rb +26 -0
- data/lib/puma/rack/builder.rb +4 -5
- data/lib/puma/rack/urlmap.rb +2 -0
- data/lib/puma/rack_default.rb +2 -0
- data/lib/puma/reactor.rb +87 -316
- data/lib/puma/request.rb +456 -0
- data/lib/puma/runner.rb +33 -52
- data/lib/puma/server.rb +288 -679
- data/lib/puma/single.rb +13 -67
- data/lib/puma/state_file.rb +10 -3
- data/lib/puma/systemd.rb +46 -0
- data/lib/puma/thread_pool.rb +131 -81
- data/lib/puma/util.rb +14 -6
- data/lib/puma.rb +54 -0
- data/lib/rack/handler/puma.rb +8 -6
- data/tools/Dockerfile +16 -0
- data/tools/trickletest.rb +0 -1
- metadata +45 -29
- 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/configuration.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'puma/rack/builder'
|
2
4
|
require 'puma/plugin'
|
3
5
|
require 'puma/const'
|
@@ -18,7 +20,7 @@ module Puma
|
|
18
20
|
# In this class any "user" specified options take precedence over any
|
19
21
|
# "file" specified options, take precedence over any "default" options.
|
20
22
|
#
|
21
|
-
# User input is
|
23
|
+
# User input is preferred over "defaults":
|
22
24
|
# user_options = { foo: "bar" }
|
23
25
|
# default_options = { foo: "zoo" }
|
24
26
|
# options = UserFileDefaultOptions.new(user_options, default_options)
|
@@ -30,7 +32,7 @@ module Puma
|
|
30
32
|
# puts options.all_of(:foo)
|
31
33
|
# # => ["bar", "zoo"]
|
32
34
|
#
|
33
|
-
# A "file" option can be set. This config will be
|
35
|
+
# A "file" option can be set. This config will be preferred over "default" options
|
34
36
|
# but will defer to any available "user" specified options.
|
35
37
|
#
|
36
38
|
# user_options = { foo: "bar" }
|
@@ -52,9 +54,7 @@ module Puma
|
|
52
54
|
attr_reader :user_options, :file_options, :default_options
|
53
55
|
|
54
56
|
def [](key)
|
55
|
-
|
56
|
-
return file_options[key] if file_options.key?(key)
|
57
|
-
return default_options[key] if default_options.key?(key)
|
57
|
+
fetch(key)
|
58
58
|
end
|
59
59
|
|
60
60
|
def []=(key, value)
|
@@ -62,7 +62,11 @@ module Puma
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def fetch(key, default_value = nil)
|
65
|
-
|
65
|
+
return user_options[key] if user_options.key?(key)
|
66
|
+
return file_options[key] if file_options.key?(key)
|
67
|
+
return default_options[key] if default_options.key?(key)
|
68
|
+
|
69
|
+
default_value
|
66
70
|
end
|
67
71
|
|
68
72
|
def all_of(key)
|
@@ -88,6 +92,12 @@ module Puma
|
|
88
92
|
end
|
89
93
|
end
|
90
94
|
end
|
95
|
+
|
96
|
+
def final_options
|
97
|
+
default_options
|
98
|
+
.merge(file_options)
|
99
|
+
.merge(user_options)
|
100
|
+
end
|
91
101
|
end
|
92
102
|
|
93
103
|
# The main configuration class of Puma.
|
@@ -104,16 +114,17 @@ module Puma
|
|
104
114
|
#
|
105
115
|
# It also handles loading plugins.
|
106
116
|
#
|
107
|
-
#
|
117
|
+
# [Note:]
|
118
|
+
# `:port` and `:host` are not valid keys. By the time they make it to the
|
108
119
|
# configuration options they are expected to be incorporated into a `:binds` key.
|
109
120
|
# Under the hood the DSL maps `port` and `host` calls to `:binds`
|
110
121
|
#
|
111
|
-
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
#
|
115
|
-
#
|
116
|
-
#
|
122
|
+
# config = Configuration.new({}) do |user_config, file_config, default_config|
|
123
|
+
# user_config.port 3003
|
124
|
+
# end
|
125
|
+
# config.load
|
126
|
+
# puts config.options[:port]
|
127
|
+
# # => 3003
|
117
128
|
#
|
118
129
|
# It is expected that `load` is called on the configuration instance after setting
|
119
130
|
# config. This method expands any values in `config_file` and puts them into the
|
@@ -135,6 +146,10 @@ module Puma
|
|
135
146
|
@file_dsl = DSL.new(@options.file_options, self)
|
136
147
|
@default_dsl = DSL.new(@options.default_options, self)
|
137
148
|
|
149
|
+
if !@options[:prune_bundler]
|
150
|
+
default_options[:preload_app] = (@options[:workers] > 1) && Puma.forkable?
|
151
|
+
end
|
152
|
+
|
138
153
|
if block
|
139
154
|
configure(&block)
|
140
155
|
end
|
@@ -165,26 +180,35 @@ module Puma
|
|
165
180
|
self
|
166
181
|
end
|
167
182
|
|
183
|
+
# @version 5.0.0
|
184
|
+
def default_max_threads
|
185
|
+
Puma.mri? ? 5 : 16
|
186
|
+
end
|
187
|
+
|
168
188
|
def puma_default_options
|
169
189
|
{
|
170
|
-
:min_threads => 0,
|
171
|
-
:max_threads =>
|
190
|
+
:min_threads => Integer(ENV['PUMA_MIN_THREADS'] || ENV['MIN_THREADS'] || 0),
|
191
|
+
:max_threads => Integer(ENV['PUMA_MAX_THREADS'] || ENV['MAX_THREADS'] || default_max_threads),
|
172
192
|
:log_requests => false,
|
173
193
|
:debug => false,
|
174
194
|
:binds => ["tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"],
|
175
|
-
:workers => 0,
|
176
|
-
:
|
195
|
+
:workers => Integer(ENV['WEB_CONCURRENCY'] || 0),
|
196
|
+
:silence_single_worker_warning => false,
|
177
197
|
:mode => :http,
|
178
198
|
:worker_timeout => DefaultWorkerTimeout,
|
179
199
|
:worker_boot_timeout => DefaultWorkerTimeout,
|
180
200
|
:worker_shutdown_timeout => DefaultWorkerShutdownTimeout,
|
181
201
|
:remote_address => :socket,
|
182
202
|
:tag => method(:infer_tag),
|
183
|
-
:environment => -> { ENV['RACK_ENV'] || "development" },
|
203
|
+
:environment => -> { ENV['RACK_ENV'] || ENV['RAILS_ENV'] || "development" },
|
184
204
|
:rackup => DefaultRackup,
|
185
205
|
:logger => STDOUT,
|
186
206
|
:persistent_timeout => Const::PERSISTENT_TIMEOUT,
|
187
|
-
:first_data_timeout => Const::FIRST_DATA_TIMEOUT
|
207
|
+
:first_data_timeout => Const::FIRST_DATA_TIMEOUT,
|
208
|
+
:raise_exception_on_sigterm => true,
|
209
|
+
:max_fast_inline => Const::MAX_FAST_INLINE,
|
210
|
+
:io_selector_backend => :auto,
|
211
|
+
:mutate_stdout_and_stderr_to_sync_on_write => true,
|
188
212
|
}
|
189
213
|
end
|
190
214
|
|
@@ -242,14 +266,6 @@ module Puma
|
|
242
266
|
def app
|
243
267
|
found = options[:app] || load_rackup
|
244
268
|
|
245
|
-
if @options[:mode] == :tcp
|
246
|
-
require 'puma/tcp_logger'
|
247
|
-
|
248
|
-
logger = @options[:logger]
|
249
|
-
quiet = !@options[:log_requests]
|
250
|
-
return TCPLogger.new(logger, found, quiet)
|
251
|
-
end
|
252
|
-
|
253
269
|
if @options[:log_requests]
|
254
270
|
require 'puma/commonlogger'
|
255
271
|
logger = @options[:logger]
|
@@ -272,8 +288,19 @@ module Puma
|
|
272
288
|
@plugins.create name
|
273
289
|
end
|
274
290
|
|
275
|
-
def run_hooks(key, arg)
|
276
|
-
@options.all_of(key).each
|
291
|
+
def run_hooks(key, arg, events)
|
292
|
+
@options.all_of(key).each do |b|
|
293
|
+
begin
|
294
|
+
b.call arg
|
295
|
+
rescue => e
|
296
|
+
events.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
|
297
|
+
events.debug e.backtrace.join("\n")
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
def final_options
|
303
|
+
@options.final_options
|
277
304
|
end
|
278
305
|
|
279
306
|
def self.temp_path
|
@@ -329,29 +356,9 @@ module Puma
|
|
329
356
|
end
|
330
357
|
|
331
358
|
def self.random_token
|
332
|
-
|
333
|
-
require 'openssl'
|
334
|
-
rescue LoadError
|
335
|
-
end
|
336
|
-
|
337
|
-
count = 16
|
338
|
-
|
339
|
-
bytes = nil
|
340
|
-
|
341
|
-
if defined? OpenSSL::Random
|
342
|
-
bytes = OpenSSL::Random.random_bytes(count)
|
343
|
-
elsif File.exist?("/dev/urandom")
|
344
|
-
File.open('/dev/urandom') { |f| bytes = f.read(count) }
|
345
|
-
end
|
346
|
-
|
347
|
-
if bytes
|
348
|
-
token = "".dup
|
349
|
-
bytes.each_byte { |b| token << b.to_s(16) }
|
350
|
-
else
|
351
|
-
token = (0..count).to_a.map { rand(255).to_s(16) }.join
|
352
|
-
end
|
359
|
+
require 'securerandom' unless defined?(SecureRandom)
|
353
360
|
|
354
|
-
|
361
|
+
SecureRandom.hex(16)
|
355
362
|
end
|
356
363
|
end
|
357
364
|
end
|
data/lib/puma/const.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
#encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
module Puma
|
3
5
|
class UnsupportedOption < RuntimeError
|
4
6
|
end
|
@@ -98,8 +100,9 @@ module Puma
|
|
98
100
|
# too taxing on performance.
|
99
101
|
module Const
|
100
102
|
|
101
|
-
PUMA_VERSION = VERSION = "3.
|
102
|
-
CODE_NAME = "
|
103
|
+
PUMA_VERSION = VERSION = "5.3.1".freeze
|
104
|
+
CODE_NAME = "Sweetnighter".freeze
|
105
|
+
|
103
106
|
PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
|
104
107
|
|
105
108
|
FAST_TRACK_KA_TIMEOUT = 0.2
|
@@ -116,31 +119,35 @@ module Puma
|
|
116
119
|
# sending data back
|
117
120
|
WRITE_TIMEOUT = 10
|
118
121
|
|
122
|
+
# How many requests to attempt inline before sending a client back to
|
123
|
+
# the reactor to be subject to normal ordering. The idea here is that
|
124
|
+
# we amortize the cost of going back to the reactor for a well behaved
|
125
|
+
# but very "greedy" client across 10 requests. This prevents a not
|
126
|
+
# well behaved client from monopolizing the thread forever.
|
127
|
+
MAX_FAST_INLINE = 10
|
128
|
+
|
119
129
|
# The original URI requested by the client.
|
120
130
|
REQUEST_URI= 'REQUEST_URI'.freeze
|
121
131
|
REQUEST_PATH = 'REQUEST_PATH'.freeze
|
122
132
|
QUERY_STRING = 'QUERY_STRING'.freeze
|
133
|
+
CONTENT_LENGTH = "CONTENT_LENGTH".freeze
|
123
134
|
|
124
135
|
PATH_INFO = 'PATH_INFO'.freeze
|
125
136
|
|
126
137
|
PUMA_TMP_BASE = "puma".freeze
|
127
138
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
ERROR_500_RESPONSE = "HTTP/1.1 500 Internal Server Error\r\n\r\n".freeze
|
141
|
-
|
142
|
-
# A common header for indicating the server is too busy. Not used yet.
|
143
|
-
ERROR_503_RESPONSE = "HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze
|
139
|
+
ERROR_RESPONSE = {
|
140
|
+
# Indicate that we couldn't parse the request
|
141
|
+
400 => "HTTP/1.1 400 Bad Request\r\n\r\n".freeze,
|
142
|
+
# The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
|
143
|
+
404 => "HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\nNOT FOUND".freeze,
|
144
|
+
# The standard empty 408 response for requests that timed out.
|
145
|
+
408 => "HTTP/1.1 408 Request Timeout\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\n".freeze,
|
146
|
+
# Indicate that there was an internal error, obviously.
|
147
|
+
500 => "HTTP/1.1 500 Internal Server Error\r\n\r\n".freeze,
|
148
|
+
# A common header for indicating the server is too busy. Not used yet.
|
149
|
+
503 => "HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze
|
150
|
+
}
|
144
151
|
|
145
152
|
# The basic max request size we'll try to read.
|
146
153
|
CHUNK_SIZE = 16 * 1024
|
@@ -158,6 +165,9 @@ module Puma
|
|
158
165
|
LINE_END = "\r\n".freeze
|
159
166
|
REMOTE_ADDR = "REMOTE_ADDR".freeze
|
160
167
|
HTTP_X_FORWARDED_FOR = "HTTP_X_FORWARDED_FOR".freeze
|
168
|
+
HTTP_X_FORWARDED_SSL = "HTTP_X_FORWARDED_SSL".freeze
|
169
|
+
HTTP_X_FORWARDED_SCHEME = "HTTP_X_FORWARDED_SCHEME".freeze
|
170
|
+
HTTP_X_FORWARDED_PROTO = "HTTP_X_FORWARDED_PROTO".freeze
|
161
171
|
|
162
172
|
SERVER_NAME = "SERVER_NAME".freeze
|
163
173
|
SERVER_PORT = "SERVER_PORT".freeze
|
@@ -166,7 +176,6 @@ module Puma
|
|
166
176
|
PORT_443 = "443".freeze
|
167
177
|
LOCALHOST = "localhost".freeze
|
168
178
|
LOCALHOST_IP = "127.0.0.1".freeze
|
169
|
-
LOCALHOST_ADDR = "127.0.0.1:0".freeze
|
170
179
|
|
171
180
|
SERVER_PROTOCOL = "SERVER_PROTOCOL".freeze
|
172
181
|
HTTP_11 = "HTTP/1.1".freeze
|
@@ -225,5 +234,18 @@ module Puma
|
|
225
234
|
HIJACK_IO = "rack.hijack_io".freeze
|
226
235
|
|
227
236
|
EARLY_HINTS = "rack.early_hints".freeze
|
237
|
+
|
238
|
+
# Minimum interval to checks worker health
|
239
|
+
WORKER_CHECK_INTERVAL = 5
|
240
|
+
|
241
|
+
# Illegal character in the key or value of response header
|
242
|
+
DQUOTE = "\"".freeze
|
243
|
+
HTTP_HEADER_DELIMITER = Regexp.escape("(),/:;<=>?@[]{}\\").freeze
|
244
|
+
ILLEGAL_HEADER_KEY_REGEX = /[\x00-\x20#{DQUOTE}#{HTTP_HEADER_DELIMITER}]/.freeze
|
245
|
+
# header values can contain HTAB?
|
246
|
+
ILLEGAL_HEADER_VALUE_REGEX = /[\x00-\x08\x0A-\x1F]/.freeze
|
247
|
+
|
248
|
+
# Banned keys of response header
|
249
|
+
BANNED_HEADER_KEY = /\A(rack\.|status\z)/.freeze
|
228
250
|
end
|
229
251
|
end
|
data/lib/puma/control_cli.rb
CHANGED
@@ -1,15 +1,42 @@
|
|
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
|
+
'phased-restart' => 'SIGUSR1',
|
22
|
+
'refork' => 'SIGURG',
|
23
|
+
'reload-worker-directory' => nil,
|
24
|
+
'restart' => 'SIGUSR2',
|
25
|
+
'start' => nil,
|
26
|
+
'stats' => nil,
|
27
|
+
'status' => '',
|
28
|
+
'stop' => 'SIGTERM',
|
29
|
+
'thread-backtraces' => nil
|
30
|
+
}.freeze
|
31
|
+
|
32
|
+
# @deprecated 6.0.0
|
33
|
+
COMMANDS = CMD_PATH_SIG_MAP.keys.freeze
|
34
|
+
|
35
|
+
# commands that cannot be used in a request
|
36
|
+
NO_REQ_COMMANDS = %w{refork}.freeze
|
37
|
+
|
38
|
+
# @version 5.0.0
|
39
|
+
PRINTABLE_COMMANDS = %w{gc-stats stats thread-backtraces}.freeze
|
13
40
|
|
14
41
|
def initialize(argv, stdout=STDOUT, stderr=STDERR)
|
15
42
|
@state = nil
|
@@ -20,6 +47,7 @@ module Puma
|
|
20
47
|
@control_auth_token = nil
|
21
48
|
@config_file = nil
|
22
49
|
@command = nil
|
50
|
+
@environment = ENV['RACK_ENV'] || ENV['RAILS_ENV']
|
23
51
|
|
24
52
|
@argv = argv.dup
|
25
53
|
@stdout = stdout
|
@@ -27,7 +55,7 @@ module Puma
|
|
27
55
|
@cli_options = {}
|
28
56
|
|
29
57
|
opts = OptionParser.new do |o|
|
30
|
-
o.banner = "Usage: pumactl (-p PID | -P pidfile | -S status_file | -C url -T token | -F config.rb) (#{
|
58
|
+
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
59
|
|
32
60
|
o.on "-S", "--state PATH", "Where the state file to use is" do |arg|
|
33
61
|
@state = arg
|
@@ -57,13 +85,18 @@ module Puma
|
|
57
85
|
@config_file = arg
|
58
86
|
end
|
59
87
|
|
88
|
+
o.on "-e", "--environment ENVIRONMENT",
|
89
|
+
"The environment to run the Rack app on (default development)" do |arg|
|
90
|
+
@environment = arg
|
91
|
+
end
|
92
|
+
|
60
93
|
o.on_tail("-H", "--help", "Show this message") do
|
61
94
|
@stdout.puts o
|
62
95
|
exit
|
63
96
|
end
|
64
97
|
|
65
98
|
o.on_tail("-V", "--version", "Show version") do
|
66
|
-
puts Const::PUMA_VERSION
|
99
|
+
@stdout.puts Const::PUMA_VERSION
|
67
100
|
exit
|
68
101
|
end
|
69
102
|
end
|
@@ -73,9 +106,22 @@ module Puma
|
|
73
106
|
|
74
107
|
@command = argv.shift
|
75
108
|
|
109
|
+
# check presence of command
|
110
|
+
unless @command
|
111
|
+
raise "Available commands: #{CMD_PATH_SIG_MAP.keys.join(", ")}"
|
112
|
+
end
|
113
|
+
|
114
|
+
unless CMD_PATH_SIG_MAP.key? @command
|
115
|
+
raise "Invalid command: #{@command}"
|
116
|
+
end
|
117
|
+
|
76
118
|
unless @config_file == '-'
|
77
|
-
|
78
|
-
|
119
|
+
environment = @environment || 'development'
|
120
|
+
|
121
|
+
if @config_file.nil?
|
122
|
+
@config_file = %W(config/puma/#{environment}.rb config/puma.rb).find do |f|
|
123
|
+
File.exist?(f)
|
124
|
+
end
|
79
125
|
end
|
80
126
|
|
81
127
|
if @config_file
|
@@ -87,19 +133,8 @@ module Puma
|
|
87
133
|
@pidfile ||= config.options[:pidfile]
|
88
134
|
end
|
89
135
|
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
136
|
rescue => e
|
101
137
|
@stdout.puts e.message
|
102
|
-
@stdout.puts e.backtrace
|
103
138
|
exit 1
|
104
139
|
end
|
105
140
|
|
@@ -121,7 +156,7 @@ module Puma
|
|
121
156
|
@pid = sf.pid
|
122
157
|
elsif @pidfile
|
123
158
|
# get pid from pid_file
|
124
|
-
@pid = File.
|
159
|
+
@pid = File.read(@pidfile, mode: 'rb:UTF-8').to_i
|
125
160
|
end
|
126
161
|
end
|
127
162
|
|
@@ -129,17 +164,29 @@ module Puma
|
|
129
164
|
uri = URI.parse @control_url
|
130
165
|
|
131
166
|
# create server object by scheme
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
167
|
+
server =
|
168
|
+
case uri.scheme
|
169
|
+
when 'ssl'
|
170
|
+
require 'openssl'
|
171
|
+
OpenSSL::SSL::SSLSocket.new(
|
172
|
+
TCPSocket.new(uri.host, uri.port),
|
173
|
+
OpenSSL::SSL::SSLContext.new)
|
174
|
+
.tap { |ssl| ssl.sync_close = true } # default is false
|
175
|
+
.tap(&:connect)
|
176
|
+
when 'tcp'
|
177
|
+
TCPSocket.new uri.host, uri.port
|
178
|
+
when 'unix'
|
179
|
+
# check for abstract UNIXSocket
|
180
|
+
UNIXSocket.new(@control_url.start_with?('unix://@') ?
|
181
|
+
"\0#{uri.host}#{uri.path}" : "#{uri.host}#{uri.path}")
|
182
|
+
else
|
183
|
+
raise "Invalid scheme: #{uri.scheme}"
|
184
|
+
end
|
185
|
+
|
186
|
+
if @command == 'status'
|
187
|
+
message 'Puma is started'
|
188
|
+
elsif NO_REQ_COMMANDS.include? @command
|
189
|
+
raise "Invalid request command: #{@command}"
|
143
190
|
else
|
144
191
|
url = "/#{@command}"
|
145
192
|
|
@@ -147,10 +194,10 @@ module Puma
|
|
147
194
|
url = url + "?token=#{@control_auth_token}"
|
148
195
|
end
|
149
196
|
|
150
|
-
|
197
|
+
server.syswrite "GET #{url} HTTP/1.0\r\n\r\n"
|
151
198
|
|
152
|
-
unless data =
|
153
|
-
raise
|
199
|
+
unless data = server.read
|
200
|
+
raise 'Server closed connection before responding'
|
154
201
|
end
|
155
202
|
|
156
203
|
response = data.split("\r\n")
|
@@ -159,57 +206,55 @@ module Puma
|
|
159
206
|
raise "Server sent empty response"
|
160
207
|
end
|
161
208
|
|
162
|
-
|
209
|
+
@http, @code, @message = response.first.split(' ',3)
|
163
210
|
|
164
|
-
if @code ==
|
165
|
-
raise
|
166
|
-
elsif @code ==
|
211
|
+
if @code == '403'
|
212
|
+
raise 'Unauthorized access to server (wrong auth token)'
|
213
|
+
elsif @code == '404'
|
167
214
|
raise "Command error: #{response.last}"
|
168
|
-
elsif @code !=
|
215
|
+
elsif @code != '200'
|
169
216
|
raise "Bad response from server: #{@code}"
|
170
217
|
end
|
171
218
|
|
172
219
|
message "Command #{@command} sent success"
|
173
|
-
message response.last if @command
|
220
|
+
message response.last if PRINTABLE_COMMANDS.include?(@command)
|
221
|
+
end
|
222
|
+
ensure
|
223
|
+
if server
|
224
|
+
if uri.scheme == 'ssl'
|
225
|
+
server.sysclose
|
226
|
+
else
|
227
|
+
server.close unless server.closed?
|
228
|
+
end
|
174
229
|
end
|
175
|
-
|
176
|
-
@server.close
|
177
230
|
end
|
178
231
|
|
179
232
|
def send_signal
|
180
233
|
unless @pid
|
181
|
-
raise
|
234
|
+
raise 'Neither pid nor control url available'
|
182
235
|
end
|
183
236
|
|
184
237
|
begin
|
238
|
+
sig = CMD_PATH_SIG_MAP[@command]
|
185
239
|
|
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"
|
240
|
+
if sig.nil?
|
241
|
+
@stdout.puts "'#{@command}' not available via pid only"
|
242
|
+
@stdout.flush unless @stdout.sync
|
202
243
|
return
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
244
|
+
elsif sig.start_with? 'SIG'
|
245
|
+
Process.kill sig, @pid
|
246
|
+
elsif @command == 'status'
|
247
|
+
begin
|
248
|
+
Process.kill 0, @pid
|
249
|
+
@stdout.puts 'Puma is started'
|
250
|
+
@stdout.flush unless @stdout.sync
|
251
|
+
rescue Errno::ESRCH
|
252
|
+
raise 'Puma is not running'
|
253
|
+
end
|
208
254
|
return
|
209
255
|
end
|
210
|
-
|
211
256
|
rescue SystemCallError
|
212
|
-
if @command ==
|
257
|
+
if @command == 'restart'
|
213
258
|
start
|
214
259
|
else
|
215
260
|
raise "No pid '#{@pid}' found"
|
@@ -220,23 +265,21 @@ module Puma
|
|
220
265
|
end
|
221
266
|
|
222
267
|
def run
|
223
|
-
return start if @command ==
|
224
|
-
|
268
|
+
return start if @command == 'start'
|
225
269
|
prepare_configuration
|
226
270
|
|
227
|
-
if Puma.windows?
|
271
|
+
if Puma.windows? || @control_url
|
228
272
|
send_request
|
229
273
|
else
|
230
|
-
|
274
|
+
send_signal
|
231
275
|
end
|
232
276
|
|
233
277
|
rescue => e
|
234
278
|
message e.message
|
235
|
-
message e.backtrace
|
236
279
|
exit 1
|
237
280
|
end
|
238
281
|
|
239
|
-
|
282
|
+
private
|
240
283
|
def start
|
241
284
|
require 'puma/cli'
|
242
285
|
|
@@ -248,6 +291,7 @@ module Puma
|
|
248
291
|
run_args += ["--control-url", @control_url] if @control_url
|
249
292
|
run_args += ["--control-token", @control_auth_token] if @control_auth_token
|
250
293
|
run_args += ["-C", @config_file] if @config_file
|
294
|
+
run_args += ["-e", @environment] if @environment
|
251
295
|
|
252
296
|
events = Puma::Events.new @stdout, @stderr
|
253
297
|
|
data/lib/puma/detect.rb
CHANGED
@@ -1,13 +1,36 @@
|
|
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
|
+
IS_JRUBY = Object.const_defined? :JRUBY_VERSION
|
12
|
+
|
13
|
+
IS_WINDOWS = !!(RUBY_PLATFORM =~ /mswin|ming|cygwin/ ||
|
14
|
+
IS_JRUBY && RUBY_DESCRIPTION =~ /mswin/)
|
15
|
+
|
16
|
+
# @version 5.2.0
|
17
|
+
IS_MRI = (RUBY_ENGINE == 'ruby' || RUBY_ENGINE.nil?)
|
3
18
|
|
4
19
|
def self.jruby?
|
5
20
|
IS_JRUBY
|
6
21
|
end
|
7
22
|
|
8
|
-
IS_WINDOWS = RUBY_PLATFORM =~ /mswin|ming|cygwin/
|
9
|
-
|
10
23
|
def self.windows?
|
11
24
|
IS_WINDOWS
|
12
25
|
end
|
26
|
+
|
27
|
+
# @version 5.0.0
|
28
|
+
def self.mri?
|
29
|
+
IS_MRI
|
30
|
+
end
|
31
|
+
|
32
|
+
# @version 5.0.0
|
33
|
+
def self.forkable?
|
34
|
+
HAS_FORK
|
35
|
+
end
|
13
36
|
end
|