puma 2.0.0.b5 → 5.0.0.beta1
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 +7 -0
- data/History.md +1598 -0
- data/LICENSE +23 -20
- data/README.md +222 -62
- data/bin/puma-wild +31 -0
- data/bin/pumactl +1 -1
- data/docs/architecture.md +37 -0
- data/docs/deployment.md +113 -0
- 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 +13 -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/jungle/upstart/README.md +61 -0
- data/docs/jungle/upstart/puma-manager.conf +31 -0
- data/docs/jungle/upstart/puma.conf +69 -0
- data/docs/nginx.md +5 -10
- data/docs/plugins.md +38 -0
- data/docs/restart.md +41 -0
- data/docs/signals.md +97 -0
- data/docs/systemd.md +228 -0
- data/ext/puma_http11/PumaHttp11Service.java +2 -2
- data/ext/puma_http11/extconf.rb +23 -2
- data/ext/puma_http11/http11_parser.c +301 -482
- data/ext/puma_http11/http11_parser.h +13 -11
- data/ext/puma_http11/http11_parser.java.rl +26 -42
- data/ext/puma_http11/http11_parser.rl +22 -21
- data/ext/puma_http11/http11_parser_common.rl +5 -5
- data/ext/puma_http11/mini_ssl.c +377 -18
- data/ext/puma_http11/org/jruby/puma/Http11.java +108 -107
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +137 -170
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +265 -191
- data/ext/puma_http11/puma_http11.c +57 -81
- data/lib/puma.rb +25 -4
- data/lib/puma/accept_nonblock.rb +7 -1
- data/lib/puma/app/status.rb +61 -24
- data/lib/puma/binder.rb +212 -78
- data/lib/puma/cli.rb +149 -644
- data/lib/puma/client.rb +316 -65
- data/lib/puma/cluster.rb +659 -0
- data/lib/puma/commonlogger.rb +108 -0
- data/lib/puma/configuration.rb +279 -180
- data/lib/puma/const.rb +126 -39
- data/lib/puma/control_cli.rb +183 -96
- data/lib/puma/detect.rb +20 -1
- data/lib/puma/dsl.rb +776 -0
- data/lib/puma/events.rb +91 -23
- data/lib/puma/io_buffer.rb +9 -5
- data/lib/puma/jruby_restart.rb +9 -5
- data/lib/puma/launcher.rb +487 -0
- data/lib/puma/minissl.rb +239 -93
- data/lib/puma/minissl/context_builder.rb +76 -0
- data/lib/puma/null_io.rb +22 -12
- data/lib/puma/plugin.rb +111 -0
- data/lib/puma/plugin/tmp_restart.rb +36 -0
- data/lib/puma/rack/builder.rb +297 -0
- data/lib/puma/rack/urlmap.rb +93 -0
- data/lib/puma/rack_default.rb +9 -0
- data/lib/puma/reactor.rb +290 -43
- data/lib/puma/runner.rb +163 -0
- data/lib/puma/server.rb +493 -126
- data/lib/puma/single.rb +66 -0
- data/lib/puma/state_file.rb +34 -0
- data/lib/puma/thread_pool.rb +228 -47
- data/lib/puma/util.rb +115 -0
- data/lib/rack/handler/puma.rb +78 -31
- data/tools/Dockerfile +16 -0
- data/tools/trickletest.rb +44 -0
- metadata +60 -155
- data/COPYING +0 -55
- data/Gemfile +0 -8
- data/History.txt +0 -196
- data/Manifest.txt +0 -56
- data/Rakefile +0 -121
- data/TODO +0 -5
- data/docs/config.md +0 -0
- data/ext/puma_http11/io_buffer.c +0 -154
- data/lib/puma/capistrano.rb +0 -26
- data/lib/puma/compat.rb +0 -11
- data/lib/puma/daemon_ext.rb +0 -20
- data/lib/puma/delegation.rb +0 -11
- data/lib/puma/java_io_buffer.rb +0 -45
- data/lib/puma/rack_patch.rb +0 -25
- data/puma.gemspec +0 -45
- data/test/test_app_status.rb +0 -88
- data/test/test_cli.rb +0 -171
- data/test/test_config.rb +0 -16
- data/test/test_http10.rb +0 -27
- data/test/test_http11.rb +0 -126
- data/test/test_integration.rb +0 -150
- data/test/test_iobuffer.rb +0 -38
- data/test/test_minissl.rb +0 -22
- data/test/test_null_io.rb +0 -31
- data/test/test_persistent.rb +0 -238
- data/test/test_puma_server.rb +0 -128
- data/test/test_rack_handler.rb +0 -10
- data/test/test_rack_server.rb +0 -141
- data/test/test_thread_pool.rb +0 -146
- data/test/test_unix_socket.rb +0 -39
- data/test/test_ws.rb +0 -89
- data/tools/jungle/README.md +0 -54
- data/tools/jungle/puma +0 -332
- data/tools/jungle/run-puma +0 -3
data/lib/puma/binder.rb
CHANGED
@@ -1,31 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
require 'socket'
|
5
|
+
|
1
6
|
require 'puma/const'
|
7
|
+
require 'puma/util'
|
8
|
+
require 'puma/minissl/context_builder'
|
2
9
|
|
3
10
|
module Puma
|
4
11
|
class Binder
|
5
12
|
include Puma::Const
|
6
13
|
|
14
|
+
RACK_VERSION = [1,6].freeze
|
15
|
+
|
7
16
|
def initialize(events)
|
8
17
|
@events = events
|
9
18
|
@listeners = []
|
10
19
|
@inherited_fds = {}
|
20
|
+
@activated_sockets = {}
|
11
21
|
@unix_paths = []
|
12
22
|
|
13
23
|
@proto_env = {
|
14
|
-
"rack.version".freeze =>
|
24
|
+
"rack.version".freeze => RACK_VERSION,
|
15
25
|
"rack.errors".freeze => events.stderr,
|
16
26
|
"rack.multithread".freeze => true,
|
17
27
|
"rack.multiprocess".freeze => false,
|
18
|
-
"rack.run_once".freeze =>
|
28
|
+
"rack.run_once".freeze => false,
|
19
29
|
"SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
|
20
30
|
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
|
31
|
+
# I'd like to set a default CONTENT_TYPE here but some things
|
32
|
+
# depend on their not being a default set and inferring
|
33
|
+
# it from the content. And so if i set it here, it won't
|
34
|
+
# infer properly.
|
25
35
|
|
26
36
|
"QUERY_STRING".freeze => "",
|
27
37
|
SERVER_PROTOCOL => HTTP_11,
|
28
|
-
SERVER_SOFTWARE =>
|
38
|
+
SERVER_SOFTWARE => PUMA_SERVER_STRING,
|
29
39
|
GATEWAY_INTERFACE => CGI_VER
|
30
40
|
}
|
31
41
|
|
@@ -33,7 +43,8 @@ module Puma
|
|
33
43
|
@ios = []
|
34
44
|
end
|
35
45
|
|
36
|
-
attr_reader :listeners, :
|
46
|
+
attr_reader :ios, :listeners, :unix_paths, :proto_env, :envs, :activated_sockets, :inherited_fds
|
47
|
+
attr_writer :ios, :listeners
|
37
48
|
|
38
49
|
def env(sock)
|
39
50
|
@envs.fetch(sock, @proto_env)
|
@@ -41,95 +52,123 @@ module Puma
|
|
41
52
|
|
42
53
|
def close
|
43
54
|
@ios.each { |i| i.close }
|
44
|
-
@unix_paths.each { |i| File.unlink i }
|
45
55
|
end
|
46
56
|
|
47
|
-
def
|
48
|
-
|
57
|
+
def connected_ports
|
58
|
+
ios.map { |io| io.addr[1] }.uniq
|
59
|
+
end
|
49
60
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end
|
61
|
+
def create_inherited_fds(env_hash)
|
62
|
+
env_hash.select {|k,v| k =~ /PUMA_INHERIT_\d+/}.each do |_k, v|
|
63
|
+
fd, url = v.split(":", 2)
|
64
|
+
@inherited_fds[url] = fd.to_i
|
65
|
+
end.keys # pass keys back for removal
|
66
|
+
end
|
57
67
|
|
58
|
-
|
59
|
-
|
68
|
+
# systemd socket activation.
|
69
|
+
# LISTEN_FDS = number of listening sockets. e.g. 2 means accept on 2 sockets w/descriptors 3 and 4.
|
70
|
+
# LISTEN_PID = PID of the service process, aka us
|
71
|
+
# see https://www.freedesktop.org/software/systemd/man/systemd-socket-activate.html
|
72
|
+
def create_activated_fds(env_hash)
|
73
|
+
return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
|
74
|
+
env_hash['LISTEN_FDS'].to_i.times do |index|
|
75
|
+
sock = TCPServer.for_fd(socket_activation_fd(index))
|
76
|
+
key = begin # Try to parse as a path
|
77
|
+
[:unix, Socket.unpack_sockaddr_un(sock.getsockname)]
|
78
|
+
rescue ArgumentError # Try to parse as a port/ip
|
79
|
+
port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
|
80
|
+
addr = "[#{addr}]" if addr =~ /\:/
|
81
|
+
[:tcp, addr, port]
|
82
|
+
end
|
83
|
+
@activated_sockets[key] = sock
|
84
|
+
@events.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
|
60
85
|
end
|
86
|
+
["LISTEN_FDS", "LISTEN_PID"] # Signal to remove these keys from ENV
|
61
87
|
end
|
62
88
|
|
63
|
-
def parse(binds, logger)
|
89
|
+
def parse(binds, logger, log_msg = 'Listening')
|
64
90
|
binds.each do |str|
|
65
91
|
uri = URI.parse str
|
66
92
|
case uri.scheme
|
67
93
|
when "tcp"
|
68
94
|
if fd = @inherited_fds.delete(str)
|
69
|
-
logger.log "* Inherited #{str}"
|
70
95
|
io = inherit_tcp_listener uri.host, uri.port, fd
|
96
|
+
logger.log "* Inherited #{str}"
|
97
|
+
elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
|
98
|
+
io = inherit_tcp_listener uri.host, uri.port, sock
|
99
|
+
logger.log "* Activated #{str}"
|
71
100
|
else
|
72
|
-
|
73
|
-
|
101
|
+
params = Util.parse_query uri.query
|
102
|
+
|
103
|
+
opt = params.key?('low_latency')
|
104
|
+
bak = params.fetch('backlog', 1024).to_i
|
105
|
+
|
106
|
+
io = add_tcp_listener uri.host, uri.port, opt, bak
|
107
|
+
|
108
|
+
@ios.each do |i|
|
109
|
+
next unless TCPServer === i
|
110
|
+
addr = if i.local_address.ipv6?
|
111
|
+
"[#{i.local_address.ip_unpack[0]}]:#{i.local_address.ip_unpack[1]}"
|
112
|
+
else
|
113
|
+
i.local_address.ip_unpack.join(':')
|
114
|
+
end
|
115
|
+
|
116
|
+
logger.log "* #{log_msg} on tcp://#{addr}"
|
117
|
+
end
|
74
118
|
end
|
75
119
|
|
76
|
-
@listeners << [str, io]
|
120
|
+
@listeners << [str, io] if io
|
77
121
|
when "unix"
|
78
|
-
path = "#{uri.host}#{uri.path}"
|
122
|
+
path = "#{uri.host}#{uri.path}".gsub("%20", " ")
|
79
123
|
|
80
124
|
if fd = @inherited_fds.delete(str)
|
81
|
-
logger.log "* Inherited #{str}"
|
82
125
|
io = inherit_unix_listener path, fd
|
126
|
+
logger.log "* Inherited #{str}"
|
127
|
+
elsif sock = @activated_sockets.delete([ :unix, path ])
|
128
|
+
io = inherit_unix_listener path, sock
|
129
|
+
logger.log "* Activated #{str}"
|
83
130
|
else
|
84
|
-
logger.log "* Listening on #{str}"
|
85
|
-
|
86
131
|
umask = nil
|
132
|
+
mode = nil
|
133
|
+
backlog = 1024
|
87
134
|
|
88
135
|
if uri.query
|
89
|
-
params =
|
136
|
+
params = Util.parse_query uri.query
|
90
137
|
if u = params['umask']
|
91
138
|
# Use Integer() to respect the 0 prefix as octal
|
92
139
|
umask = Integer(u)
|
93
140
|
end
|
141
|
+
|
142
|
+
if u = params['mode']
|
143
|
+
mode = Integer('0'+u)
|
144
|
+
end
|
145
|
+
|
146
|
+
if u = params['backlog']
|
147
|
+
backlog = Integer(u)
|
148
|
+
end
|
94
149
|
end
|
95
150
|
|
96
|
-
io = add_unix_listener path, umask
|
151
|
+
io = add_unix_listener path, umask, mode, backlog
|
152
|
+
logger.log "* #{log_msg} on #{str}"
|
97
153
|
end
|
98
154
|
|
99
155
|
@listeners << [str, io]
|
100
156
|
when "ssl"
|
101
|
-
|
102
|
-
|
103
|
-
raise UnsupportedOption
|
104
|
-
end
|
105
|
-
|
106
|
-
params = Rack::Utils.parse_query uri.query
|
107
|
-
require 'puma/minissl'
|
108
|
-
|
109
|
-
ctx = MiniSSL::Context.new
|
110
|
-
unless params['key']
|
111
|
-
@events.error "Please specify the SSL key via 'key='"
|
112
|
-
end
|
113
|
-
|
114
|
-
ctx.key = params['key']
|
115
|
-
|
116
|
-
unless params['cert']
|
117
|
-
@events.error "Please specify the SSL cert via 'cert='"
|
118
|
-
end
|
119
|
-
|
120
|
-
ctx.cert = params['cert']
|
121
|
-
|
122
|
-
ctx.verify_mode = MiniSSL::VERIFY_NONE
|
157
|
+
params = Util.parse_query uri.query
|
158
|
+
ctx = MiniSSL::ContextBuilder.new(params, @events).context
|
123
159
|
|
124
160
|
if fd = @inherited_fds.delete(str)
|
125
161
|
logger.log "* Inherited #{str}"
|
126
|
-
io =
|
162
|
+
io = inherit_ssl_listener fd, ctx
|
163
|
+
elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
|
164
|
+
io = inherit_ssl_listener sock, ctx
|
165
|
+
logger.log "* Activated #{str}"
|
127
166
|
else
|
128
|
-
logger.log "* Listening on #{str}"
|
129
167
|
io = add_ssl_listener uri.host, uri.port, ctx
|
168
|
+
logger.log "* Listening on #{str}"
|
130
169
|
end
|
131
170
|
|
132
|
-
@listeners << [str, io]
|
171
|
+
@listeners << [str, io] if io
|
133
172
|
else
|
134
173
|
logger.error "Invalid URI: #{str}"
|
135
174
|
end
|
@@ -153,6 +192,16 @@ module Puma
|
|
153
192
|
end
|
154
193
|
end
|
155
194
|
|
195
|
+
# Also close any unused activated sockets
|
196
|
+
@activated_sockets.each do |key, sock|
|
197
|
+
logger.log "* Closing unused activated socket: #{key.join ':'}"
|
198
|
+
begin
|
199
|
+
sock.close
|
200
|
+
rescue SystemCallError
|
201
|
+
end
|
202
|
+
# We have to unlink a unix socket path that's not being used
|
203
|
+
File.unlink key[1] if key[0] == :unix
|
204
|
+
end
|
156
205
|
end
|
157
206
|
|
158
207
|
# Tell the server to listen on host +host+, port +port+.
|
@@ -163,14 +212,23 @@ module Puma
|
|
163
212
|
# allow to accumulate before returning connection refused.
|
164
213
|
#
|
165
214
|
def add_tcp_listener(host, port, optimize_for_latency=true, backlog=1024)
|
166
|
-
|
215
|
+
if host == "localhost"
|
216
|
+
loopback_addresses.each do |addr|
|
217
|
+
add_tcp_listener addr, port, optimize_for_latency, backlog
|
218
|
+
end
|
219
|
+
return
|
220
|
+
end
|
221
|
+
|
222
|
+
host = host[1..-2] if host and host[0..0] == '['
|
223
|
+
tcp_server = TCPServer.new(host, port)
|
167
224
|
if optimize_for_latency
|
168
|
-
|
225
|
+
tcp_server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
169
226
|
end
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
227
|
+
tcp_server.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
228
|
+
tcp_server.listen backlog
|
229
|
+
|
230
|
+
@ios << tcp_server
|
231
|
+
tcp_server
|
174
232
|
end
|
175
233
|
|
176
234
|
def inherit_tcp_listener(host, port, fd)
|
@@ -186,13 +244,18 @@ module Puma
|
|
186
244
|
|
187
245
|
def add_ssl_listener(host, port, ctx,
|
188
246
|
optimize_for_latency=true, backlog=1024)
|
189
|
-
if IS_JRUBY
|
190
|
-
@events.error "SSL not supported on JRuby"
|
191
|
-
raise UnsupportedOption
|
192
|
-
end
|
193
|
-
|
194
247
|
require 'puma/minissl'
|
195
248
|
|
249
|
+
MiniSSL.check
|
250
|
+
|
251
|
+
if host == "localhost"
|
252
|
+
loopback_addresses.each do |addr|
|
253
|
+
add_ssl_listener addr, port, ctx, optimize_for_latency, backlog
|
254
|
+
end
|
255
|
+
return
|
256
|
+
end
|
257
|
+
|
258
|
+
host = host[1..-2] if host[0..0] == '['
|
196
259
|
s = TCPServer.new(host, port)
|
197
260
|
if optimize_for_latency
|
198
261
|
s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
@@ -200,6 +263,7 @@ module Puma
|
|
200
263
|
s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
201
264
|
s.listen backlog
|
202
265
|
|
266
|
+
|
203
267
|
ssl = MiniSSL::Server.new s, ctx
|
204
268
|
env = @proto_env.dup
|
205
269
|
env[HTTPS_KEY] = HTTPS
|
@@ -209,45 +273,115 @@ module Puma
|
|
209
273
|
s
|
210
274
|
end
|
211
275
|
|
212
|
-
def
|
213
|
-
|
214
|
-
|
215
|
-
|
276
|
+
def inherit_ssl_listener(fd, ctx)
|
277
|
+
require 'puma/minissl'
|
278
|
+
MiniSSL.check
|
279
|
+
|
280
|
+
if fd.kind_of? TCPServer
|
281
|
+
s = fd
|
282
|
+
else
|
283
|
+
s = TCPServer.for_fd(fd)
|
216
284
|
end
|
285
|
+
ssl = MiniSSL::Server.new(s, ctx)
|
286
|
+
|
287
|
+
env = @proto_env.dup
|
288
|
+
env[HTTPS_KEY] = HTTPS
|
289
|
+
@envs[ssl] = env
|
290
|
+
|
291
|
+
@ios << ssl
|
217
292
|
|
218
|
-
require 'puma/minissl'
|
219
|
-
s = TCPServer.for_fd(fd)
|
220
|
-
@ios << MiniSSL::Server.new(s, ctx)
|
221
293
|
s
|
222
294
|
end
|
223
295
|
|
224
296
|
# Tell the server to listen on +path+ as a UNIX domain socket.
|
225
297
|
#
|
226
|
-
def add_unix_listener(path, umask=nil)
|
227
|
-
@unix_paths << path
|
298
|
+
def add_unix_listener(path, umask=nil, mode=nil, backlog=1024)
|
299
|
+
@unix_paths << path unless File.exist? path
|
228
300
|
|
229
301
|
# Let anyone connect by default
|
230
302
|
umask ||= 0
|
231
303
|
|
232
304
|
begin
|
233
305
|
old_mask = File.umask(umask)
|
306
|
+
|
307
|
+
if File.exist? path
|
308
|
+
begin
|
309
|
+
old = UNIXSocket.new path
|
310
|
+
rescue SystemCallError, IOError
|
311
|
+
File.unlink path
|
312
|
+
else
|
313
|
+
old.close
|
314
|
+
raise "There is already a server bound to: #{path}"
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
234
318
|
s = UNIXServer.new(path)
|
319
|
+
s.listen backlog
|
235
320
|
@ios << s
|
236
321
|
ensure
|
237
322
|
File.umask old_mask
|
238
323
|
end
|
239
324
|
|
325
|
+
if mode
|
326
|
+
File.chmod mode, path
|
327
|
+
end
|
328
|
+
|
329
|
+
env = @proto_env.dup
|
330
|
+
env[REMOTE_ADDR] = "127.0.0.1"
|
331
|
+
@envs[s] = env
|
332
|
+
|
240
333
|
s
|
241
334
|
end
|
242
335
|
|
243
336
|
def inherit_unix_listener(path, fd)
|
244
|
-
@unix_paths << path
|
337
|
+
@unix_paths << path unless File.exist? path
|
245
338
|
|
246
|
-
|
339
|
+
if fd.kind_of? TCPServer
|
340
|
+
s = fd
|
341
|
+
else
|
342
|
+
s = UNIXServer.for_fd fd
|
343
|
+
end
|
247
344
|
@ios << s
|
248
345
|
|
346
|
+
env = @proto_env.dup
|
347
|
+
env[REMOTE_ADDR] = "127.0.0.1"
|
348
|
+
@envs[s] = env
|
349
|
+
|
249
350
|
s
|
250
351
|
end
|
251
352
|
|
353
|
+
def close_listeners
|
354
|
+
listeners.each do |l, io|
|
355
|
+
io.close unless io.closed? # Ruby 2.2 issue
|
356
|
+
uri = URI.parse(l)
|
357
|
+
next unless uri.scheme == 'unix'
|
358
|
+
unix_path = "#{uri.host}#{uri.path}"
|
359
|
+
File.unlink unix_path if unix_paths.include? unix_path
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
def redirects_for_restart
|
364
|
+
redirects = listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h
|
365
|
+
redirects[:close_others] = true
|
366
|
+
redirects
|
367
|
+
end
|
368
|
+
|
369
|
+
def redirects_for_restart_env
|
370
|
+
listeners.each_with_object({}).with_index do |(listen, memo), i|
|
371
|
+
memo["PUMA_INHERIT_#{i}"] = "#{listen[1].to_i}:#{listen[0]}"
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
private
|
376
|
+
|
377
|
+
def loopback_addresses
|
378
|
+
Socket.ip_address_list.select do |addrinfo|
|
379
|
+
addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
|
380
|
+
end.map { |addrinfo| addrinfo.ip_address }.uniq
|
381
|
+
end
|
382
|
+
|
383
|
+
def socket_activation_fd(int)
|
384
|
+
int + 3 # 3 is the magic number you add to follow the SA protocol
|
385
|
+
end
|
252
386
|
end
|
253
387
|
end
|
data/lib/puma/cli.rb
CHANGED
@@ -1,726 +1,231 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'optparse'
|
2
4
|
require 'uri'
|
3
5
|
|
4
|
-
require 'puma
|
5
|
-
require 'puma/const'
|
6
|
+
require 'puma'
|
6
7
|
require 'puma/configuration'
|
7
|
-
require 'puma/
|
8
|
-
require 'puma/
|
9
|
-
require 'puma/
|
10
|
-
require 'puma/util'
|
11
|
-
|
12
|
-
require 'rack/commonlogger'
|
13
|
-
require 'rack/utils'
|
8
|
+
require 'puma/launcher'
|
9
|
+
require 'puma/const'
|
10
|
+
require 'puma/events'
|
14
11
|
|
15
12
|
module Puma
|
13
|
+
class << self
|
14
|
+
# The CLI exports its Puma::Configuration object here to allow
|
15
|
+
# apps to pick it up. An app needs to use it conditionally though
|
16
|
+
# since it is not set if the app is launched via another
|
17
|
+
# mechanism than the CLI class.
|
18
|
+
attr_accessor :cli_config
|
19
|
+
end
|
20
|
+
|
16
21
|
# Handles invoke a Puma::Server in a command line style.
|
17
22
|
#
|
18
23
|
class CLI
|
24
|
+
KEYS_NOT_TO_PERSIST_IN_STATE = Launcher::KEYS_NOT_TO_PERSIST_IN_STATE
|
25
|
+
|
19
26
|
# Create a new CLI object using +argv+ as the command line
|
20
27
|
# arguments.
|
21
28
|
#
|
22
29
|
# +stdout+ and +stderr+ can be set to IO-like objects which
|
23
30
|
# this object will report status on.
|
24
31
|
#
|
25
|
-
def initialize(argv,
|
32
|
+
def initialize(argv, events=Events.stdio)
|
26
33
|
@debug = false
|
27
|
-
@argv = argv
|
28
|
-
@stdout = stdout
|
29
|
-
@stderr = stderr
|
30
|
-
|
31
|
-
@phase = 0
|
32
|
-
@workers = []
|
34
|
+
@argv = argv.dup
|
33
35
|
|
34
|
-
@events =
|
36
|
+
@events = events
|
35
37
|
|
36
|
-
@
|
37
|
-
@status = nil
|
38
|
+
@conf = nil
|
38
39
|
|
39
|
-
@
|
40
|
-
@
|
40
|
+
@stdout = nil
|
41
|
+
@stderr = nil
|
42
|
+
@append = false
|
41
43
|
|
42
|
-
|
44
|
+
@control_url = nil
|
45
|
+
@control_options = {}
|
43
46
|
|
44
47
|
setup_options
|
45
48
|
|
46
|
-
|
47
|
-
|
48
|
-
@binder = Binder.new(@events)
|
49
|
-
@binder.import_from_env
|
50
|
-
end
|
51
|
-
|
52
|
-
def restart_on_stop!
|
53
|
-
@restart = true
|
54
|
-
end
|
55
|
-
|
56
|
-
def generate_restart_data
|
57
|
-
# Use the same trick as unicorn, namely favor PWD because
|
58
|
-
# it will contain an unresolved symlink, useful for when
|
59
|
-
# the pwd is /data/releases/current.
|
60
|
-
if dir = ENV['PWD']
|
61
|
-
s_env = File.stat(dir)
|
62
|
-
s_pwd = File.stat(Dir.pwd)
|
63
|
-
|
64
|
-
if s_env.ino == s_pwd.ino and s_env.dev == s_pwd.dev
|
65
|
-
@restart_dir = dir
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
@restart_dir ||= Dir.pwd
|
70
|
-
|
71
|
-
@original_argv = ARGV.dup
|
72
|
-
|
73
|
-
if defined? Rubinius::OS_ARGV
|
74
|
-
@restart_argv = Rubinius::OS_ARGV
|
75
|
-
else
|
76
|
-
require 'rubygems'
|
77
|
-
|
78
|
-
# if $0 is a file in the current directory, then restart
|
79
|
-
# it the same, otherwise add -S on there because it was
|
80
|
-
# picked up in PATH.
|
81
|
-
#
|
82
|
-
if File.exists?($0)
|
83
|
-
arg0 = [Gem.ruby, $0]
|
84
|
-
else
|
85
|
-
arg0 = [Gem.ruby, "-S", $0]
|
86
|
-
end
|
87
|
-
|
88
|
-
# Detect and reinject -Ilib from the command line
|
89
|
-
lib = File.expand_path "lib"
|
90
|
-
arg0[1,0] = ["-I", lib] if $:[0] == lib
|
91
|
-
|
92
|
-
@restart_argv = arg0 + ARGV
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
def restart!
|
97
|
-
@options[:on_restart].each do |blk|
|
98
|
-
blk.call self
|
99
|
-
end
|
100
|
-
|
101
|
-
if jruby?
|
102
|
-
@binder.listeners.each_with_index do |(str,io),i|
|
103
|
-
io.close
|
104
|
-
|
105
|
-
# We have to unlink a unix socket path that's not being used
|
106
|
-
uri = URI.parse str
|
107
|
-
if uri.scheme == "unix"
|
108
|
-
path = "#{uri.host}#{uri.path}"
|
109
|
-
File.unlink path
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
require 'puma/jruby_restart'
|
114
|
-
JRubyRestart.chdir_exec(@restart_dir, Gem.ruby, *@restart_argv)
|
115
|
-
else
|
116
|
-
@binder.listeners.each_with_index do |(l,io),i|
|
117
|
-
ENV["PUMA_INHERIT_#{i}"] = "#{io.to_i}:#{l}"
|
118
|
-
end
|
119
|
-
|
120
|
-
if cmd = @options[:restart_cmd]
|
121
|
-
argv = cmd.split(' ') + @original_argv
|
122
|
-
else
|
123
|
-
argv = @restart_argv
|
124
|
-
end
|
125
|
-
|
126
|
-
Dir.chdir @restart_dir
|
127
|
-
Kernel.exec(*argv)
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
# Delegate +log+ to +@events+
|
132
|
-
#
|
133
|
-
def log(str)
|
134
|
-
@events.log str
|
135
|
-
end
|
136
|
-
|
137
|
-
# Delegate +error+ to +@events+
|
138
|
-
#
|
139
|
-
def error(str)
|
140
|
-
@events.error str
|
141
|
-
end
|
142
|
-
|
143
|
-
def debug(str)
|
144
|
-
if @options[:debug]
|
145
|
-
@events.log "- #{str}"
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
def jruby?
|
150
|
-
IS_JRUBY
|
151
|
-
end
|
152
|
-
|
153
|
-
def windows?
|
154
|
-
RUBY_PLATFORM =~ /mswin32|ming32/
|
155
|
-
end
|
156
|
-
|
157
|
-
def unsupported(str, cond=true)
|
158
|
-
return unless cond
|
159
|
-
@events.error str
|
160
|
-
raise UnsupportedOption
|
161
|
-
end
|
162
|
-
|
163
|
-
# Build the OptionParser object to handle the available options.
|
164
|
-
#
|
165
|
-
def setup_options
|
166
|
-
@options = {
|
167
|
-
:min_threads => 0,
|
168
|
-
:max_threads => 16,
|
169
|
-
:quiet => false,
|
170
|
-
:debug => false,
|
171
|
-
:binds => [],
|
172
|
-
:workers => 0,
|
173
|
-
:daemon => false,
|
174
|
-
:environment => "development"
|
175
|
-
}
|
176
|
-
|
177
|
-
@parser = OptionParser.new do |o|
|
178
|
-
o.on "-b", "--bind URI", "URI to bind to (tcp://, unix://, ssl://)" do |arg|
|
179
|
-
@options[:binds] << arg
|
180
|
-
end
|
181
|
-
|
182
|
-
o.on "-C", "--config PATH", "Load PATH as a config file" do |arg|
|
183
|
-
@options[:config_file] = arg
|
184
|
-
end
|
185
|
-
|
186
|
-
o.on "--control URL", "The bind url to use for the control server",
|
187
|
-
"Use 'auto' to use temp unix server" do |arg|
|
188
|
-
if arg
|
189
|
-
@options[:control_url] = arg
|
190
|
-
elsif jruby?
|
191
|
-
unsupported "No default url available on JRuby"
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
o.on "--control-token TOKEN",
|
196
|
-
"The token to use as authentication for the control server" do |arg|
|
197
|
-
@options[:control_auth_token] = arg
|
198
|
-
end
|
199
|
-
|
200
|
-
o.on "-d", "--daemon", "Daemonize the server into the background" do
|
201
|
-
@options[:daemon] = true
|
202
|
-
@options[:quiet] = true
|
203
|
-
end
|
204
|
-
|
205
|
-
o.on "--debug", "Log lowlevel debugging information" do
|
206
|
-
@options[:debug] = true
|
207
|
-
end
|
208
|
-
|
209
|
-
o.on "--dir DIR", "Change to DIR before starting" do |d|
|
210
|
-
@options[:directory] = d.to_s
|
211
|
-
end
|
212
|
-
|
213
|
-
o.on "-e", "--environment ENVIRONMENT",
|
214
|
-
"The environment to run the Rack app on (default development)" do |arg|
|
215
|
-
@options[:environment] = arg
|
216
|
-
end
|
217
|
-
|
218
|
-
o.on "-I", "--include PATH", "Specify $LOAD_PATH directories" do |arg|
|
219
|
-
$LOAD_PATH.unshift(*arg.split(':'))
|
220
|
-
end
|
221
|
-
|
222
|
-
o.on "-p", "--port PORT", "Define what port TCP port to bind to",
|
223
|
-
"Use -b for more advanced options" do |arg|
|
224
|
-
@options[:binds] << "tcp://#{Configuration::DefaultTCPHost}:#{arg}"
|
225
|
-
end
|
226
|
-
|
227
|
-
o.on "--pidfile PATH", "Use PATH as a pidfile" do |arg|
|
228
|
-
@options[:pidfile] = arg
|
229
|
-
end
|
230
|
-
|
231
|
-
o.on "-q", "--quiet", "Quiet down the output" do
|
232
|
-
@options[:quiet] = true
|
233
|
-
end
|
234
|
-
|
235
|
-
o.on "-R", "--restart-cmd CMD",
|
236
|
-
"The puma command to run during a hot restart",
|
237
|
-
"Default: inferred" do |cmd|
|
238
|
-
@options[:restart_cmd] = cmd
|
239
|
-
end
|
240
|
-
|
241
|
-
o.on "-S", "--state PATH", "Where to store the state details" do |arg|
|
242
|
-
@options[:state] = arg
|
243
|
-
end
|
49
|
+
begin
|
50
|
+
@parser.parse! @argv
|
244
51
|
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
@options[:min_threads] = min.to_i
|
249
|
-
@options[:max_threads] = max.to_i
|
250
|
-
else
|
251
|
-
@options[:min_threads] = 0
|
252
|
-
@options[:max_threads] = arg.to_i
|
52
|
+
if file = @argv.shift
|
53
|
+
@conf.configure do |user_config, file_config|
|
54
|
+
file_config.rackup file
|
253
55
|
end
|
254
56
|
end
|
255
|
-
|
256
|
-
o.on "-w", "--workers COUNT",
|
257
|
-
"Activate cluster mode: How many worker processes to create" do |arg|
|
258
|
-
unsupported "-w not supported on JRuby and Windows",
|
259
|
-
jruby? || windows?
|
260
|
-
|
261
|
-
@options[:workers] = arg.to_i
|
262
|
-
end
|
263
|
-
|
264
|
-
end
|
265
|
-
|
266
|
-
@parser.banner = "puma <options> <rackup file>"
|
267
|
-
|
268
|
-
@parser.on_tail "-h", "--help", "Show help" do
|
269
|
-
log @parser
|
57
|
+
rescue UnsupportedOption
|
270
58
|
exit 1
|
271
59
|
end
|
272
|
-
end
|
273
60
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
def write_pid
|
278
|
-
if path = @options[:pidfile]
|
279
|
-
File.open(path, "w") do |f|
|
280
|
-
f.puts Process.pid
|
61
|
+
@conf.configure do |user_config, file_config|
|
62
|
+
if @stdout || @stderr
|
63
|
+
user_config.stdout_redirect @stdout, @stderr, @append
|
281
64
|
end
|
282
|
-
end
|
283
|
-
end
|
284
|
-
|
285
|
-
def set_rack_environment
|
286
|
-
# Try the user option first, then the environment variable,
|
287
|
-
# finally default to development
|
288
|
-
|
289
|
-
ENV['RACK_ENV'] = @options[:environment] ||
|
290
|
-
ENV['RACK_ENV'] ||
|
291
|
-
'development'
|
292
|
-
end
|
293
|
-
|
294
|
-
def delete_pidfile
|
295
|
-
if path = @options[:pidfile]
|
296
|
-
File.unlink path
|
297
|
-
end
|
298
|
-
end
|
299
|
-
|
300
|
-
def write_state
|
301
|
-
write_pid
|
302
|
-
|
303
|
-
require 'yaml'
|
304
|
-
|
305
|
-
if path = @options[:state]
|
306
|
-
state = { "pid" => Process.pid }
|
307
65
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
state["config"] = cfg
|
312
|
-
|
313
|
-
File.open(path, "w") do |f|
|
314
|
-
f.write state.to_yaml
|
66
|
+
if @control_url
|
67
|
+
user_config.activate_control_app @control_url, @control_options
|
315
68
|
end
|
316
69
|
end
|
317
|
-
end
|
318
|
-
|
319
|
-
# :nodoc:
|
320
|
-
def parse_options
|
321
|
-
@parser.parse! @argv
|
322
70
|
|
323
|
-
|
324
|
-
@options[:rackup] = @argv.shift
|
325
|
-
end
|
326
|
-
|
327
|
-
@config = Puma::Configuration.new @options
|
328
|
-
@config.load
|
329
|
-
end
|
330
|
-
|
331
|
-
def graceful_stop(server)
|
332
|
-
log " - Gracefully stopping, waiting for requests to finish"
|
333
|
-
@status.stop(true) if @status
|
334
|
-
server.stop(true)
|
335
|
-
delete_pidfile
|
336
|
-
log " - Goodbye!"
|
71
|
+
@launcher = Puma::Launcher.new(@conf, :events => @events, :argv => argv)
|
337
72
|
end
|
338
73
|
|
339
|
-
|
340
|
-
stdout = @options[:redirect_stdout]
|
341
|
-
stderr = @options[:redirect_stderr]
|
342
|
-
append = @options[:redirect_append]
|
343
|
-
|
344
|
-
if stdout
|
345
|
-
STDOUT.reopen stdout, (append ? "a" : "w")
|
346
|
-
STDOUT.puts "=== puma startup: #{Time.now} ==="
|
347
|
-
end
|
348
|
-
|
349
|
-
if stderr
|
350
|
-
STDERR.reopen stderr, (append ? "a" : "w")
|
351
|
-
STDERR.puts "=== puma startup: #{Time.now} ==="
|
352
|
-
end
|
353
|
-
end
|
74
|
+
attr_reader :launcher
|
354
75
|
|
355
76
|
# Parse the options, load the rackup, start the server and wait
|
356
77
|
# for it to finish.
|
357
78
|
#
|
358
79
|
def run
|
359
|
-
|
360
|
-
parse_options
|
361
|
-
rescue UnsupportedOption
|
362
|
-
exit 1
|
363
|
-
end
|
364
|
-
|
365
|
-
if dir = @options[:directory]
|
366
|
-
Dir.chdir dir
|
367
|
-
end
|
368
|
-
|
369
|
-
clustered = @options[:workers] > 0
|
370
|
-
|
371
|
-
if clustered
|
372
|
-
@events = PidEvents.new STDOUT, STDERR
|
373
|
-
@options[:logger] = @events
|
374
|
-
end
|
375
|
-
|
376
|
-
set_rack_environment
|
377
|
-
|
378
|
-
if clustered
|
379
|
-
run_cluster
|
380
|
-
else
|
381
|
-
run_single
|
382
|
-
end
|
80
|
+
@launcher.run
|
383
81
|
end
|
384
82
|
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
max_t = @options[:max_threads]
|
390
|
-
|
391
|
-
log "Puma #{Puma::Const::PUMA_VERSION} starting..."
|
392
|
-
log "* Min threads: #{min_t}, max threads: #{max_t}"
|
393
|
-
log "* Environment: #{ENV['RACK_ENV']}"
|
394
|
-
|
395
|
-
@binder.parse @options[:binds], self
|
396
|
-
|
397
|
-
if @options[:daemon]
|
398
|
-
Process.daemon(true, true)
|
399
|
-
end
|
400
|
-
|
401
|
-
server = Puma::Server.new @config.app, @events
|
402
|
-
server.binder = @binder
|
403
|
-
server.min_threads = min_t
|
404
|
-
server.max_threads = max_t
|
405
|
-
|
406
|
-
@server = server
|
407
|
-
|
408
|
-
if str = @options[:control_url]
|
409
|
-
require 'puma/app/status'
|
410
|
-
|
411
|
-
uri = URI.parse str
|
412
|
-
|
413
|
-
app = Puma::App::Status.new server, self
|
414
|
-
|
415
|
-
if token = @options[:control_auth_token]
|
416
|
-
app.auth_token = token unless token.empty? or token == :none
|
417
|
-
end
|
418
|
-
|
419
|
-
status = Puma::Server.new app, @events
|
420
|
-
status.min_threads = 0
|
421
|
-
status.max_threads = 1
|
422
|
-
|
423
|
-
case uri.scheme
|
424
|
-
when "tcp"
|
425
|
-
log "* Starting status server on #{str}"
|
426
|
-
status.add_tcp_listener uri.host, uri.port
|
427
|
-
when "unix"
|
428
|
-
log "* Starting status server on #{str}"
|
429
|
-
path = "#{uri.host}#{uri.path}"
|
430
|
-
|
431
|
-
status.add_unix_listener path
|
432
|
-
else
|
433
|
-
error "Invalid status URI: #{str}"
|
434
|
-
end
|
435
|
-
|
436
|
-
status.run
|
437
|
-
@status = status
|
438
|
-
end
|
439
|
-
|
440
|
-
begin
|
441
|
-
Signal.trap "SIGUSR2" do
|
442
|
-
@restart = true
|
443
|
-
server.begin_restart
|
444
|
-
end
|
445
|
-
rescue Exception
|
446
|
-
log "*** Sorry signal SIGUSR2 not implemented, restart feature disabled!"
|
447
|
-
end
|
448
|
-
|
449
|
-
begin
|
450
|
-
Signal.trap "SIGTERM" do
|
451
|
-
log " - Gracefully stopping, waiting for requests to finish"
|
452
|
-
server.stop false
|
453
|
-
end
|
454
|
-
rescue Exception
|
455
|
-
log "*** Sorry signal SIGTERM not implemented, gracefully stopping feature disabled!"
|
456
|
-
end
|
457
|
-
|
458
|
-
unless @options[:daemon]
|
459
|
-
log "Use Ctrl-C to stop"
|
460
|
-
end
|
461
|
-
|
462
|
-
redirect_io
|
463
|
-
|
464
|
-
if jruby?
|
465
|
-
Signal.trap("INT") do
|
466
|
-
graceful_stop server
|
467
|
-
exit
|
468
|
-
end
|
469
|
-
end
|
470
|
-
|
471
|
-
begin
|
472
|
-
server.run.join
|
473
|
-
rescue Interrupt
|
474
|
-
graceful_stop server
|
475
|
-
end
|
476
|
-
|
477
|
-
if @restart
|
478
|
-
log "* Restarting..."
|
479
|
-
@status.stop true if @status
|
480
|
-
restart!
|
481
|
-
end
|
482
|
-
end
|
483
|
-
|
484
|
-
def worker
|
485
|
-
$0 = "puma: cluster worker: #{@master_pid}"
|
486
|
-
Signal.trap "SIGINT", "IGNORE"
|
487
|
-
|
488
|
-
@master_read.close
|
489
|
-
@suicide_pipe.close
|
490
|
-
|
491
|
-
Thread.new do
|
492
|
-
IO.select [@check_pipe]
|
493
|
-
log "! Detected parent died, dieing"
|
494
|
-
exit! 1
|
495
|
-
end
|
496
|
-
|
497
|
-
min_t = @options[:min_threads]
|
498
|
-
max_t = @options[:max_threads]
|
499
|
-
|
500
|
-
server = Puma::Server.new @config.app, @events
|
501
|
-
server.min_threads = min_t
|
502
|
-
server.max_threads = max_t
|
503
|
-
server.binder = @binder
|
504
|
-
|
505
|
-
Signal.trap "SIGTERM" do
|
506
|
-
server.stop
|
507
|
-
end
|
508
|
-
|
509
|
-
@worker_write << "b#{Process.pid}\n"
|
510
|
-
|
511
|
-
server.run.join
|
512
|
-
|
513
|
-
ensure
|
514
|
-
@worker_write.close
|
83
|
+
private
|
84
|
+
def unsupported(str)
|
85
|
+
@events.error(str)
|
86
|
+
raise UnsupportedOption
|
515
87
|
end
|
516
88
|
|
517
|
-
def
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
Process.waitall
|
523
|
-
rescue Interrupt
|
524
|
-
log "! Cancelled waiting for workers"
|
525
|
-
else
|
526
|
-
log "- Goodbye!"
|
89
|
+
def configure_control_url(command_line_arg)
|
90
|
+
if command_line_arg
|
91
|
+
@control_url = command_line_arg
|
92
|
+
elsif Puma.jruby?
|
93
|
+
unsupported "No default url available on JRuby"
|
527
94
|
end
|
528
95
|
end
|
529
96
|
|
530
|
-
|
531
|
-
|
532
|
-
log "- Starting phased worker restart, phase: #{@phase}"
|
533
|
-
end
|
534
|
-
|
535
|
-
class Worker
|
536
|
-
def initialize(pid, phase)
|
537
|
-
@pid = pid
|
538
|
-
@phase = phase
|
539
|
-
@stage = :started
|
540
|
-
end
|
541
|
-
|
542
|
-
attr_reader :pid, :phase
|
543
|
-
|
544
|
-
def booted?
|
545
|
-
@stage == :booted
|
546
|
-
end
|
547
|
-
|
548
|
-
def boot!
|
549
|
-
@stage = :booted
|
550
|
-
end
|
551
|
-
|
552
|
-
def term
|
553
|
-
begin
|
554
|
-
Process.kill "TERM", @pid
|
555
|
-
rescue Errno::ESRCH
|
556
|
-
end
|
557
|
-
end
|
558
|
-
end
|
97
|
+
# Build the OptionParser object to handle the available options.
|
98
|
+
#
|
559
99
|
|
560
|
-
def
|
561
|
-
|
100
|
+
def setup_options
|
101
|
+
@conf = Configuration.new do |user_config, file_config|
|
102
|
+
@parser = OptionParser.new do |o|
|
103
|
+
o.on "-b", "--bind URI", "URI to bind to (tcp://, unix://, ssl://)" do |arg|
|
104
|
+
user_config.bind arg
|
105
|
+
end
|
562
106
|
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
@workers << Worker.new(pid, @phase)
|
567
|
-
end
|
107
|
+
o.on "-C", "--config PATH", "Load PATH as a config file" do |arg|
|
108
|
+
file_config.load arg
|
109
|
+
end
|
568
110
|
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
end
|
111
|
+
o.on "--control-url URL", "The bind url to use for the control server. Use 'auto' to use temp unix server" do |arg|
|
112
|
+
configure_control_url(arg)
|
113
|
+
end
|
573
114
|
|
574
|
-
|
575
|
-
|
576
|
-
|
115
|
+
o.on "--control-token TOKEN",
|
116
|
+
"The token to use as authentication for the control server" do |arg|
|
117
|
+
@control_options[:auth_token] = arg
|
118
|
+
end
|
577
119
|
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
break unless pid
|
120
|
+
o.on "--debug", "Log lowlevel debugging information" do
|
121
|
+
user_config.debug
|
122
|
+
end
|
582
123
|
|
583
|
-
|
584
|
-
|
124
|
+
o.on "--dir DIR", "Change to DIR before starting" do |d|
|
125
|
+
user_config.directory d
|
126
|
+
end
|
585
127
|
|
586
|
-
|
128
|
+
o.on "-e", "--environment ENVIRONMENT",
|
129
|
+
"The environment to run the Rack app on (default development)" do |arg|
|
130
|
+
user_config.environment arg
|
131
|
+
end
|
587
132
|
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
w = @workers.find { |x| x.phase != @phase }
|
133
|
+
o.on "-f", "--fork-worker=[REQUESTS]", OptionParser::DecimalInteger,
|
134
|
+
"Fork new workers from existing worker. Cluster mode only",
|
135
|
+
"Auto-refork after REQUESTS (default 1000)" do |*args|
|
136
|
+
user_config.fork_worker(*args.compact)
|
137
|
+
end
|
594
138
|
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
w.term
|
599
|
-
end
|
600
|
-
end
|
601
|
-
end
|
139
|
+
o.on "-I", "--include PATH", "Specify $LOAD_PATH directories" do |arg|
|
140
|
+
$LOAD_PATH.unshift(*arg.split(':'))
|
141
|
+
end
|
602
142
|
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
log "* Environment: #{ENV['RACK_ENV']}"
|
143
|
+
o.on "-p", "--port PORT", "Define the TCP port to bind to",
|
144
|
+
"Use -b for more advanced options" do |arg|
|
145
|
+
user_config.bind "tcp://#{Configuration::DefaultTCPHost}:#{arg}"
|
146
|
+
end
|
608
147
|
|
609
|
-
|
148
|
+
o.on "--pidfile PATH", "Use PATH as a pidfile" do |arg|
|
149
|
+
user_config.pidfile arg
|
150
|
+
end
|
610
151
|
|
611
|
-
|
152
|
+
o.on "--preload", "Preload the app. Cluster mode only" do
|
153
|
+
user_config.preload_app!
|
154
|
+
end
|
612
155
|
|
613
|
-
|
156
|
+
o.on "--prune-bundler", "Prune out the bundler env if possible" do
|
157
|
+
user_config.prune_bundler
|
158
|
+
end
|
614
159
|
|
615
|
-
|
616
|
-
|
617
|
-
|
160
|
+
o.on "--extra-runtime-dependencies GEM1,GEM2", "Defines any extra needed gems when using --prune-bundler" do |arg|
|
161
|
+
user_config.extra_runtime_dependencies arg.split(',')
|
162
|
+
end
|
618
163
|
|
619
|
-
|
164
|
+
o.on "-q", "--quiet", "Do not log requests internally (default true)" do
|
165
|
+
user_config.quiet
|
166
|
+
end
|
620
167
|
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
stop = true
|
625
|
-
write.write "!"
|
626
|
-
end
|
627
|
-
rescue Exception
|
628
|
-
end
|
168
|
+
o.on "-v", "--log-requests", "Log requests as they occur" do
|
169
|
+
user_config.log_requests
|
170
|
+
end
|
629
171
|
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
rescue Exception
|
636
|
-
end
|
172
|
+
o.on "-R", "--restart-cmd CMD",
|
173
|
+
"The puma command to run during a hot restart",
|
174
|
+
"Default: inferred" do |cmd|
|
175
|
+
user_config.restart_command cmd
|
176
|
+
end
|
637
177
|
|
638
|
-
|
178
|
+
o.on "-S", "--state PATH", "Where to store the state details" do |arg|
|
179
|
+
user_config.state_path arg
|
180
|
+
end
|
639
181
|
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
182
|
+
o.on '-t', '--threads INT', "min:max threads to use (default 0:16)" do |arg|
|
183
|
+
min, max = arg.split(":")
|
184
|
+
if max
|
185
|
+
user_config.threads min, max
|
186
|
+
else
|
187
|
+
user_config.threads min, min
|
188
|
+
end
|
189
|
+
end
|
647
190
|
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
# closed.
|
652
|
-
#
|
653
|
-
@check_pipe, @suicide_pipe = Puma::Util.pipe
|
654
|
-
|
655
|
-
if @options[:daemon]
|
656
|
-
Process.daemon(true, true)
|
657
|
-
else
|
658
|
-
log "Use Ctrl-C to stop"
|
659
|
-
end
|
191
|
+
o.on "--early-hints", "Enable early hints support" do
|
192
|
+
user_config.early_hints
|
193
|
+
end
|
660
194
|
|
661
|
-
|
195
|
+
o.on "-V", "--version", "Print the version information" do
|
196
|
+
puts "puma version #{Puma::Const::VERSION}"
|
197
|
+
exit 0
|
198
|
+
end
|
662
199
|
|
663
|
-
|
200
|
+
o.on "-w", "--workers COUNT",
|
201
|
+
"Activate cluster mode: How many worker processes to create" do |arg|
|
202
|
+
user_config.workers arg
|
203
|
+
end
|
664
204
|
|
665
|
-
|
666
|
-
|
205
|
+
o.on "--tag NAME", "Additional text to display in process listing" do |arg|
|
206
|
+
user_config.tag arg
|
207
|
+
end
|
667
208
|
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
end
|
209
|
+
o.on "--redirect-stdout FILE", "Redirect STDOUT to a specific file" do |arg|
|
210
|
+
@stdout = arg.to_s
|
211
|
+
end
|
672
212
|
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
res = IO.select([read], nil, nil, 5)
|
677
|
-
|
678
|
-
if res
|
679
|
-
req = read.read_nonblock(1)
|
680
|
-
|
681
|
-
if req == "b"
|
682
|
-
pid = read.gets.to_i
|
683
|
-
w = @workers.find { |w| w.pid == pid }
|
684
|
-
if w
|
685
|
-
w.boot!
|
686
|
-
log "- Worker #{pid} booted, phase: #{w.phase}"
|
687
|
-
else
|
688
|
-
log "! Out-of-sync worker list, no #{pid} worker"
|
689
|
-
end
|
690
|
-
end
|
691
|
-
end
|
213
|
+
o.on "--redirect-stderr FILE", "Redirect STDERR to a specific file" do |arg|
|
214
|
+
@stderr = arg.to_s
|
215
|
+
end
|
692
216
|
|
693
|
-
|
217
|
+
o.on "--[no-]redirect-append", "Append to redirected files" do |val|
|
218
|
+
@append = val
|
219
|
+
end
|
694
220
|
|
695
|
-
|
696
|
-
start_phased_restart
|
697
|
-
phased_restart = false
|
698
|
-
end
|
221
|
+
o.banner = "puma <options> <rackup file>"
|
699
222
|
|
700
|
-
|
701
|
-
|
223
|
+
o.on_tail "-h", "--help", "Show help" do
|
224
|
+
$stdout.puts o
|
225
|
+
exit 0
|
702
226
|
end
|
703
227
|
end
|
704
|
-
|
705
|
-
stop_workers
|
706
|
-
ensure
|
707
|
-
delete_pidfile
|
708
|
-
@check_pipe.close
|
709
|
-
@suicide_pipe.close
|
710
|
-
read.close
|
711
|
-
write.close
|
712
228
|
end
|
713
|
-
|
714
|
-
if @restart
|
715
|
-
log "* Restarting..."
|
716
|
-
restart!
|
717
|
-
end
|
718
|
-
end
|
719
|
-
|
720
|
-
def stop
|
721
|
-
@status.stop(true) if @status
|
722
|
-
@server.stop(true) if @server
|
723
|
-
delete_pidfile
|
724
229
|
end
|
725
230
|
end
|
726
231
|
end
|