puma 2.7.0 → 3.1.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 +5 -13
- data/DEPLOYMENT.md +91 -0
- data/Gemfile +3 -2
- data/History.txt +624 -1
- data/Manifest.txt +15 -3
- data/README.md +129 -14
- data/Rakefile +3 -3
- data/bin/puma-wild +31 -0
- data/bin/pumactl +1 -1
- data/docs/nginx.md +1 -1
- data/docs/signals.md +43 -0
- data/ext/puma_http11/extconf.rb +7 -2
- data/ext/puma_http11/http11_parser.java.rl +5 -5
- data/ext/puma_http11/io_buffer.c +1 -1
- data/ext/puma_http11/mini_ssl.c +233 -18
- data/ext/puma_http11/org/jruby/puma/Http11.java +12 -3
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +39 -39
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +245 -195
- data/ext/puma_http11/puma_http11.c +12 -4
- data/lib/puma.rb +1 -0
- data/lib/puma/app/status.rb +7 -0
- data/lib/puma/binder.rb +108 -39
- data/lib/puma/capistrano.rb +23 -6
- data/lib/puma/cli.rb +141 -446
- data/lib/puma/client.rb +48 -1
- data/lib/puma/cluster.rb +207 -58
- data/lib/puma/commonlogger.rb +107 -0
- data/lib/puma/configuration.rb +262 -235
- data/lib/puma/const.rb +97 -14
- data/lib/puma/control_cli.rb +85 -77
- data/lib/puma/convenient.rb +23 -0
- data/lib/puma/daemon_ext.rb +11 -4
- data/lib/puma/detect.rb +8 -1
- data/lib/puma/dsl.rb +456 -0
- data/lib/puma/events.rb +35 -18
- data/lib/puma/jruby_restart.rb +1 -1
- data/lib/puma/launcher.rb +399 -0
- data/lib/puma/minissl.rb +49 -20
- data/lib/puma/null_io.rb +15 -0
- data/lib/puma/plugin.rb +104 -0
- data/lib/puma/plugin/tmp_restart.rb +35 -0
- data/lib/puma/rack/backports/uri/common_18.rb +56 -0
- data/lib/puma/rack/backports/uri/common_192.rb +52 -0
- data/lib/puma/rack/backports/uri/common_193.rb +29 -0
- data/lib/puma/rack/builder.rb +295 -0
- data/lib/puma/rack/urlmap.rb +90 -0
- data/lib/puma/reactor.rb +14 -1
- data/lib/puma/runner.rb +35 -17
- data/lib/puma/server.rb +161 -58
- data/lib/puma/single.rb +15 -10
- data/lib/puma/state_file.rb +29 -0
- data/lib/puma/thread_pool.rb +88 -13
- data/lib/puma/util.rb +123 -0
- data/lib/rack/handler/puma.rb +35 -29
- data/puma.gemspec +2 -4
- data/tools/jungle/init.d/README.md +2 -2
- data/tools/jungle/init.d/puma +69 -7
- data/tools/jungle/upstart/puma.conf +8 -2
- metadata +51 -71
- data/COPYING +0 -55
- data/TODO +0 -5
- data/lib/puma/rack_patch.rb +0 -45
- data/test/test_app_status.rb +0 -92
- data/test/test_cli.rb +0 -173
- data/test/test_config.rb +0 -16
- data/test/test_http10.rb +0 -27
- data/test/test_http11.rb +0 -145
- data/test/test_integration.rb +0 -165
- data/test/test_iobuffer.rb +0 -38
- data/test/test_minissl.rb +0 -25
- data/test/test_null_io.rb +0 -31
- data/test/test_persistent.rb +0 -238
- data/test/test_puma_server.rb +0 -292
- data/test/test_rack_handler.rb +0 -10
- data/test/test_rack_server.rb +0 -141
- data/test/test_tcp_rack.rb +0 -42
- data/test/test_thread_pool.rb +0 -156
- data/test/test_unix_socket.rb +0 -39
- data/test/test_ws.rb +0 -89
@@ -176,14 +176,12 @@ static VALUE find_common_field_value(const char *field, size_t flen)
|
|
176
176
|
void http_field(puma_parser* hp, const char *field, size_t flen,
|
177
177
|
const char *value, size_t vlen)
|
178
178
|
{
|
179
|
-
VALUE v = Qnil;
|
180
179
|
VALUE f = Qnil;
|
180
|
+
VALUE v;
|
181
181
|
|
182
182
|
VALIDATE_MAX_LENGTH(flen, FIELD_NAME);
|
183
183
|
VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE);
|
184
184
|
|
185
|
-
v = rb_str_new(value, vlen);
|
186
|
-
|
187
185
|
f = find_common_field_value(field, flen);
|
188
186
|
|
189
187
|
if (f == Qnil) {
|
@@ -201,7 +199,17 @@ void http_field(puma_parser* hp, const char *field, size_t flen,
|
|
201
199
|
f = rb_str_new(hp->buf, new_size);
|
202
200
|
}
|
203
201
|
|
204
|
-
|
202
|
+
/* check for duplicate header */
|
203
|
+
v = rb_hash_aref(hp->request, f);
|
204
|
+
|
205
|
+
if (v == Qnil) {
|
206
|
+
v = rb_str_new(value, vlen);
|
207
|
+
rb_hash_aset(hp->request, f, v);
|
208
|
+
} else {
|
209
|
+
/* if duplicate header, normalize to comma-separated values */
|
210
|
+
rb_str_cat2(v, ", ");
|
211
|
+
rb_str_cat(v, value, vlen);
|
212
|
+
}
|
205
213
|
}
|
206
214
|
|
207
215
|
void request_method(puma_parser* hp, const char *at, size_t length)
|
data/lib/puma.rb
CHANGED
data/lib/puma/app/status.rb
CHANGED
@@ -48,6 +48,13 @@ module Puma
|
|
48
48
|
return rack_response(200, OK_STATUS)
|
49
49
|
end
|
50
50
|
|
51
|
+
when /\/reload-worker-directory$/
|
52
|
+
if !@cli.send(:reload_worker_directory)
|
53
|
+
return rack_response(404, '{ "error": "reload_worker_directory not available" }')
|
54
|
+
else
|
55
|
+
return rack_response(200, OK_STATUS)
|
56
|
+
end
|
57
|
+
|
51
58
|
when /\/stats$/
|
52
59
|
return rack_response(200, @cli.stats)
|
53
60
|
else
|
data/lib/puma/binder.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
require 'puma/const'
|
2
|
+
require 'uri'
|
2
3
|
|
3
4
|
module Puma
|
4
5
|
class Binder
|
5
6
|
include Puma::Const
|
6
7
|
|
8
|
+
RACK_VERSION = [1,3].freeze
|
9
|
+
|
7
10
|
def initialize(events)
|
8
11
|
@events = events
|
9
12
|
@listeners = []
|
@@ -11,21 +14,21 @@ module Puma
|
|
11
14
|
@unix_paths = []
|
12
15
|
|
13
16
|
@proto_env = {
|
14
|
-
"rack.version".freeze =>
|
17
|
+
"rack.version".freeze => RACK_VERSION,
|
15
18
|
"rack.errors".freeze => events.stderr,
|
16
19
|
"rack.multithread".freeze => true,
|
17
20
|
"rack.multiprocess".freeze => false,
|
18
21
|
"rack.run_once".freeze => false,
|
19
22
|
"SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
|
20
23
|
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
|
24
|
+
# I'd like to set a default CONTENT_TYPE here but some things
|
25
|
+
# depend on their not being a default set and infering
|
26
|
+
# it from the content. And so if i set it here, it won't
|
27
|
+
# infer properly.
|
25
28
|
|
26
29
|
"QUERY_STRING".freeze => "",
|
27
30
|
SERVER_PROTOCOL => HTTP_11,
|
28
|
-
SERVER_SOFTWARE =>
|
31
|
+
SERVER_SOFTWARE => PUMA_SERVER_STRING,
|
29
32
|
GATEWAY_INTERFACE => CGI_VER
|
30
33
|
}
|
31
34
|
|
@@ -87,13 +90,18 @@ module Puma
|
|
87
90
|
logger.log "* Inherited #{str}"
|
88
91
|
io = inherit_tcp_listener uri.host, uri.port, fd
|
89
92
|
else
|
93
|
+
params = Util.parse_query uri.query
|
94
|
+
|
95
|
+
opt = params.key?('low_latency')
|
96
|
+
bak = params.fetch('backlog', 1024).to_i
|
97
|
+
|
90
98
|
logger.log "* Listening on #{str}"
|
91
|
-
io = add_tcp_listener uri.host, uri.port
|
99
|
+
io = add_tcp_listener uri.host, uri.port, opt, bak
|
92
100
|
end
|
93
101
|
|
94
102
|
@listeners << [str, io]
|
95
103
|
when "unix"
|
96
|
-
path = "#{uri.host}#{uri.path}"
|
104
|
+
path = "#{uri.host}#{uri.path}".gsub("%20", " ")
|
97
105
|
|
98
106
|
if fd = @inherited_fds.delete(str)
|
99
107
|
logger.log "* Inherited #{str}"
|
@@ -102,42 +110,84 @@ module Puma
|
|
102
110
|
logger.log "* Listening on #{str}"
|
103
111
|
|
104
112
|
umask = nil
|
113
|
+
mode = nil
|
114
|
+
backlog = nil
|
105
115
|
|
106
116
|
if uri.query
|
107
|
-
params =
|
117
|
+
params = Util.parse_query uri.query
|
108
118
|
if u = params['umask']
|
109
119
|
# Use Integer() to respect the 0 prefix as octal
|
110
120
|
umask = Integer(u)
|
111
121
|
end
|
122
|
+
|
123
|
+
if u = params['mode']
|
124
|
+
mode = Integer('0'+u)
|
125
|
+
end
|
126
|
+
|
127
|
+
if u = params['backlog']
|
128
|
+
backlog = Integer(u)
|
129
|
+
end
|
112
130
|
end
|
113
131
|
|
114
|
-
io = add_unix_listener path, umask
|
132
|
+
io = add_unix_listener path, umask, mode, backlog
|
115
133
|
end
|
116
134
|
|
117
135
|
@listeners << [str, io]
|
118
136
|
when "ssl"
|
119
|
-
|
120
|
-
@events.error "SSL not supported on JRuby"
|
121
|
-
raise UnsupportedOption
|
122
|
-
end
|
137
|
+
MiniSSL.check
|
123
138
|
|
124
|
-
params =
|
139
|
+
params = Util.parse_query uri.query
|
125
140
|
require 'puma/minissl'
|
126
141
|
|
127
142
|
ctx = MiniSSL::Context.new
|
128
|
-
unless params['key']
|
129
|
-
@events.error "Please specify the SSL key via 'key='"
|
130
|
-
end
|
131
143
|
|
132
|
-
|
144
|
+
if defined?(JRUBY_VERSION)
|
145
|
+
unless params['keystore']
|
146
|
+
@events.error "Please specify the Java keystore via 'keystore='"
|
147
|
+
end
|
133
148
|
|
134
|
-
|
135
|
-
@events.error "Please specify the SSL cert via 'cert='"
|
136
|
-
end
|
149
|
+
ctx.keystore = params['keystore']
|
137
150
|
|
138
|
-
|
151
|
+
unless params['keystore-pass']
|
152
|
+
@events.error "Please specify the Java keystore password via 'keystore-pass='"
|
153
|
+
end
|
154
|
+
|
155
|
+
ctx.keystore_pass = params['keystore-pass']
|
156
|
+
else
|
157
|
+
unless params['key']
|
158
|
+
@events.error "Please specify the SSL key via 'key='"
|
159
|
+
end
|
160
|
+
|
161
|
+
ctx.key = params['key']
|
162
|
+
|
163
|
+
unless params['cert']
|
164
|
+
@events.error "Please specify the SSL cert via 'cert='"
|
165
|
+
end
|
166
|
+
|
167
|
+
ctx.cert = params['cert']
|
168
|
+
|
169
|
+
if ['peer', 'force_peer'].include?(params['verify_mode'])
|
170
|
+
unless params['ca']
|
171
|
+
@events.error "Please specify the SSL ca via 'ca='"
|
172
|
+
end
|
173
|
+
end
|
139
174
|
|
140
|
-
|
175
|
+
ctx.ca = params['ca'] if params['ca']
|
176
|
+
|
177
|
+
if params['verify_mode']
|
178
|
+
ctx.verify_mode = case params['verify_mode']
|
179
|
+
when "peer"
|
180
|
+
MiniSSL::VERIFY_PEER
|
181
|
+
when "force_peer"
|
182
|
+
MiniSSL::VERIFY_PEER | MiniSSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
183
|
+
when "none"
|
184
|
+
MiniSSL::VERIFY_NONE
|
185
|
+
else
|
186
|
+
@events.error "Please specify a valid verify_mode="
|
187
|
+
MiniSSL::VERIFY_NONE
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
141
191
|
|
142
192
|
if fd = @inherited_fds.delete(str)
|
143
193
|
logger.log "* Inherited #{str}"
|
@@ -186,17 +236,21 @@ module Puma
|
|
186
236
|
# allow to accumulate before returning connection refused.
|
187
237
|
#
|
188
238
|
def add_tcp_listener(host, port, optimize_for_latency=true, backlog=1024)
|
189
|
-
host = host[1..-2] if host[0..0] == '['
|
239
|
+
host = host[1..-2] if host and host[0..0] == '['
|
190
240
|
s = TCPServer.new(host, port)
|
191
241
|
if optimize_for_latency
|
192
242
|
s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
193
243
|
end
|
194
244
|
s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
195
245
|
s.listen backlog
|
246
|
+
@connected_port = s.addr[1]
|
247
|
+
|
196
248
|
@ios << s
|
197
249
|
s
|
198
250
|
end
|
199
251
|
|
252
|
+
attr_reader :connected_port
|
253
|
+
|
200
254
|
def inherit_tcp_listener(host, port, fd)
|
201
255
|
if fd.kind_of? TCPServer
|
202
256
|
s = fd
|
@@ -210,13 +264,11 @@ module Puma
|
|
210
264
|
|
211
265
|
def add_ssl_listener(host, port, ctx,
|
212
266
|
optimize_for_latency=true, backlog=1024)
|
213
|
-
if IS_JRUBY
|
214
|
-
@events.error "SSL not supported on JRuby"
|
215
|
-
raise UnsupportedOption
|
216
|
-
end
|
217
|
-
|
218
267
|
require 'puma/minissl'
|
219
268
|
|
269
|
+
MiniSSL.check
|
270
|
+
|
271
|
+
host = host[1..-2] if host[0..0] == '['
|
220
272
|
s = TCPServer.new(host, port)
|
221
273
|
if optimize_for_latency
|
222
274
|
s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
@@ -234,20 +286,24 @@ module Puma
|
|
234
286
|
end
|
235
287
|
|
236
288
|
def inherited_ssl_listener(fd, ctx)
|
237
|
-
if IS_JRUBY
|
238
|
-
@events.error "SSL not supported on JRuby"
|
239
|
-
raise UnsupportedOption
|
240
|
-
end
|
241
|
-
|
242
289
|
require 'puma/minissl'
|
290
|
+
MiniSSL.check
|
291
|
+
|
243
292
|
s = TCPServer.for_fd(fd)
|
244
|
-
|
293
|
+
ssl = MiniSSL::Server.new(s, ctx)
|
294
|
+
|
295
|
+
env = @proto_env.dup
|
296
|
+
env[HTTPS_KEY] = HTTPS
|
297
|
+
@envs[ssl] = env
|
298
|
+
|
299
|
+
@ios << ssl
|
300
|
+
|
245
301
|
s
|
246
302
|
end
|
247
303
|
|
248
304
|
# Tell the server to listen on +path+ as a UNIX domain socket.
|
249
305
|
#
|
250
|
-
def add_unix_listener(path, umask=nil)
|
306
|
+
def add_unix_listener(path, umask=nil, mode=nil, backlog=nil)
|
251
307
|
@unix_paths << path
|
252
308
|
|
253
309
|
# Let anyone connect by default
|
@@ -256,10 +312,10 @@ module Puma
|
|
256
312
|
begin
|
257
313
|
old_mask = File.umask(umask)
|
258
314
|
|
259
|
-
if File.
|
315
|
+
if File.exist? path
|
260
316
|
begin
|
261
317
|
old = UNIXSocket.new path
|
262
|
-
rescue SystemCallError
|
318
|
+
rescue SystemCallError, IOError
|
263
319
|
File.unlink path
|
264
320
|
else
|
265
321
|
old.close
|
@@ -268,11 +324,20 @@ module Puma
|
|
268
324
|
end
|
269
325
|
|
270
326
|
s = UNIXServer.new(path)
|
327
|
+
s.listen backlog if backlog
|
271
328
|
@ios << s
|
272
329
|
ensure
|
273
330
|
File.umask old_mask
|
274
331
|
end
|
275
332
|
|
333
|
+
if mode
|
334
|
+
File.chmod mode, path
|
335
|
+
end
|
336
|
+
|
337
|
+
env = @proto_env.dup
|
338
|
+
env[REMOTE_ADDR] = "127.0.0.1"
|
339
|
+
@envs[s] = env
|
340
|
+
|
276
341
|
s
|
277
342
|
end
|
278
343
|
|
@@ -286,6 +351,10 @@ module Puma
|
|
286
351
|
end
|
287
352
|
@ios << s
|
288
353
|
|
354
|
+
env = @proto_env.dup
|
355
|
+
env[REMOTE_ADDR] = "127.0.0.1"
|
356
|
+
@envs[s] = env
|
357
|
+
|
289
358
|
s
|
290
359
|
end
|
291
360
|
|
data/lib/puma/capistrano.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
+
$stderr.puts "DEPRECATED: To manage puma with capistrano, use https://github.com/seuros/capistrano-puma"
|
2
|
+
|
1
3
|
Capistrano::Configuration.instance.load do
|
2
|
-
after 'deploy:stop', 'puma:stop'
|
3
|
-
after 'deploy:start', 'puma:start'
|
4
|
-
after 'deploy:restart', 'puma:restart'
|
5
4
|
|
6
5
|
# Ensure the tmp/sockets directory is created by the deploy:setup task and
|
7
6
|
# symlinked in by the deploy:update task. This is not handled by Capistrano
|
8
7
|
# v2 but is fixed in v3.
|
9
8
|
shared_children.push('tmp/sockets')
|
10
9
|
|
10
|
+
_cset(:puma_default_hooks) { true }
|
11
11
|
_cset(:puma_cmd) { "#{fetch(:bundle_cmd, 'bundle')} exec puma" }
|
12
12
|
_cset(:pumactl_cmd) { "#{fetch(:bundle_cmd, 'bundle')} exec pumactl" }
|
13
13
|
_cset(:puma_env) { fetch(:rack_env, fetch(:rails_env, 'production')) }
|
@@ -15,10 +15,16 @@ Capistrano::Configuration.instance.load do
|
|
15
15
|
_cset(:puma_socket) { "unix://#{shared_path}/sockets/puma.sock" }
|
16
16
|
_cset(:puma_role) { :app }
|
17
17
|
|
18
|
+
if fetch(:puma_default_hooks)
|
19
|
+
after 'deploy:stop', 'puma:stop'
|
20
|
+
after 'deploy:start', 'puma:start'
|
21
|
+
after 'deploy:restart', 'puma:restart'
|
22
|
+
end
|
23
|
+
|
18
24
|
namespace :puma do
|
19
25
|
desc 'Start puma'
|
20
26
|
task :start, :roles => lambda { puma_role }, :on_no_matching_servers => :continue do
|
21
|
-
run "cd #{current_path} && #{puma_cmd} #{start_options}", :pty => false
|
27
|
+
run "cd #{current_path} && #{puma_rails_additional_env} #{puma_cmd} #{start_options}", :pty => false
|
22
28
|
end
|
23
29
|
|
24
30
|
desc 'Stop puma'
|
@@ -29,7 +35,7 @@ Capistrano::Configuration.instance.load do
|
|
29
35
|
desc 'Restart puma'
|
30
36
|
task :restart, :roles => lambda { puma_role }, :on_no_matching_servers => :continue do
|
31
37
|
begin
|
32
|
-
run "cd #{current_path} && #{pumactl_cmd} -S #{state_path} restart"
|
38
|
+
run "cd #{current_path} && #{puma_rails_additional_env} #{pumactl_cmd} -S #{state_path} restart"
|
33
39
|
rescue Capistrano::CommandError => ex
|
34
40
|
puts "Failed to restart puma: #{ex}\nAssuming not started."
|
35
41
|
start
|
@@ -38,7 +44,12 @@ Capistrano::Configuration.instance.load do
|
|
38
44
|
|
39
45
|
desc 'Restart puma (phased restart)'
|
40
46
|
task :phased_restart, :roles => lambda { puma_role }, :on_no_matching_servers => :continue do
|
41
|
-
|
47
|
+
begin
|
48
|
+
run "cd #{current_path} && #{puma_rails_additional_env} #{pumactl_cmd} -S #{state_path} phased-restart"
|
49
|
+
rescue Capistrano::CommandError => ex
|
50
|
+
puts "Failed to restart puma: #{ex}\nAssuming not started."
|
51
|
+
start
|
52
|
+
end
|
42
53
|
end
|
43
54
|
|
44
55
|
end
|
@@ -63,11 +74,17 @@ Capistrano::Configuration.instance.load do
|
|
63
74
|
fetch(:rack_env, fetch(:rails_env, 'production'))
|
64
75
|
end
|
65
76
|
|
77
|
+
#add additional env when start rails, such as : secret key, db username, db pwd or other what you want.
|
78
|
+
def puma_rails_additional_env
|
79
|
+
fetch(:puma_rails_additional_env, '')
|
80
|
+
end
|
81
|
+
|
66
82
|
def state_path
|
67
83
|
(config_file ? configuration.options[:state] : nil) || puma_state
|
68
84
|
end
|
69
85
|
|
70
86
|
def configuration
|
87
|
+
require 'puma'
|
71
88
|
require 'puma/configuration'
|
72
89
|
|
73
90
|
config = Puma::Configuration.new(:config_file => config_file)
|
data/lib/puma/cli.rb
CHANGED
@@ -1,23 +1,22 @@
|
|
1
1
|
require 'optparse'
|
2
2
|
require 'uri'
|
3
3
|
|
4
|
-
require 'puma/
|
5
|
-
require 'puma/const'
|
6
|
-
require 'puma/configuration'
|
7
|
-
require 'puma/binder'
|
8
|
-
require 'puma/detect'
|
9
|
-
require 'puma/daemon_ext'
|
10
|
-
require 'puma/util'
|
11
|
-
require 'puma/single'
|
12
|
-
require 'puma/cluster'
|
13
|
-
|
14
|
-
require 'rack/commonlogger'
|
15
|
-
require 'rack/utils'
|
4
|
+
require 'puma/launcher'
|
16
5
|
|
17
6
|
module Puma
|
7
|
+
class << self
|
8
|
+
# The CLI exports its Puma::Configuration object here to allow
|
9
|
+
# apps to pick it up. An app needs to use it conditionally though
|
10
|
+
# since it is not set if the app is launched via another
|
11
|
+
# mechanism than the CLI class.
|
12
|
+
attr_accessor :cli_config
|
13
|
+
end
|
14
|
+
|
18
15
|
# Handles invoke a Puma::Server in a command line style.
|
19
16
|
#
|
20
17
|
class CLI
|
18
|
+
KEYS_NOT_TO_PERSIST_IN_STATE = Launcher::KEYS_NOT_TO_PERSIST_IN_STATE
|
19
|
+
|
21
20
|
# Create a new CLI object using +argv+ as the command line
|
22
21
|
# arguments.
|
23
22
|
#
|
@@ -26,497 +25,193 @@ module Puma
|
|
26
25
|
#
|
27
26
|
def initialize(argv, events=Events.stdio)
|
28
27
|
@debug = false
|
29
|
-
@argv = argv
|
28
|
+
@argv = argv.dup
|
30
29
|
|
31
30
|
@events = events
|
32
31
|
|
33
|
-
@
|
34
|
-
@runner = nil
|
32
|
+
@conf = nil
|
35
33
|
|
36
|
-
@
|
34
|
+
@stdout = nil
|
35
|
+
@stderr = nil
|
36
|
+
@append = false
|
37
37
|
|
38
|
-
|
38
|
+
@control_url = nil
|
39
|
+
@control_options = {}
|
39
40
|
|
40
41
|
setup_options
|
41
|
-
generate_restart_data
|
42
|
-
|
43
|
-
@binder = Binder.new(@events)
|
44
|
-
@binder.import_from_env
|
45
|
-
end
|
46
|
-
|
47
|
-
# The Binder object containing the sockets bound to.
|
48
|
-
attr_reader :binder
|
49
|
-
|
50
|
-
# The Configuration object used.
|
51
|
-
attr_reader :config
|
52
|
-
|
53
|
-
# The Hash of options used to configure puma.
|
54
|
-
attr_reader :options
|
55
|
-
|
56
|
-
# The Events object used to output information.
|
57
|
-
attr_reader :events
|
58
|
-
|
59
|
-
# Delegate +log+ to +@events+
|
60
|
-
#
|
61
|
-
def log(str)
|
62
|
-
@events.log str
|
63
|
-
end
|
64
|
-
|
65
|
-
# Delegate +error+ to +@events+
|
66
|
-
#
|
67
|
-
def error(str)
|
68
|
-
@events.error str
|
69
|
-
end
|
70
|
-
|
71
|
-
def debug(str)
|
72
|
-
if @options[:debug]
|
73
|
-
@events.log "- #{str}"
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def jruby?
|
78
|
-
IS_JRUBY
|
79
|
-
end
|
80
|
-
|
81
|
-
def windows?
|
82
|
-
RUBY_PLATFORM =~ /mswin32|ming32/
|
83
|
-
end
|
84
|
-
|
85
|
-
def unsupported(str, cond=true)
|
86
|
-
return unless cond
|
87
|
-
@events.error str
|
88
|
-
raise UnsupportedOption
|
89
|
-
end
|
90
|
-
|
91
|
-
# Build the OptionParser object to handle the available options.
|
92
|
-
#
|
93
|
-
def setup_options
|
94
|
-
@options = {
|
95
|
-
:min_threads => 0,
|
96
|
-
:max_threads => 16,
|
97
|
-
:quiet => false,
|
98
|
-
:debug => false,
|
99
|
-
:binds => [],
|
100
|
-
:workers => 0,
|
101
|
-
:daemon => false,
|
102
|
-
:worker_boot => []
|
103
|
-
}
|
104
|
-
|
105
|
-
@parser = OptionParser.new do |o|
|
106
|
-
o.on "-b", "--bind URI", "URI to bind to (tcp://, unix://, ssl://)" do |arg|
|
107
|
-
@options[:binds] << arg
|
108
|
-
end
|
109
|
-
|
110
|
-
o.on "-C", "--config PATH", "Load PATH as a config file" do |arg|
|
111
|
-
@options[:config_file] = arg
|
112
|
-
end
|
113
|
-
|
114
|
-
o.on "--control URL", "The bind url to use for the control server",
|
115
|
-
"Use 'auto' to use temp unix server" do |arg|
|
116
|
-
if arg
|
117
|
-
@options[:control_url] = arg
|
118
|
-
elsif jruby?
|
119
|
-
unsupported "No default url available on JRuby"
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
o.on "--control-token TOKEN",
|
124
|
-
"The token to use as authentication for the control server" do |arg|
|
125
|
-
@options[:control_auth_token] = arg
|
126
|
-
end
|
127
|
-
|
128
|
-
o.on "-d", "--daemon", "Daemonize the server into the background" do
|
129
|
-
@options[:daemon] = true
|
130
|
-
@options[:quiet] = true
|
131
|
-
end
|
132
|
-
|
133
|
-
o.on "--debug", "Log lowlevel debugging information" do
|
134
|
-
@options[:debug] = true
|
135
|
-
end
|
136
|
-
|
137
|
-
o.on "--dir DIR", "Change to DIR before starting" do |d|
|
138
|
-
@options[:directory] = d.to_s
|
139
|
-
@options[:worker_directory] = d.to_s
|
140
|
-
end
|
141
|
-
|
142
|
-
o.on "-e", "--environment ENVIRONMENT",
|
143
|
-
"The environment to run the Rack app on (default development)" do |arg|
|
144
|
-
@options[:environment] = arg
|
145
|
-
end
|
146
|
-
|
147
|
-
o.on "-I", "--include PATH", "Specify $LOAD_PATH directories" do |arg|
|
148
|
-
$LOAD_PATH.unshift(*arg.split(':'))
|
149
|
-
end
|
150
42
|
|
151
|
-
|
152
|
-
|
153
|
-
@options[:binds] << "tcp://#{Configuration::DefaultTCPHost}:#{arg}"
|
154
|
-
end
|
155
|
-
|
156
|
-
o.on "--pidfile PATH", "Use PATH as a pidfile" do |arg|
|
157
|
-
@options[:pidfile] = arg
|
158
|
-
end
|
159
|
-
|
160
|
-
o.on "--preload", "Preload the app. Cluster mode only" do
|
161
|
-
@options[:preload_app] = true
|
162
|
-
end
|
163
|
-
|
164
|
-
o.on "-q", "--quiet", "Quiet down the output" do
|
165
|
-
@options[:quiet] = true
|
166
|
-
end
|
167
|
-
|
168
|
-
o.on "-R", "--restart-cmd CMD",
|
169
|
-
"The puma command to run during a hot restart",
|
170
|
-
"Default: inferred" do |cmd|
|
171
|
-
@options[:restart_cmd] = cmd
|
172
|
-
end
|
173
|
-
|
174
|
-
o.on "-S", "--state PATH", "Where to store the state details" do |arg|
|
175
|
-
@options[:state] = arg
|
176
|
-
end
|
43
|
+
begin
|
44
|
+
@parser.parse! @argv
|
177
45
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
@options[:min_threads] = min
|
182
|
-
@options[:max_threads] = max
|
183
|
-
else
|
184
|
-
@options[:min_threads] = 0
|
185
|
-
@options[:max_threads] = arg
|
46
|
+
if file = @argv.shift
|
47
|
+
@conf.configure do |c|
|
48
|
+
c.rackup file
|
186
49
|
end
|
187
50
|
end
|
188
|
-
|
189
|
-
o.on "--tcp-mode", "Run the app in raw TCP mode instead of HTTP mode" do
|
190
|
-
@options[:mode] = :tcp
|
191
|
-
end
|
192
|
-
|
193
|
-
o.on "-V", "--version", "Print the version information" do
|
194
|
-
puts "puma version #{Puma::Const::VERSION}"
|
195
|
-
exit 1
|
196
|
-
end
|
197
|
-
|
198
|
-
o.on "-w", "--workers COUNT",
|
199
|
-
"Activate cluster mode: How many worker processes to create" do |arg|
|
200
|
-
@options[:workers] = arg.to_i
|
201
|
-
end
|
202
|
-
|
203
|
-
end
|
204
|
-
|
205
|
-
@parser.banner = "puma <options> <rackup file>"
|
206
|
-
|
207
|
-
@parser.on_tail "-h", "--help", "Show help" do
|
208
|
-
log @parser
|
51
|
+
rescue UnsupportedOption
|
209
52
|
exit 1
|
210
53
|
end
|
211
|
-
end
|
212
|
-
|
213
|
-
def write_state
|
214
|
-
write_pid
|
215
|
-
|
216
|
-
require 'yaml'
|
217
|
-
|
218
|
-
if path = @options[:state]
|
219
|
-
state = { "pid" => Process.pid }
|
220
|
-
|
221
|
-
cfg = @config.dup
|
222
|
-
|
223
|
-
[ :logger, :worker_boot, :on_restart ].each { |o| cfg.options.delete o }
|
224
54
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
f.write state.to_yaml
|
55
|
+
@conf.configure do |c|
|
56
|
+
if @stdout || @stderr
|
57
|
+
c.stdout_redirect @stdout, @stderr, @append
|
229
58
|
end
|
230
|
-
end
|
231
|
-
end
|
232
59
|
|
233
|
-
|
234
|
-
|
235
|
-
#
|
236
|
-
def write_pid
|
237
|
-
if path = @options[:pidfile]
|
238
|
-
File.open(path, "w") do |f|
|
239
|
-
f.puts Process.pid
|
60
|
+
if @control_url
|
61
|
+
c.activate_control_app @control_url, @control_options
|
240
62
|
end
|
241
|
-
|
242
|
-
cur = Process.pid
|
243
|
-
|
244
|
-
at_exit do
|
245
|
-
if cur == Process.pid
|
246
|
-
delete_pidfile
|
247
|
-
end
|
248
|
-
end
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
|
-
def set_rack_environment
|
253
|
-
# Try the user option first, then the environment variable,
|
254
|
-
# finally default to development
|
255
|
-
|
256
|
-
env = @options[:environment] ||
|
257
|
-
ENV['RACK_ENV'] ||
|
258
|
-
'development'
|
259
|
-
|
260
|
-
@options[:environment] = env
|
261
|
-
ENV['RACK_ENV'] = env
|
262
|
-
end
|
263
|
-
|
264
|
-
def delete_pidfile
|
265
|
-
if path = @options[:pidfile]
|
266
|
-
File.unlink path if File.exists? path
|
267
|
-
end
|
268
|
-
end
|
269
|
-
|
270
|
-
# :nodoc:
|
271
|
-
def parse_options
|
272
|
-
@parser.parse! @argv
|
273
|
-
|
274
|
-
if @argv.last
|
275
|
-
@options[:rackup] = @argv.shift
|
276
|
-
end
|
277
|
-
|
278
|
-
@config = Puma::Configuration.new @options
|
279
|
-
|
280
|
-
# Advertise the Configuration
|
281
|
-
Puma.cli_config = @config
|
282
|
-
|
283
|
-
@config.load
|
284
|
-
|
285
|
-
if clustered?
|
286
|
-
unsupported "worker mode not supported on JRuby or Windows",
|
287
|
-
jruby? || windows?
|
288
|
-
end
|
289
|
-
|
290
|
-
if @options[:daemon] and windows?
|
291
|
-
unsupported "daemon mode not supported on Windows"
|
292
63
|
end
|
293
|
-
end
|
294
|
-
|
295
|
-
def clustered?
|
296
|
-
@options[:workers] > 0
|
297
|
-
end
|
298
64
|
|
299
|
-
|
300
|
-
@runner.stop_blocked
|
301
|
-
log "- Goodbye!"
|
65
|
+
@launcher = Puma::Launcher.new(@conf, :events => @events, :argv => argv)
|
302
66
|
end
|
303
67
|
|
304
|
-
|
305
|
-
# Use the same trick as unicorn, namely favor PWD because
|
306
|
-
# it will contain an unresolved symlink, useful for when
|
307
|
-
# the pwd is /data/releases/current.
|
308
|
-
if dir = ENV['PWD']
|
309
|
-
s_env = File.stat(dir)
|
310
|
-
s_pwd = File.stat(Dir.pwd)
|
311
|
-
|
312
|
-
if s_env.ino == s_pwd.ino and s_env.dev == s_pwd.dev
|
313
|
-
@restart_dir = dir
|
314
|
-
@options[:worker_directory] = dir
|
315
|
-
end
|
316
|
-
end
|
317
|
-
|
318
|
-
@restart_dir ||= Dir.pwd
|
319
|
-
|
320
|
-
@original_argv = ARGV.dup
|
321
|
-
|
322
|
-
if defined? Rubinius::OS_ARGV
|
323
|
-
@restart_argv = Rubinius::OS_ARGV
|
324
|
-
else
|
325
|
-
require 'rubygems'
|
326
|
-
|
327
|
-
# if $0 is a file in the current directory, then restart
|
328
|
-
# it the same, otherwise add -S on there because it was
|
329
|
-
# picked up in PATH.
|
330
|
-
#
|
331
|
-
if File.exists?($0)
|
332
|
-
arg0 = [Gem.ruby, $0]
|
333
|
-
else
|
334
|
-
arg0 = [Gem.ruby, "-S", $0]
|
335
|
-
end
|
336
|
-
|
337
|
-
# Detect and reinject -Ilib from the command line
|
338
|
-
lib = File.expand_path "lib"
|
339
|
-
arg0[1,0] = ["-I", lib] if $:[0] == lib
|
68
|
+
attr_reader :launcher
|
340
69
|
|
341
|
-
|
342
|
-
|
70
|
+
# Parse the options, load the rackup, start the server and wait
|
71
|
+
# for it to finish.
|
72
|
+
#
|
73
|
+
def run
|
74
|
+
@launcher.run
|
343
75
|
end
|
344
76
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
@restart_argv
|
350
|
-
end
|
77
|
+
private
|
78
|
+
def unsupported(str)
|
79
|
+
@events.error(str)
|
80
|
+
raise UnsupportedOption
|
351
81
|
end
|
352
82
|
|
353
|
-
|
354
|
-
|
355
|
-
JRubyRestart.daemon_start(@restart_dir, restart_args)
|
356
|
-
end
|
83
|
+
# Build the OptionParser object to handle the available options.
|
84
|
+
#
|
357
85
|
|
358
|
-
def
|
359
|
-
@
|
360
|
-
|
361
|
-
|
86
|
+
def setup_options
|
87
|
+
@conf = Configuration.new do |c|
|
88
|
+
@parser = OptionParser.new do |o|
|
89
|
+
o.on "-b", "--bind URI", "URI to bind to (tcp://, unix://, ssl://)" do |arg|
|
90
|
+
c.bind arg
|
91
|
+
end
|
362
92
|
|
363
|
-
|
364
|
-
|
365
|
-
|
93
|
+
o.on "-C", "--config PATH", "Load PATH as a config file" do |arg|
|
94
|
+
c.load arg
|
95
|
+
end
|
366
96
|
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
97
|
+
o.on "--control URL", "The bind url to use for the control server",
|
98
|
+
"Use 'auto' to use temp unix server" do |arg|
|
99
|
+
if arg
|
100
|
+
@control_url = arg
|
101
|
+
elsif Puma.jruby?
|
102
|
+
unsupported "No default url available on JRuby"
|
103
|
+
end
|
372
104
|
end
|
373
|
-
end
|
374
105
|
|
375
|
-
|
376
|
-
|
106
|
+
o.on "--control-token TOKEN",
|
107
|
+
"The token to use as authentication for the control server" do |arg|
|
108
|
+
@control_options[:auth_token] = arg
|
109
|
+
end
|
377
110
|
|
378
|
-
|
379
|
-
|
380
|
-
|
111
|
+
o.on "-d", "--daemon", "Daemonize the server into the background" do
|
112
|
+
c.daemonize
|
113
|
+
c.quiet
|
114
|
+
end
|
381
115
|
|
382
|
-
|
383
|
-
|
384
|
-
if uri.scheme == "unix"
|
385
|
-
path = "#{uri.host}#{uri.path}"
|
386
|
-
File.unlink path
|
116
|
+
o.on "--debug", "Log lowlevel debugging information" do
|
117
|
+
c.debug
|
387
118
|
end
|
388
|
-
end
|
389
119
|
|
390
|
-
|
120
|
+
o.on "--dir DIR", "Change to DIR before starting" do |d|
|
121
|
+
c.directory d
|
122
|
+
end
|
391
123
|
|
392
|
-
|
124
|
+
o.on "-e", "--environment ENVIRONMENT",
|
125
|
+
"The environment to run the Rack app on (default development)" do |arg|
|
126
|
+
c.environment arg
|
127
|
+
end
|
393
128
|
|
394
|
-
|
395
|
-
|
129
|
+
o.on "-I", "--include PATH", "Specify $LOAD_PATH directories" do |arg|
|
130
|
+
$LOAD_PATH.unshift(*arg.split(':'))
|
131
|
+
end
|
396
132
|
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
redirects[io.to_i] = io.to_i
|
402
|
-
end
|
133
|
+
o.on "-p", "--port PORT", "Define the TCP port to bind to",
|
134
|
+
"Use -b for more advanced options" do |arg|
|
135
|
+
c.bind "tcp://#{Configuration::DefaultTCPHost}:#{arg}"
|
136
|
+
end
|
403
137
|
|
404
|
-
|
138
|
+
o.on "--pidfile PATH", "Use PATH as a pidfile" do |arg|
|
139
|
+
c.pidfile arg
|
140
|
+
end
|
405
141
|
|
406
|
-
|
142
|
+
o.on "--preload", "Preload the app. Cluster mode only" do
|
143
|
+
c.preload_app!
|
144
|
+
end
|
407
145
|
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
end
|
146
|
+
o.on "--prune-bundler", "Prune out the bundler env if possible" do
|
147
|
+
c.prune_bundler
|
148
|
+
end
|
412
149
|
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
def run
|
417
|
-
begin
|
418
|
-
parse_options
|
419
|
-
rescue UnsupportedOption
|
420
|
-
exit 1
|
421
|
-
end
|
150
|
+
o.on "-q", "--quiet", "Do not log requests internally (default true)" do
|
151
|
+
c.quiet
|
152
|
+
end
|
422
153
|
|
423
|
-
|
424
|
-
|
425
|
-
|
154
|
+
o.on "-v", "--log-requests", "Log requests as they occur" do
|
155
|
+
c.log_requests
|
156
|
+
end
|
426
157
|
|
427
|
-
|
158
|
+
o.on "-R", "--restart-cmd CMD",
|
159
|
+
"The puma command to run during a hot restart",
|
160
|
+
"Default: inferred" do |cmd|
|
161
|
+
c.restart_command cmd
|
162
|
+
end
|
428
163
|
|
429
|
-
|
430
|
-
|
431
|
-
|
164
|
+
o.on "-S", "--state PATH", "Where to store the state details" do |arg|
|
165
|
+
c.state_path arg
|
166
|
+
end
|
432
167
|
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
168
|
+
o.on '-t', '--threads INT', "min:max threads to use (default 0:16)" do |arg|
|
169
|
+
min, max = arg.split(":")
|
170
|
+
if max
|
171
|
+
c.threads min, max
|
172
|
+
else
|
173
|
+
c.threads 0, max
|
174
|
+
end
|
175
|
+
end
|
437
176
|
|
438
|
-
|
177
|
+
o.on "--tcp-mode", "Run the app in raw TCP mode instead of HTTP mode" do
|
178
|
+
c.tcp_mode!
|
179
|
+
end
|
439
180
|
|
440
|
-
|
181
|
+
o.on "-V", "--version", "Print the version information" do
|
182
|
+
puts "puma version #{Puma::Const::VERSION}"
|
183
|
+
exit 0
|
184
|
+
end
|
441
185
|
|
442
|
-
|
186
|
+
o.on "-w", "--workers COUNT",
|
187
|
+
"Activate cluster mode: How many worker processes to create" do |arg|
|
188
|
+
c.workers arg
|
189
|
+
end
|
443
190
|
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
when :run, :stop
|
448
|
-
graceful_stop
|
449
|
-
when :restart
|
450
|
-
log "* Restarting..."
|
451
|
-
@runner.before_restart
|
452
|
-
restart!
|
453
|
-
when :exit
|
454
|
-
# nothing
|
455
|
-
end
|
191
|
+
o.on "--tag NAME", "Additional text to display in process listing" do |arg|
|
192
|
+
c.tag arg
|
193
|
+
end
|
456
194
|
|
457
|
-
|
458
|
-
|
459
|
-
|
195
|
+
o.on "--redirect-stdout FILE", "Redirect STDOUT to a specific file" do |arg|
|
196
|
+
@stdout = arg.to_s
|
197
|
+
end
|
460
198
|
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
restart
|
465
|
-
end
|
466
|
-
rescue Exception
|
467
|
-
log "*** SIGUSR2 not implemented, signal based restart unavailable!"
|
468
|
-
end
|
199
|
+
o.on "--redirect-stderr FILE", "Redirect STDERR to a specific file" do |arg|
|
200
|
+
@stderr = arg.to_s
|
201
|
+
end
|
469
202
|
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
end
|
474
|
-
rescue Exception
|
475
|
-
log "*** SIGUSR1 not implemented, signal based restart unavailable!"
|
476
|
-
end
|
203
|
+
o.on "--[no-]redirect-append", "Append to redirected files" do |val|
|
204
|
+
@append = val
|
205
|
+
end
|
477
206
|
|
478
|
-
|
479
|
-
Signal.trap "SIGTERM" do
|
480
|
-
stop
|
481
|
-
end
|
482
|
-
rescue Exception
|
483
|
-
log "*** SIGTERM not implemented, signal based gracefully stopping unavailable!"
|
484
|
-
end
|
207
|
+
o.banner = "puma <options> <rackup file>"
|
485
208
|
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
exit
|
209
|
+
o.on_tail "-h", "--help", "Show help" do
|
210
|
+
$stdout.puts o
|
211
|
+
exit 0
|
212
|
+
end
|
491
213
|
end
|
492
214
|
end
|
493
215
|
end
|
494
|
-
|
495
|
-
def stop
|
496
|
-
@status = :stop
|
497
|
-
@runner.stop
|
498
|
-
end
|
499
|
-
|
500
|
-
def restart
|
501
|
-
@status = :restart
|
502
|
-
@runner.restart
|
503
|
-
end
|
504
|
-
|
505
|
-
def phased_restart
|
506
|
-
unless @runner.respond_to?(:phased_restart) and @runner.phased_restart
|
507
|
-
log "* phased-restart called but not available, restarting normally."
|
508
|
-
return restart
|
509
|
-
end
|
510
|
-
true
|
511
|
-
end
|
512
|
-
|
513
|
-
def stats
|
514
|
-
@runner.stats
|
515
|
-
end
|
516
|
-
|
517
|
-
def halt
|
518
|
-
@status = :halt
|
519
|
-
@runner.halt
|
520
|
-
end
|
521
216
|
end
|
522
217
|
end
|