puma 5.2.2 → 6.3.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/History.md +483 -4
- data/README.md +101 -20
- data/bin/puma-wild +1 -1
- data/docs/architecture.md +50 -16
- data/docs/compile_options.md +38 -2
- data/docs/deployment.md +53 -67
- data/docs/fork_worker.md +1 -3
- data/docs/jungle/rc.d/README.md +1 -1
- data/docs/kubernetes.md +1 -1
- data/docs/nginx.md +1 -1
- data/docs/plugins.md +15 -15
- data/docs/rails_dev_mode.md +2 -3
- data/docs/restart.md +7 -7
- data/docs/signals.md +11 -10
- data/docs/stats.md +8 -8
- data/docs/systemd.md +65 -69
- data/docs/testing_benchmarks_local_files.md +150 -0
- data/docs/testing_test_rackup_ci_files.md +36 -0
- data/ext/puma_http11/extconf.rb +44 -13
- data/ext/puma_http11/http11_parser.c +24 -11
- data/ext/puma_http11/http11_parser.h +2 -2
- data/ext/puma_http11/http11_parser.java.rl +2 -2
- data/ext/puma_http11/http11_parser.rl +2 -2
- data/ext/puma_http11/http11_parser_common.rl +3 -3
- data/ext/puma_http11/mini_ssl.c +150 -23
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +50 -48
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +188 -102
- data/ext/puma_http11/puma_http11.c +18 -10
- data/lib/puma/app/status.rb +10 -7
- data/lib/puma/binder.rb +112 -62
- data/lib/puma/cli.rb +24 -20
- data/lib/puma/client.rb +162 -36
- data/lib/puma/cluster/worker.rb +31 -27
- data/lib/puma/cluster/worker_handle.rb +12 -1
- data/lib/puma/cluster.rb +102 -61
- data/lib/puma/commonlogger.rb +21 -14
- data/lib/puma/configuration.rb +78 -54
- data/lib/puma/const.rb +135 -97
- data/lib/puma/control_cli.rb +25 -20
- data/lib/puma/detect.rb +12 -2
- data/lib/puma/dsl.rb +308 -58
- data/lib/puma/error_logger.rb +20 -11
- data/lib/puma/events.rb +6 -126
- data/lib/puma/io_buffer.rb +39 -4
- data/lib/puma/jruby_restart.rb +2 -1
- data/lib/puma/{json.rb → json_serialization.rb} +1 -1
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +114 -173
- data/lib/puma/log_writer.rb +147 -0
- data/lib/puma/minissl/context_builder.rb +30 -16
- data/lib/puma/minissl.rb +132 -38
- data/lib/puma/null_io.rb +5 -0
- data/lib/puma/plugin/systemd.rb +90 -0
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/plugin.rb +2 -2
- data/lib/puma/rack/builder.rb +7 -7
- data/lib/puma/rack_default.rb +19 -4
- data/lib/puma/reactor.rb +19 -10
- data/lib/puma/request.rb +373 -153
- data/lib/puma/runner.rb +74 -28
- data/lib/puma/sd_notify.rb +149 -0
- data/lib/puma/server.rb +127 -136
- data/lib/puma/single.rb +13 -11
- data/lib/puma/state_file.rb +39 -7
- data/lib/puma/thread_pool.rb +33 -26
- data/lib/puma/util.rb +20 -15
- data/lib/puma.rb +28 -11
- data/lib/rack/handler/puma.rb +113 -86
- data/tools/Dockerfile +1 -1
- metadata +15 -10
- data/lib/puma/queue_close.rb +0 -26
- data/lib/puma/systemd.rb +0 -46
@@ -36,13 +36,13 @@ static VALUE global_request_method;
|
|
36
36
|
static VALUE global_request_uri;
|
37
37
|
static VALUE global_fragment;
|
38
38
|
static VALUE global_query_string;
|
39
|
-
static VALUE
|
39
|
+
static VALUE global_server_protocol;
|
40
40
|
static VALUE global_request_path;
|
41
41
|
|
42
42
|
/** Defines common length and error messages for input length validation. */
|
43
43
|
#define QUOTE(s) #s
|
44
|
-
#define
|
45
|
-
#define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP element " # N " is longer than the "
|
44
|
+
#define EXPAND_MAX_LENGTH_VALUE(s) QUOTE(s)
|
45
|
+
#define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP element " # N " is longer than the " EXPAND_MAX_LENGTH_VALUE(length) " allowed length (was %d)"
|
46
46
|
|
47
47
|
/** Validates the max length of given input and throws an HttpParserError exception if over. */
|
48
48
|
#define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR, len); }
|
@@ -52,15 +52,23 @@ static VALUE global_request_path;
|
|
52
52
|
|
53
53
|
|
54
54
|
/* Defines the maximum allowed lengths for various input elements.*/
|
55
|
+
#ifndef PUMA_REQUEST_URI_MAX_LENGTH
|
56
|
+
#define PUMA_REQUEST_URI_MAX_LENGTH (1024 * 12)
|
57
|
+
#endif
|
58
|
+
|
59
|
+
#ifndef PUMA_REQUEST_PATH_MAX_LENGTH
|
60
|
+
#define PUMA_REQUEST_PATH_MAX_LENGTH (8192)
|
61
|
+
#endif
|
62
|
+
|
55
63
|
#ifndef PUMA_QUERY_STRING_MAX_LENGTH
|
56
64
|
#define PUMA_QUERY_STRING_MAX_LENGTH (1024 * 10)
|
57
65
|
#endif
|
58
66
|
|
59
67
|
DEF_MAX_LENGTH(FIELD_NAME, 256);
|
60
68
|
DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
|
61
|
-
DEF_MAX_LENGTH(REQUEST_URI,
|
69
|
+
DEF_MAX_LENGTH(REQUEST_URI, PUMA_REQUEST_URI_MAX_LENGTH);
|
62
70
|
DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
|
63
|
-
DEF_MAX_LENGTH(REQUEST_PATH,
|
71
|
+
DEF_MAX_LENGTH(REQUEST_PATH, PUMA_REQUEST_PATH_MAX_LENGTH);
|
64
72
|
DEF_MAX_LENGTH(QUERY_STRING, PUMA_QUERY_STRING_MAX_LENGTH);
|
65
73
|
DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
|
66
74
|
|
@@ -236,10 +244,10 @@ void query_string(puma_parser* hp, const char *at, size_t length)
|
|
236
244
|
rb_hash_aset(hp->request, global_query_string, val);
|
237
245
|
}
|
238
246
|
|
239
|
-
void
|
247
|
+
void server_protocol(puma_parser* hp, const char *at, size_t length)
|
240
248
|
{
|
241
249
|
VALUE val = rb_str_new(at, length);
|
242
|
-
rb_hash_aset(hp->request,
|
250
|
+
rb_hash_aset(hp->request, global_server_protocol, val);
|
243
251
|
}
|
244
252
|
|
245
253
|
/** Finalizes the request header to have a bunch of stuff that's
|
@@ -281,7 +289,7 @@ VALUE HttpParser_alloc(VALUE klass)
|
|
281
289
|
hp->fragment = fragment;
|
282
290
|
hp->request_path = request_path;
|
283
291
|
hp->query_string = query_string;
|
284
|
-
hp->
|
292
|
+
hp->server_protocol = server_protocol;
|
285
293
|
hp->header_done = header_done;
|
286
294
|
hp->request = Qnil;
|
287
295
|
|
@@ -451,7 +459,7 @@ VALUE HttpParser_body(VALUE self) {
|
|
451
459
|
void Init_mini_ssl(VALUE mod);
|
452
460
|
#endif
|
453
461
|
|
454
|
-
void Init_puma_http11()
|
462
|
+
void Init_puma_http11(void)
|
455
463
|
{
|
456
464
|
|
457
465
|
VALUE mPuma = rb_define_module("Puma");
|
@@ -461,7 +469,7 @@ void Init_puma_http11()
|
|
461
469
|
DEF_GLOBAL(request_uri, "REQUEST_URI");
|
462
470
|
DEF_GLOBAL(fragment, "FRAGMENT");
|
463
471
|
DEF_GLOBAL(query_string, "QUERY_STRING");
|
464
|
-
DEF_GLOBAL(
|
472
|
+
DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
|
465
473
|
DEF_GLOBAL(request_path, "REQUEST_PATH");
|
466
474
|
|
467
475
|
eHttpParserError = rb_define_class_under(mPuma, "HttpParserError", rb_eIOError);
|
data/lib/puma/app/status.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
require_relative '../json_serialization'
|
3
3
|
|
4
4
|
module Puma
|
5
5
|
module App
|
@@ -39,6 +39,9 @@ module Puma
|
|
39
39
|
when 'phased-restart'
|
40
40
|
@launcher.phased_restart ? 200 : 404
|
41
41
|
|
42
|
+
when 'refork'
|
43
|
+
@launcher.refork ? 200 : 404
|
44
|
+
|
42
45
|
when 'reload-worker-directory'
|
43
46
|
@launcher.send(:reload_worker_directory) ? 200 : 404
|
44
47
|
|
@@ -46,17 +49,17 @@ module Puma
|
|
46
49
|
GC.start ; 200
|
47
50
|
|
48
51
|
when 'gc-stats'
|
49
|
-
Puma::
|
52
|
+
Puma::JSONSerialization.generate GC.stat
|
50
53
|
|
51
54
|
when 'stats'
|
52
|
-
Puma::
|
55
|
+
Puma::JSONSerialization.generate @launcher.stats
|
53
56
|
|
54
57
|
when 'thread-backtraces'
|
55
58
|
backtraces = []
|
56
59
|
@launcher.thread_status do |name, backtrace|
|
57
60
|
backtraces << { name: name, backtrace: backtrace }
|
58
61
|
end
|
59
|
-
Puma::
|
62
|
+
Puma::JSONSerialization.generate backtraces
|
60
63
|
|
61
64
|
else
|
62
65
|
return rack_response(404, "Unsupported action", 'text/plain')
|
@@ -77,13 +80,13 @@ module Puma
|
|
77
80
|
|
78
81
|
def authenticate(env)
|
79
82
|
return true unless @auth_token
|
80
|
-
env['QUERY_STRING'].to_s.split(
|
83
|
+
env['QUERY_STRING'].to_s.split('&;').include? "token=#{@auth_token}"
|
81
84
|
end
|
82
85
|
|
83
86
|
def rack_response(status, body, content_type='application/json')
|
84
87
|
headers = {
|
85
|
-
'
|
86
|
-
'
|
88
|
+
'content-type' => content_type,
|
89
|
+
'content-length' => body.bytesize.to_s
|
87
90
|
}
|
88
91
|
|
89
92
|
[status, headers, [body]]
|
data/lib/puma/binder.rb
CHANGED
@@ -3,24 +3,15 @@
|
|
3
3
|
require 'uri'
|
4
4
|
require 'socket'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
require_relative 'const'
|
7
|
+
require_relative 'util'
|
8
|
+
require_relative 'configuration'
|
9
9
|
|
10
10
|
module Puma
|
11
11
|
|
12
12
|
if HAS_SSL
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
# Odd bug in 'pure Ruby' nio4r verion 2.5.2, which installs with Ruby 2.3.
|
17
|
-
# NIO doesn't create any OpenSSL objects, but it rescues an OpenSSL error.
|
18
|
-
# The bug was that it did not require openssl.
|
19
|
-
# @todo remove when Ruby 2.3 support is dropped
|
20
|
-
#
|
21
|
-
if windows? && RbConfig::CONFIG['ruby_version'] == '2.3.0'
|
22
|
-
require 'openssl'
|
23
|
-
end
|
13
|
+
require_relative 'minissl'
|
14
|
+
require_relative 'minissl/context_builder'
|
24
15
|
end
|
25
16
|
|
26
17
|
class Binder
|
@@ -28,8 +19,9 @@ module Puma
|
|
28
19
|
|
29
20
|
RACK_VERSION = [1,6].freeze
|
30
21
|
|
31
|
-
def initialize(
|
32
|
-
@
|
22
|
+
def initialize(log_writer, conf = Configuration.new)
|
23
|
+
@log_writer = log_writer
|
24
|
+
@conf = conf
|
33
25
|
@listeners = []
|
34
26
|
@inherited_fds = {}
|
35
27
|
@activated_sockets = {}
|
@@ -37,10 +29,11 @@ module Puma
|
|
37
29
|
|
38
30
|
@proto_env = {
|
39
31
|
"rack.version".freeze => RACK_VERSION,
|
40
|
-
"rack.errors".freeze =>
|
32
|
+
"rack.errors".freeze => log_writer.stderr,
|
41
33
|
"rack.multithread".freeze => conf.options[:max_threads] > 1,
|
42
34
|
"rack.multiprocess".freeze => conf.options[:workers] >= 1,
|
43
35
|
"rack.run_once".freeze => false,
|
36
|
+
RACK_URL_SCHEME => conf.options[:rack_url_scheme],
|
44
37
|
"SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
|
45
38
|
|
46
39
|
# I'd like to set a default CONTENT_TYPE here but some things
|
@@ -49,7 +42,6 @@ module Puma
|
|
49
42
|
# infer properly.
|
50
43
|
|
51
44
|
"QUERY_STRING".freeze => "",
|
52
|
-
SERVER_PROTOCOL => HTTP_11,
|
53
45
|
SERVER_SOFTWARE => PUMA_SERVER_STRING,
|
54
46
|
GATEWAY_INTERFACE => CGI_VER
|
55
47
|
}
|
@@ -77,7 +69,7 @@ module Puma
|
|
77
69
|
# @!attribute [r] connected_ports
|
78
70
|
# @version 5.0.0
|
79
71
|
def connected_ports
|
80
|
-
ios.map { |io| io.addr[1] }.uniq
|
72
|
+
t = ios.map { |io| io.addr[1] }; t.uniq!; t
|
81
73
|
end
|
82
74
|
|
83
75
|
# @version 5.0.0
|
@@ -95,6 +87,7 @@ module Puma
|
|
95
87
|
# @version 5.0.0
|
96
88
|
#
|
97
89
|
def create_activated_fds(env_hash)
|
90
|
+
@log_writer.debug "ENV['LISTEN_FDS'] #{ENV['LISTEN_FDS'].inspect} env_hash['LISTEN_PID'] #{env_hash['LISTEN_PID'].inspect}"
|
98
91
|
return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
|
99
92
|
env_hash['LISTEN_FDS'].to_i.times do |index|
|
100
93
|
sock = TCPServer.for_fd(socket_activation_fd(index))
|
@@ -102,11 +95,11 @@ module Puma
|
|
102
95
|
[:unix, Socket.unpack_sockaddr_un(sock.getsockname)]
|
103
96
|
rescue ArgumentError # Try to parse as a port/ip
|
104
97
|
port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
|
105
|
-
addr = "[#{addr}]" if addr
|
98
|
+
addr = "[#{addr}]" if addr&.include? ':'
|
106
99
|
[:tcp, addr, port]
|
107
100
|
end
|
108
101
|
@activated_sockets[key] = sock
|
109
|
-
@
|
102
|
+
@log_writer.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
|
110
103
|
end
|
111
104
|
["LISTEN_FDS", "LISTEN_PID"] # Signal to remove these keys from ENV
|
112
105
|
end
|
@@ -148,42 +141,52 @@ module Puma
|
|
148
141
|
end
|
149
142
|
end
|
150
143
|
|
151
|
-
def parse(binds,
|
144
|
+
def parse(binds, log_writer = nil, log_msg = 'Listening')
|
145
|
+
log_writer ||= @log_writer
|
152
146
|
binds.each do |str|
|
153
147
|
uri = URI.parse str
|
154
148
|
case uri.scheme
|
155
149
|
when "tcp"
|
156
150
|
if fd = @inherited_fds.delete(str)
|
157
151
|
io = inherit_tcp_listener uri.host, uri.port, fd
|
158
|
-
|
152
|
+
log_writer.log "* Inherited #{str}"
|
159
153
|
elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
|
160
154
|
io = inherit_tcp_listener uri.host, uri.port, sock
|
161
|
-
|
155
|
+
log_writer.log "* Activated #{str}"
|
162
156
|
else
|
163
157
|
ios_len = @ios.length
|
164
158
|
params = Util.parse_query uri.query
|
165
159
|
|
166
|
-
|
167
|
-
|
160
|
+
low_latency = params.key?('low_latency') && params['low_latency'] != 'false'
|
161
|
+
backlog = params.fetch('backlog', 1024).to_i
|
168
162
|
|
169
|
-
io = add_tcp_listener uri.host, uri.port,
|
163
|
+
io = add_tcp_listener uri.host, uri.port, low_latency, backlog
|
170
164
|
|
171
165
|
@ios[ios_len..-1].each do |i|
|
172
166
|
addr = loc_addr_str i
|
173
|
-
|
167
|
+
log_writer.log "* #{log_msg} on http://#{addr}"
|
174
168
|
end
|
175
169
|
end
|
176
170
|
|
177
171
|
@listeners << [str, io] if io
|
178
172
|
when "unix"
|
179
173
|
path = "#{uri.host}#{uri.path}".gsub("%20", " ")
|
174
|
+
abstract = false
|
175
|
+
if str.start_with? 'unix://@'
|
176
|
+
raise "OS does not support abstract UNIXSockets" unless Puma.abstract_unix_socket?
|
177
|
+
abstract = true
|
178
|
+
path = "@#{path}"
|
179
|
+
end
|
180
180
|
|
181
181
|
if fd = @inherited_fds.delete(str)
|
182
|
+
@unix_paths << path unless abstract || File.exist?(path)
|
182
183
|
io = inherit_unix_listener path, fd
|
183
|
-
|
184
|
-
elsif sock = @activated_sockets.delete([ :unix, path ])
|
184
|
+
log_writer.log "* Inherited #{str}"
|
185
|
+
elsif sock = @activated_sockets.delete([ :unix, path ]) ||
|
186
|
+
@activated_sockets.delete([ :unix, File.realdirpath(path) ])
|
187
|
+
@unix_paths << path unless abstract || File.exist?(path)
|
185
188
|
io = inherit_unix_listener path, sock
|
186
|
-
|
189
|
+
log_writer.log "* Activated #{str}"
|
187
190
|
else
|
188
191
|
umask = nil
|
189
192
|
mode = nil
|
@@ -205,44 +208,67 @@ module Puma
|
|
205
208
|
end
|
206
209
|
end
|
207
210
|
|
211
|
+
@unix_paths << path unless abstract || File.exist?(path)
|
208
212
|
io = add_unix_listener path, umask, mode, backlog
|
209
|
-
|
213
|
+
log_writer.log "* #{log_msg} on #{str}"
|
210
214
|
end
|
211
215
|
|
212
216
|
@listeners << [str, io]
|
213
217
|
when "ssl"
|
218
|
+
cert_key = %w[cert key]
|
214
219
|
|
215
220
|
raise "Puma compiled without SSL support" unless HAS_SSL
|
216
221
|
|
217
222
|
params = Util.parse_query uri.query
|
218
|
-
|
223
|
+
|
224
|
+
# If key and certs are not defined and localhost gem is required.
|
225
|
+
# localhost gem will be used for self signed
|
226
|
+
# Load localhost authority if not loaded.
|
227
|
+
# Ruby 3 `values_at` accepts an array, earlier do not
|
228
|
+
if params.values_at(*cert_key).all? { |v| v.to_s.empty? }
|
229
|
+
ctx = localhost_authority && localhost_authority_context
|
230
|
+
end
|
231
|
+
|
232
|
+
ctx ||=
|
233
|
+
begin
|
234
|
+
# Extract cert_pem and key_pem from options[:store] if present
|
235
|
+
cert_key.each do |v|
|
236
|
+
if params[v]&.start_with?('store:')
|
237
|
+
index = Integer(params.delete(v).split('store:').last)
|
238
|
+
params["#{v}_pem"] = @conf.options[:store][index]
|
239
|
+
end
|
240
|
+
end
|
241
|
+
MiniSSL::ContextBuilder.new(params, @log_writer).context
|
242
|
+
end
|
219
243
|
|
220
244
|
if fd = @inherited_fds.delete(str)
|
221
|
-
|
245
|
+
log_writer.log "* Inherited #{str}"
|
222
246
|
io = inherit_ssl_listener fd, ctx
|
223
247
|
elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
|
224
248
|
io = inherit_ssl_listener sock, ctx
|
225
|
-
|
249
|
+
log_writer.log "* Activated #{str}"
|
226
250
|
else
|
227
251
|
ios_len = @ios.length
|
228
|
-
|
252
|
+
backlog = params.fetch('backlog', 1024).to_i
|
253
|
+
low_latency = params['low_latency'] != 'false'
|
254
|
+
io = add_ssl_listener uri.host, uri.port, ctx, low_latency, backlog
|
229
255
|
|
230
256
|
@ios[ios_len..-1].each do |i|
|
231
257
|
addr = loc_addr_str i
|
232
|
-
|
258
|
+
log_writer.log "* #{log_msg} on ssl://#{addr}?#{uri.query}"
|
233
259
|
end
|
234
260
|
end
|
235
261
|
|
236
262
|
@listeners << [str, io] if io
|
237
263
|
else
|
238
|
-
|
264
|
+
log_writer.error "Invalid URI: #{str}"
|
239
265
|
end
|
240
266
|
end
|
241
267
|
|
242
268
|
# If we inherited fds but didn't use them (because of a
|
243
269
|
# configuration change), then be sure to close them.
|
244
270
|
@inherited_fds.each do |str, fd|
|
245
|
-
|
271
|
+
log_writer.log "* Closing unused inherited connection: #{str}"
|
246
272
|
|
247
273
|
begin
|
248
274
|
IO.for_fd(fd).close
|
@@ -258,17 +284,37 @@ module Puma
|
|
258
284
|
end
|
259
285
|
|
260
286
|
# Also close any unused activated sockets
|
261
|
-
@activated_sockets.
|
262
|
-
|
263
|
-
|
264
|
-
sock.
|
265
|
-
|
287
|
+
unless @activated_sockets.empty?
|
288
|
+
fds = @ios.map(&:to_i)
|
289
|
+
@activated_sockets.each do |key, sock|
|
290
|
+
next if fds.include? sock.to_i
|
291
|
+
log_writer.log "* Closing unused activated socket: #{key.first}://#{key[1..-1].join ':'}"
|
292
|
+
begin
|
293
|
+
sock.close
|
294
|
+
rescue SystemCallError
|
295
|
+
end
|
296
|
+
# We have to unlink a unix socket path that's not being used
|
297
|
+
File.unlink key[1] if key.first == :unix
|
266
298
|
end
|
267
|
-
# We have to unlink a unix socket path that's not being used
|
268
|
-
File.unlink key[1] if key[0] == :unix
|
269
299
|
end
|
270
300
|
end
|
271
301
|
|
302
|
+
def localhost_authority
|
303
|
+
@localhost_authority ||= Localhost::Authority.fetch if defined?(Localhost::Authority) && !Puma::IS_JRUBY
|
304
|
+
end
|
305
|
+
|
306
|
+
def localhost_authority_context
|
307
|
+
return unless localhost_authority
|
308
|
+
|
309
|
+
key_path, crt_path = if [:key_path, :certificate_path].all? { |m| localhost_authority.respond_to?(m) }
|
310
|
+
[localhost_authority.key_path, localhost_authority.certificate_path]
|
311
|
+
else
|
312
|
+
local_certificates_path = File.expand_path("~/.localhost")
|
313
|
+
[File.join(local_certificates_path, "localhost.key"), File.join(local_certificates_path, "localhost.crt")]
|
314
|
+
end
|
315
|
+
MiniSSL::ContextBuilder.new({ "key" => key_path, "cert" => crt_path }, @log_writer).context
|
316
|
+
end
|
317
|
+
|
272
318
|
# Tell the server to listen on host +host+, port +port+.
|
273
319
|
# If +optimize_for_latency+ is true (the default) then clients connecting
|
274
320
|
# will be optimized for latency over throughput.
|
@@ -286,6 +332,7 @@ module Puma
|
|
286
332
|
|
287
333
|
host = host[1..-2] if host and host[0..0] == '['
|
288
334
|
tcp_server = TCPServer.new(host, port)
|
335
|
+
|
289
336
|
if optimize_for_latency
|
290
337
|
tcp_server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
291
338
|
end
|
@@ -307,6 +354,8 @@ module Puma
|
|
307
354
|
optimize_for_latency=true, backlog=1024)
|
308
355
|
|
309
356
|
raise "Puma compiled without SSL support" unless HAS_SSL
|
357
|
+
# Puma will try to use local authority context if context is supplied nil
|
358
|
+
ctx ||= localhost_authority_context
|
310
359
|
|
311
360
|
if host == "localhost"
|
312
361
|
loopback_addresses.each do |addr|
|
@@ -334,6 +383,8 @@ module Puma
|
|
334
383
|
|
335
384
|
def inherit_ssl_listener(fd, ctx)
|
336
385
|
raise "Puma compiled without SSL support" unless HAS_SSL
|
386
|
+
# Puma will try to use local authority context if context is supplied nil
|
387
|
+
ctx ||= localhost_authority_context
|
337
388
|
|
338
389
|
s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
|
339
390
|
|
@@ -351,8 +402,6 @@ module Puma
|
|
351
402
|
# Tell the server to listen on +path+ as a UNIX domain socket.
|
352
403
|
#
|
353
404
|
def add_unix_listener(path, umask=nil, mode=nil, backlog=1024)
|
354
|
-
@unix_paths << path unless File.exist? path
|
355
|
-
|
356
405
|
# Let anyone connect by default
|
357
406
|
umask ||= 0
|
358
407
|
|
@@ -369,8 +418,7 @@ module Puma
|
|
369
418
|
raise "There is already a server bound to: #{path}"
|
370
419
|
end
|
371
420
|
end
|
372
|
-
|
373
|
-
s = UNIXServer.new(path)
|
421
|
+
s = UNIXServer.new path.sub(/\A@/, "\0") # check for abstract UNIXSocket
|
374
422
|
s.listen backlog
|
375
423
|
@ios << s
|
376
424
|
ensure
|
@@ -389,8 +437,6 @@ module Puma
|
|
389
437
|
end
|
390
438
|
|
391
439
|
def inherit_unix_listener(path, fd)
|
392
|
-
@unix_paths << path unless File.exist? path
|
393
|
-
|
394
440
|
s = fd.kind_of?(::TCPServer) ? fd : ::UNIXServer.for_fd(fd)
|
395
441
|
|
396
442
|
@ios << s
|
@@ -403,24 +449,27 @@ module Puma
|
|
403
449
|
end
|
404
450
|
|
405
451
|
def close_listeners
|
406
|
-
listeners.each do |l, io|
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
452
|
+
@listeners.each do |l, io|
|
453
|
+
begin
|
454
|
+
io.close unless io.closed?
|
455
|
+
uri = URI.parse l
|
456
|
+
next unless uri.scheme == 'unix'
|
457
|
+
unix_path = "#{uri.host}#{uri.path}"
|
458
|
+
File.unlink unix_path if @unix_paths.include?(unix_path) && File.exist?(unix_path)
|
459
|
+
rescue Errno::EBADF
|
460
|
+
end
|
412
461
|
end
|
413
462
|
end
|
414
463
|
|
415
464
|
def redirects_for_restart
|
416
|
-
redirects = listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h
|
465
|
+
redirects = @listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h
|
417
466
|
redirects[:close_others] = true
|
418
467
|
redirects
|
419
468
|
end
|
420
469
|
|
421
470
|
# @version 5.0.0
|
422
471
|
def redirects_for_restart_env
|
423
|
-
listeners.each_with_object({}).with_index do |(listen, memo), i|
|
472
|
+
@listeners.each_with_object({}).with_index do |(listen, memo), i|
|
424
473
|
memo["PUMA_INHERIT_#{i}"] = "#{listen[1].to_i}:#{listen[0]}"
|
425
474
|
end
|
426
475
|
end
|
@@ -429,9 +478,10 @@ module Puma
|
|
429
478
|
|
430
479
|
# @!attribute [r] loopback_addresses
|
431
480
|
def loopback_addresses
|
432
|
-
Socket.ip_address_list.select do |addrinfo|
|
481
|
+
t = Socket.ip_address_list.select do |addrinfo|
|
433
482
|
addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
|
434
|
-
end
|
483
|
+
end
|
484
|
+
t.map! { |addrinfo| addrinfo.ip_address }; t.uniq!; t
|
435
485
|
end
|
436
486
|
|
437
487
|
def loc_addr_str(io)
|
data/lib/puma/cli.rb
CHANGED
@@ -3,36 +3,31 @@
|
|
3
3
|
require 'optparse'
|
4
4
|
require 'uri'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
require_relative '../puma'
|
7
|
+
require_relative 'configuration'
|
8
|
+
require_relative 'launcher'
|
9
|
+
require_relative 'const'
|
10
|
+
require_relative 'log_writer'
|
11
11
|
|
12
12
|
module Puma
|
13
13
|
class << self
|
14
|
-
# The CLI exports
|
15
|
-
# apps to pick it up. An app
|
16
|
-
#
|
17
|
-
#
|
14
|
+
# The CLI exports a Puma::Configuration instance here to allow
|
15
|
+
# apps to pick it up. An app must load this object conditionally
|
16
|
+
# because it is not set if the app is launched via any mechanism
|
17
|
+
# other than the CLI class.
|
18
18
|
attr_accessor :cli_config
|
19
19
|
end
|
20
20
|
|
21
21
|
# Handles invoke a Puma::Server in a command line style.
|
22
22
|
#
|
23
23
|
class CLI
|
24
|
-
KEYS_NOT_TO_PERSIST_IN_STATE = Launcher::KEYS_NOT_TO_PERSIST_IN_STATE
|
25
|
-
|
26
24
|
# Create a new CLI object using +argv+ as the command line
|
27
25
|
# arguments.
|
28
26
|
#
|
29
|
-
|
30
|
-
# this object will report status on.
|
31
|
-
#
|
32
|
-
def initialize(argv, events=Events.stdio)
|
27
|
+
def initialize(argv, log_writer = LogWriter.stdio, events = Events.new)
|
33
28
|
@debug = false
|
34
29
|
@argv = argv.dup
|
35
|
-
|
30
|
+
@log_writer = log_writer
|
36
31
|
@events = events
|
37
32
|
|
38
33
|
@conf = nil
|
@@ -68,7 +63,7 @@ module Puma
|
|
68
63
|
end
|
69
64
|
end
|
70
65
|
|
71
|
-
@launcher = Puma::Launcher.new(@conf, :events => @events, :argv => argv)
|
66
|
+
@launcher = Puma::Launcher.new(@conf, :log_writer => @log_writer, :events => @events, :argv => argv)
|
72
67
|
end
|
73
68
|
|
74
69
|
attr_reader :launcher
|
@@ -82,7 +77,7 @@ module Puma
|
|
82
77
|
|
83
78
|
private
|
84
79
|
def unsupported(str)
|
85
|
-
@
|
80
|
+
@log_writer.error(str)
|
86
81
|
raise UnsupportedOption
|
87
82
|
end
|
88
83
|
|
@@ -98,7 +93,7 @@ module Puma
|
|
98
93
|
#
|
99
94
|
|
100
95
|
def setup_options
|
101
|
-
@conf = Configuration.new do |user_config, file_config|
|
96
|
+
@conf = Configuration.new({}, {events: @events}) do |user_config, file_config|
|
102
97
|
@parser = OptionParser.new do |o|
|
103
98
|
o.on "-b", "--bind URI", "URI to bind to (tcp://, unix://, ssl://)" do |arg|
|
104
99
|
user_config.bind arg
|
@@ -112,6 +107,11 @@ module Puma
|
|
112
107
|
file_config.load arg
|
113
108
|
end
|
114
109
|
|
110
|
+
# Identical to supplying --config "-", but more semantic
|
111
|
+
o.on "--no-config", "Prevent Puma from searching for a config file" do |arg|
|
112
|
+
file_config.load "-"
|
113
|
+
end
|
114
|
+
|
115
115
|
o.on "--control-url URL", "The bind url to use for the control server. Use 'auto' to use temp unix server" do |arg|
|
116
116
|
configure_control_url(arg)
|
117
117
|
end
|
@@ -146,7 +146,7 @@ module Puma
|
|
146
146
|
|
147
147
|
o.on "-p", "--port PORT", "Define the TCP port to bind to",
|
148
148
|
"Use -b for more advanced options" do |arg|
|
149
|
-
user_config.bind "tcp://#{Configuration::
|
149
|
+
user_config.bind "tcp://#{Configuration::DEFAULTS[:tcp_host]}:#{arg}"
|
150
150
|
end
|
151
151
|
|
152
152
|
o.on "--pidfile PATH", "Use PATH as a pidfile" do |arg|
|
@@ -179,6 +179,10 @@ module Puma
|
|
179
179
|
user_config.restart_command cmd
|
180
180
|
end
|
181
181
|
|
182
|
+
o.on "-s", "--silent", "Do not log prompt messages other than errors" do
|
183
|
+
@log_writer = LogWriter.new(NullIO.new, $stderr)
|
184
|
+
end
|
185
|
+
|
182
186
|
o.on "-S", "--state PATH", "Where to store the state details" do |arg|
|
183
187
|
user_config.state_path arg
|
184
188
|
end
|