puma 5.0.4 → 5.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/History.md +322 -48
- data/LICENSE +0 -0
- data/README.md +95 -24
- data/bin/puma-wild +0 -0
- data/docs/architecture.md +57 -20
- data/docs/compile_options.md +21 -0
- data/docs/deployment.md +53 -67
- data/docs/fork_worker.md +2 -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 +0 -0
- data/docs/jungle/rc.d/README.md +1 -1
- data/docs/jungle/rc.d/puma.conf +0 -0
- data/docs/kubernetes.md +66 -0
- data/docs/nginx.md +0 -0
- data/docs/plugins.md +15 -15
- data/docs/rails_dev_mode.md +28 -0
- data/docs/restart.md +7 -7
- data/docs/signals.md +11 -10
- data/docs/stats.md +142 -0
- data/docs/systemd.md +85 -66
- data/ext/puma_http11/PumaHttp11Service.java +0 -0
- data/ext/puma_http11/ext_help.h +0 -0
- data/ext/puma_http11/extconf.rb +42 -6
- data/ext/puma_http11/http11_parser.c +68 -57
- data/ext/puma_http11/http11_parser.h +1 -1
- data/ext/puma_http11/http11_parser.java.rl +1 -1
- data/ext/puma_http11/http11_parser.rl +1 -1
- data/ext/puma_http11/http11_parser_common.rl +1 -1
- data/ext/puma_http11/mini_ssl.c +226 -88
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +0 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +0 -0
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +51 -51
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +28 -43
- data/ext/puma_http11/puma_http11.c +9 -3
- data/lib/puma/app/status.rb +4 -7
- data/lib/puma/binder.rb +138 -49
- data/lib/puma/cli.rb +18 -4
- data/lib/puma/client.rb +113 -31
- data/lib/puma/cluster/worker.rb +22 -19
- data/lib/puma/cluster/worker_handle.rb +13 -2
- data/lib/puma/cluster.rb +75 -33
- data/lib/puma/commonlogger.rb +0 -0
- data/lib/puma/configuration.rb +21 -2
- data/lib/puma/const.rb +17 -8
- data/lib/puma/control_cli.rb +76 -71
- data/lib/puma/detect.rb +19 -9
- data/lib/puma/dsl.rb +225 -31
- data/lib/puma/error_logger.rb +12 -5
- data/lib/puma/events.rb +18 -3
- data/lib/puma/io_buffer.rb +0 -0
- data/lib/puma/jruby_restart.rb +0 -0
- data/lib/puma/json_serialization.rb +96 -0
- data/lib/puma/launcher.rb +56 -7
- data/lib/puma/minissl/context_builder.rb +14 -6
- data/lib/puma/minissl.rb +72 -40
- data/lib/puma/null_io.rb +12 -0
- data/lib/puma/plugin/tmp_restart.rb +0 -0
- data/lib/puma/plugin.rb +2 -2
- data/lib/puma/queue_close.rb +7 -7
- data/lib/puma/rack/builder.rb +1 -1
- data/lib/puma/rack/urlmap.rb +0 -0
- data/lib/puma/rack_default.rb +0 -0
- data/lib/puma/reactor.rb +19 -12
- data/lib/puma/request.rb +55 -21
- data/lib/puma/runner.rb +39 -13
- data/lib/puma/server.rb +78 -142
- data/lib/puma/single.rb +0 -0
- data/lib/puma/state_file.rb +45 -9
- data/lib/puma/systemd.rb +46 -0
- data/lib/puma/thread_pool.rb +11 -8
- data/lib/puma/util.rb +8 -1
- data/lib/puma.rb +36 -10
- data/lib/rack/handler/puma.rb +1 -0
- data/tools/Dockerfile +1 -1
- data/tools/trickletest.rb +0 -0
- metadata +15 -9
@@ -6,6 +6,7 @@ import org.jruby.RubyModule;
|
|
6
6
|
import org.jruby.RubyObject;
|
7
7
|
import org.jruby.RubyString;
|
8
8
|
import org.jruby.anno.JRubyMethod;
|
9
|
+
import org.jruby.exceptions.RaiseException;
|
9
10
|
import org.jruby.javasupport.JavaEmbedUtils;
|
10
11
|
import org.jruby.runtime.Block;
|
11
12
|
import org.jruby.runtime.ObjectAllocator;
|
@@ -80,11 +81,11 @@ public class MiniSSL extends RubyObject {
|
|
80
81
|
/**
|
81
82
|
* Writes bytes to the buffer after ensuring there's room
|
82
83
|
*/
|
83
|
-
|
84
|
-
if (buffer.remaining() <
|
85
|
-
resize(buffer.limit() +
|
84
|
+
private void put(byte[] bytes, final int offset, final int length) {
|
85
|
+
if (buffer.remaining() < length) {
|
86
|
+
resize(buffer.limit() + length);
|
86
87
|
}
|
87
|
-
buffer.put(bytes);
|
88
|
+
buffer.put(bytes, offset, length);
|
88
89
|
}
|
89
90
|
|
90
91
|
/**
|
@@ -115,7 +116,7 @@ public class MiniSSL extends RubyObject {
|
|
115
116
|
|
116
117
|
buffer.get(bss);
|
117
118
|
buffer.clear();
|
118
|
-
return new ByteList(bss);
|
119
|
+
return new ByteList(bss, false);
|
119
120
|
}
|
120
121
|
|
121
122
|
@Override
|
@@ -174,8 +175,6 @@ public class MiniSSL extends RubyObject {
|
|
174
175
|
@JRubyMethod
|
175
176
|
public IRubyObject initialize(ThreadContext threadContext, IRubyObject miniSSLContext)
|
176
177
|
throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
|
177
|
-
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
|
178
|
-
KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType());
|
179
178
|
|
180
179
|
String keystoreFile = miniSSLContext.callMethod(threadContext, "keystore").convertToString().asJavaString();
|
181
180
|
KeyManagerFactory kmf = keyManagerFactoryMap.get(keystoreFile);
|
@@ -230,14 +229,9 @@ public class MiniSSL extends RubyObject {
|
|
230
229
|
|
231
230
|
@JRubyMethod
|
232
231
|
public IRubyObject inject(IRubyObject arg) {
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
return this;
|
237
|
-
} catch (Exception e) {
|
238
|
-
e.printStackTrace();
|
239
|
-
throw new RuntimeException(e);
|
240
|
-
}
|
232
|
+
ByteList bytes = arg.convertToString().getByteList();
|
233
|
+
inboundNetData.put(bytes.unsafeBytes(), bytes.getBegin(), bytes.getRealSize());
|
234
|
+
return this;
|
241
235
|
}
|
242
236
|
|
243
237
|
private enum SSLOperation {
|
@@ -297,7 +291,7 @@ public class MiniSSL extends RubyObject {
|
|
297
291
|
}
|
298
292
|
|
299
293
|
@JRubyMethod
|
300
|
-
public IRubyObject read()
|
294
|
+
public IRubyObject read() {
|
301
295
|
try {
|
302
296
|
inboundNetData.flip();
|
303
297
|
|
@@ -342,55 +336,46 @@ public class MiniSSL extends RubyObject {
|
|
342
336
|
return getRuntime().getNil();
|
343
337
|
}
|
344
338
|
|
345
|
-
RubyString
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
throw
|
339
|
+
return RubyString.newString(getRuntime(), appDataByteList);
|
340
|
+
} catch (SSLException e) {
|
341
|
+
RaiseException re = getRuntime().newEOFError(e.getMessage());
|
342
|
+
re.initCause(e);
|
343
|
+
throw re;
|
350
344
|
}
|
351
345
|
}
|
352
346
|
|
353
347
|
@JRubyMethod
|
354
348
|
public IRubyObject write(IRubyObject arg) {
|
355
|
-
|
356
|
-
|
357
|
-
outboundAppData = new MiniSSLBuffer(bls);
|
349
|
+
byte[] bls = arg.convertToString().getBytes();
|
350
|
+
outboundAppData = new MiniSSLBuffer(bls);
|
358
351
|
|
359
|
-
|
360
|
-
} catch (Exception e) {
|
361
|
-
e.printStackTrace();
|
362
|
-
throw new RuntimeException(e);
|
363
|
-
}
|
352
|
+
return getRuntime().newFixnum(bls.length);
|
364
353
|
}
|
365
354
|
|
366
355
|
@JRubyMethod
|
367
|
-
public IRubyObject extract()
|
356
|
+
public IRubyObject extract(ThreadContext context) {
|
368
357
|
try {
|
369
358
|
ByteList dataByteList = outboundNetData.asByteList();
|
370
359
|
if (dataByteList != null) {
|
371
|
-
RubyString
|
372
|
-
str.setValue(dataByteList);
|
373
|
-
return str;
|
360
|
+
return RubyString.newString(context.runtime, dataByteList);
|
374
361
|
}
|
375
362
|
|
376
363
|
if (!outboundAppData.hasRemaining()) {
|
377
|
-
return
|
364
|
+
return context.nil;
|
378
365
|
}
|
379
366
|
|
380
367
|
outboundNetData.clear();
|
381
368
|
doOp(SSLOperation.WRAP, outboundAppData, outboundNetData);
|
382
369
|
dataByteList = outboundNetData.asByteList();
|
383
370
|
if (dataByteList == null) {
|
384
|
-
return
|
371
|
+
return context.nil;
|
385
372
|
}
|
386
373
|
|
387
|
-
RubyString
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
e.printStackTrace();
|
393
|
-
throw new RuntimeException(e);
|
374
|
+
return RubyString.newString(context.runtime, dataByteList);
|
375
|
+
} catch (SSLException e) {
|
376
|
+
RaiseException ex = context.runtime.newRuntimeError(e.toString());
|
377
|
+
ex.initCause(e);
|
378
|
+
throw ex;
|
394
379
|
}
|
395
380
|
}
|
396
381
|
|
@@ -398,7 +383,7 @@ public class MiniSSL extends RubyObject {
|
|
398
383
|
public IRubyObject peercert() throws CertificateEncodingException {
|
399
384
|
try {
|
400
385
|
return JavaEmbedUtils.javaToRuby(getRuntime(), engine.getSession().getPeerCertificates()[0].getEncoded());
|
401
|
-
} catch (SSLPeerUnverifiedException
|
386
|
+
} catch (SSLPeerUnverifiedException e) {
|
402
387
|
return getRuntime().getNil();
|
403
388
|
}
|
404
389
|
}
|
@@ -40,7 +40,9 @@ static VALUE global_http_version;
|
|
40
40
|
static VALUE global_request_path;
|
41
41
|
|
42
42
|
/** Defines common length and error messages for input length validation. */
|
43
|
-
#define
|
43
|
+
#define QUOTE(s) #s
|
44
|
+
#define EXPLAIN_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 " EXPLAIN_MAX_LENGTH_VALUE(length) " allowed length (was %d)"
|
44
46
|
|
45
47
|
/** Validates the max length of given input and throws an HttpParserError exception if over. */
|
46
48
|
#define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR, len); }
|
@@ -50,12 +52,16 @@ static VALUE global_request_path;
|
|
50
52
|
|
51
53
|
|
52
54
|
/* Defines the maximum allowed lengths for various input elements.*/
|
55
|
+
#ifndef PUMA_QUERY_STRING_MAX_LENGTH
|
56
|
+
#define PUMA_QUERY_STRING_MAX_LENGTH (1024 * 10)
|
57
|
+
#endif
|
58
|
+
|
53
59
|
DEF_MAX_LENGTH(FIELD_NAME, 256);
|
54
60
|
DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
|
55
61
|
DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12);
|
56
62
|
DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
|
57
63
|
DEF_MAX_LENGTH(REQUEST_PATH, 8192);
|
58
|
-
DEF_MAX_LENGTH(QUERY_STRING,
|
64
|
+
DEF_MAX_LENGTH(QUERY_STRING, PUMA_QUERY_STRING_MAX_LENGTH);
|
59
65
|
DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
|
60
66
|
|
61
67
|
struct common_field {
|
@@ -445,7 +451,7 @@ VALUE HttpParser_body(VALUE self) {
|
|
445
451
|
void Init_mini_ssl(VALUE mod);
|
446
452
|
#endif
|
447
453
|
|
448
|
-
void Init_puma_http11()
|
454
|
+
void Init_puma_http11(void)
|
449
455
|
{
|
450
456
|
|
451
457
|
VALUE mPuma = rb_define_module("Puma");
|
data/lib/puma/app/status.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require 'puma/json_serialization'
|
2
3
|
|
3
4
|
module Puma
|
4
5
|
module App
|
@@ -22,10 +23,6 @@ module Puma
|
|
22
23
|
return rack_response(403, 'Invalid auth token', 'text/plain')
|
23
24
|
end
|
24
25
|
|
25
|
-
if env['PATH_INFO'] =~ /\/(gc-stats|stats|thread-backtraces)$/
|
26
|
-
require 'json'
|
27
|
-
end
|
28
|
-
|
29
26
|
# resp_type is processed by following case statement, return
|
30
27
|
# is a number (status) or a string used as the body of a 200 response
|
31
28
|
resp_type =
|
@@ -49,17 +46,17 @@ module Puma
|
|
49
46
|
GC.start ; 200
|
50
47
|
|
51
48
|
when 'gc-stats'
|
52
|
-
GC.stat
|
49
|
+
Puma::JSONSerialization.generate GC.stat
|
53
50
|
|
54
51
|
when 'stats'
|
55
|
-
@launcher.stats
|
52
|
+
Puma::JSONSerialization.generate @launcher.stats
|
56
53
|
|
57
54
|
when 'thread-backtraces'
|
58
55
|
backtraces = []
|
59
56
|
@launcher.thread_status do |name, backtrace|
|
60
57
|
backtraces << { name: name, backtrace: backtrace }
|
61
58
|
end
|
62
|
-
backtraces
|
59
|
+
Puma::JSONSerialization.generate backtraces
|
63
60
|
|
64
61
|
else
|
65
62
|
return rack_response(404, "Unsupported action", 'text/plain')
|
data/lib/puma/binder.rb
CHANGED
@@ -13,7 +13,7 @@ module Puma
|
|
13
13
|
require 'puma/minissl'
|
14
14
|
require 'puma/minissl/context_builder'
|
15
15
|
|
16
|
-
# Odd bug in 'pure Ruby' nio4r
|
16
|
+
# Odd bug in 'pure Ruby' nio4r version 2.5.2, which installs with Ruby 2.3.
|
17
17
|
# NIO doesn't create any OpenSSL objects, but it rescues an OpenSSL error.
|
18
18
|
# The bug was that it did not require openssl.
|
19
19
|
# @todo remove when Ruby 2.3 support is dropped
|
@@ -30,6 +30,7 @@ module Puma
|
|
30
30
|
|
31
31
|
def initialize(events, conf = Configuration.new)
|
32
32
|
@events = events
|
33
|
+
@conf = conf
|
33
34
|
@listeners = []
|
34
35
|
@inherited_fds = {}
|
35
36
|
@activated_sockets = {}
|
@@ -41,6 +42,7 @@ module Puma
|
|
41
42
|
"rack.multithread".freeze => conf.options[:max_threads] > 1,
|
42
43
|
"rack.multiprocess".freeze => conf.options[:workers] >= 1,
|
43
44
|
"rack.run_once".freeze => false,
|
45
|
+
RACK_URL_SCHEME => conf.options[:rack_url_scheme],
|
44
46
|
"SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
|
45
47
|
|
46
48
|
# I'd like to set a default CONTENT_TYPE here but some things
|
@@ -56,6 +58,7 @@ module Puma
|
|
56
58
|
|
57
59
|
@envs = {}
|
58
60
|
@ios = []
|
61
|
+
localhost_authority
|
59
62
|
end
|
60
63
|
|
61
64
|
attr_reader :ios
|
@@ -95,6 +98,7 @@ module Puma
|
|
95
98
|
# @version 5.0.0
|
96
99
|
#
|
97
100
|
def create_activated_fds(env_hash)
|
101
|
+
@events.debug "ENV['LISTEN_FDS'] #{ENV['LISTEN_FDS'].inspect} env_hash['LISTEN_PID'] #{env_hash['LISTEN_PID'].inspect}"
|
98
102
|
return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
|
99
103
|
env_hash['LISTEN_FDS'].to_i.times do |index|
|
100
104
|
sock = TCPServer.for_fd(socket_activation_fd(index))
|
@@ -111,6 +115,43 @@ module Puma
|
|
111
115
|
["LISTEN_FDS", "LISTEN_PID"] # Signal to remove these keys from ENV
|
112
116
|
end
|
113
117
|
|
118
|
+
# Synthesize binds from systemd socket activation
|
119
|
+
#
|
120
|
+
# When systemd socket activation is enabled, it can be tedious to keep the
|
121
|
+
# binds in sync. This method can synthesize any binds based on the received
|
122
|
+
# activated sockets. Any existing matching binds will be respected.
|
123
|
+
#
|
124
|
+
# When only_matching is true in, all binds that do not match an activated
|
125
|
+
# socket is removed in place.
|
126
|
+
#
|
127
|
+
# It's a noop if no activated sockets were received.
|
128
|
+
def synthesize_binds_from_activated_fs(binds, only_matching)
|
129
|
+
return binds unless activated_sockets.any?
|
130
|
+
|
131
|
+
activated_binds = []
|
132
|
+
|
133
|
+
activated_sockets.keys.each do |proto, addr, port|
|
134
|
+
if port
|
135
|
+
tcp_url = "#{proto}://#{addr}:#{port}"
|
136
|
+
ssl_url = "ssl://#{addr}:#{port}"
|
137
|
+
ssl_url_prefix = "#{ssl_url}?"
|
138
|
+
|
139
|
+
existing = binds.find { |bind| bind == tcp_url || bind == ssl_url || bind.start_with?(ssl_url_prefix) }
|
140
|
+
|
141
|
+
activated_binds << (existing || tcp_url)
|
142
|
+
else
|
143
|
+
# TODO: can there be a SSL bind without a port?
|
144
|
+
activated_binds << "#{proto}://#{addr}"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
if only_matching
|
149
|
+
activated_binds
|
150
|
+
else
|
151
|
+
binds | activated_binds
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
114
155
|
def parse(binds, logger, log_msg = 'Listening')
|
115
156
|
binds.each do |str|
|
116
157
|
uri = URI.parse str
|
@@ -123,21 +164,16 @@ module Puma
|
|
123
164
|
io = inherit_tcp_listener uri.host, uri.port, sock
|
124
165
|
logger.log "* Activated #{str}"
|
125
166
|
else
|
167
|
+
ios_len = @ios.length
|
126
168
|
params = Util.parse_query uri.query
|
127
169
|
|
128
|
-
opt = params.key?('low_latency')
|
129
|
-
|
170
|
+
opt = params.key?('low_latency') && params['low_latency'] != 'false'
|
171
|
+
backlog = params.fetch('backlog', 1024).to_i
|
130
172
|
|
131
|
-
io = add_tcp_listener uri.host, uri.port, opt,
|
132
|
-
|
133
|
-
@ios.each do |i|
|
134
|
-
next unless TCPServer === i
|
135
|
-
addr = if i.local_address.ipv6?
|
136
|
-
"[#{i.local_address.ip_unpack[0]}]:#{i.local_address.ip_unpack[1]}"
|
137
|
-
else
|
138
|
-
i.local_address.ip_unpack.join(':')
|
139
|
-
end
|
173
|
+
io = add_tcp_listener uri.host, uri.port, opt, backlog
|
140
174
|
|
175
|
+
@ios[ios_len..-1].each do |i|
|
176
|
+
addr = loc_addr_str i
|
141
177
|
logger.log "* #{log_msg} on http://#{addr}"
|
142
178
|
end
|
143
179
|
end
|
@@ -145,11 +181,20 @@ module Puma
|
|
145
181
|
@listeners << [str, io] if io
|
146
182
|
when "unix"
|
147
183
|
path = "#{uri.host}#{uri.path}".gsub("%20", " ")
|
184
|
+
abstract = false
|
185
|
+
if str.start_with? 'unix://@'
|
186
|
+
raise "OS does not support abstract UNIXSockets" unless Puma.abstract_unix_socket?
|
187
|
+
abstract = true
|
188
|
+
path = "@#{path}"
|
189
|
+
end
|
148
190
|
|
149
191
|
if fd = @inherited_fds.delete(str)
|
192
|
+
@unix_paths << path unless abstract
|
150
193
|
io = inherit_unix_listener path, fd
|
151
194
|
logger.log "* Inherited #{str}"
|
152
|
-
elsif sock = @activated_sockets.delete([ :unix, path ])
|
195
|
+
elsif sock = @activated_sockets.delete([ :unix, path ]) ||
|
196
|
+
@activated_sockets.delete([ :unix, File.realdirpath(path) ])
|
197
|
+
@unix_paths << path unless abstract || File.exist?(path)
|
153
198
|
io = inherit_unix_listener path, sock
|
154
199
|
logger.log "* Activated #{str}"
|
155
200
|
else
|
@@ -173,6 +218,7 @@ module Puma
|
|
173
218
|
end
|
174
219
|
end
|
175
220
|
|
221
|
+
@unix_paths << path unless abstract || File.exist?(path)
|
176
222
|
io = add_unix_listener path, umask, mode, backlog
|
177
223
|
logger.log "* #{log_msg} on #{str}"
|
178
224
|
end
|
@@ -183,7 +229,25 @@ module Puma
|
|
183
229
|
raise "Puma compiled without SSL support" unless HAS_SSL
|
184
230
|
|
185
231
|
params = Util.parse_query uri.query
|
186
|
-
|
232
|
+
|
233
|
+
# If key and certs are not defined and localhost gem is required.
|
234
|
+
# localhost gem will be used for self signed
|
235
|
+
# Load localhost authority if not loaded.
|
236
|
+
if params.values_at('cert', 'key').all? { |v| v.to_s.empty? }
|
237
|
+
ctx = localhost_authority && localhost_authority_context
|
238
|
+
end
|
239
|
+
|
240
|
+
ctx ||=
|
241
|
+
begin
|
242
|
+
# Extract cert_pem and key_pem from options[:store] if present
|
243
|
+
['cert', 'key'].each do |v|
|
244
|
+
if params[v] && params[v].start_with?('store:')
|
245
|
+
index = Integer(params.delete(v).split('store:').last)
|
246
|
+
params["#{v}_pem"] = @conf.options[:store][index]
|
247
|
+
end
|
248
|
+
end
|
249
|
+
MiniSSL::ContextBuilder.new(params, @events).context
|
250
|
+
end
|
187
251
|
|
188
252
|
if fd = @inherited_fds.delete(str)
|
189
253
|
logger.log "* Inherited #{str}"
|
@@ -192,8 +256,14 @@ module Puma
|
|
192
256
|
io = inherit_ssl_listener sock, ctx
|
193
257
|
logger.log "* Activated #{str}"
|
194
258
|
else
|
195
|
-
|
196
|
-
|
259
|
+
ios_len = @ios.length
|
260
|
+
backlog = params.fetch('backlog', 1024).to_i
|
261
|
+
io = add_ssl_listener uri.host, uri.port, ctx, optimize_for_latency = true, backlog
|
262
|
+
|
263
|
+
@ios[ios_len..-1].each do |i|
|
264
|
+
addr = loc_addr_str i
|
265
|
+
logger.log "* #{log_msg} on ssl://#{addr}?#{uri.query}"
|
266
|
+
end
|
197
267
|
end
|
198
268
|
|
199
269
|
@listeners << [str, io] if io
|
@@ -221,17 +291,37 @@ module Puma
|
|
221
291
|
end
|
222
292
|
|
223
293
|
# Also close any unused activated sockets
|
224
|
-
@activated_sockets.
|
225
|
-
|
226
|
-
|
227
|
-
sock.
|
228
|
-
|
294
|
+
unless @activated_sockets.empty?
|
295
|
+
fds = @ios.map(&:to_i)
|
296
|
+
@activated_sockets.each do |key, sock|
|
297
|
+
next if fds.include? sock.to_i
|
298
|
+
logger.log "* Closing unused activated socket: #{key.first}://#{key[1..-1].join ':'}"
|
299
|
+
begin
|
300
|
+
sock.close
|
301
|
+
rescue SystemCallError
|
302
|
+
end
|
303
|
+
# We have to unlink a unix socket path that's not being used
|
304
|
+
File.unlink key[1] if key.first == :unix
|
229
305
|
end
|
230
|
-
# We have to unlink a unix socket path that's not being used
|
231
|
-
File.unlink key[1] if key[0] == :unix
|
232
306
|
end
|
233
307
|
end
|
234
308
|
|
309
|
+
def localhost_authority
|
310
|
+
@localhost_authority ||= Localhost::Authority.fetch if defined?(Localhost::Authority) && !Puma::IS_JRUBY
|
311
|
+
end
|
312
|
+
|
313
|
+
def localhost_authority_context
|
314
|
+
return unless localhost_authority
|
315
|
+
|
316
|
+
key_path, crt_path = if [:key_path, :certificate_path].all? { |m| localhost_authority.respond_to?(m) }
|
317
|
+
[localhost_authority.key_path, localhost_authority.certificate_path]
|
318
|
+
else
|
319
|
+
local_certificates_path = File.expand_path("~/.localhost")
|
320
|
+
[File.join(local_certificates_path, "localhost.key"), File.join(local_certificates_path, "localhost.crt")]
|
321
|
+
end
|
322
|
+
MiniSSL::ContextBuilder.new({ "key" => key_path, "cert" => crt_path }, @events).context
|
323
|
+
end
|
324
|
+
|
235
325
|
# Tell the server to listen on host +host+, port +port+.
|
236
326
|
# If +optimize_for_latency+ is true (the default) then clients connecting
|
237
327
|
# will be optimized for latency over throughput.
|
@@ -249,6 +339,7 @@ module Puma
|
|
249
339
|
|
250
340
|
host = host[1..-2] if host and host[0..0] == '['
|
251
341
|
tcp_server = TCPServer.new(host, port)
|
342
|
+
|
252
343
|
if optimize_for_latency
|
253
344
|
tcp_server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
254
345
|
end
|
@@ -260,11 +351,7 @@ module Puma
|
|
260
351
|
end
|
261
352
|
|
262
353
|
def inherit_tcp_listener(host, port, fd)
|
263
|
-
|
264
|
-
s = fd
|
265
|
-
else
|
266
|
-
s = TCPServer.for_fd(fd)
|
267
|
-
end
|
354
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
|
268
355
|
|
269
356
|
@ios << s
|
270
357
|
s
|
@@ -274,6 +361,8 @@ module Puma
|
|
274
361
|
optimize_for_latency=true, backlog=1024)
|
275
362
|
|
276
363
|
raise "Puma compiled without SSL support" unless HAS_SSL
|
364
|
+
# Puma will try to use local authority context if context is supplied nil
|
365
|
+
ctx ||= localhost_authority_context
|
277
366
|
|
278
367
|
if host == "localhost"
|
279
368
|
loopback_addresses.each do |addr|
|
@@ -301,12 +390,11 @@ module Puma
|
|
301
390
|
|
302
391
|
def inherit_ssl_listener(fd, ctx)
|
303
392
|
raise "Puma compiled without SSL support" unless HAS_SSL
|
393
|
+
# Puma will try to use local authority context if context is supplied nil
|
394
|
+
ctx ||= localhost_authority_context
|
395
|
+
|
396
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
|
304
397
|
|
305
|
-
if fd.kind_of? TCPServer
|
306
|
-
s = fd
|
307
|
-
else
|
308
|
-
s = TCPServer.for_fd(fd)
|
309
|
-
end
|
310
398
|
ssl = MiniSSL::Server.new(s, ctx)
|
311
399
|
|
312
400
|
env = @proto_env.dup
|
@@ -321,8 +409,6 @@ module Puma
|
|
321
409
|
# Tell the server to listen on +path+ as a UNIX domain socket.
|
322
410
|
#
|
323
411
|
def add_unix_listener(path, umask=nil, mode=nil, backlog=1024)
|
324
|
-
@unix_paths << path unless File.exist? path
|
325
|
-
|
326
412
|
# Let anyone connect by default
|
327
413
|
umask ||= 0
|
328
414
|
|
@@ -339,8 +425,7 @@ module Puma
|
|
339
425
|
raise "There is already a server bound to: #{path}"
|
340
426
|
end
|
341
427
|
end
|
342
|
-
|
343
|
-
s = UNIXServer.new(path)
|
428
|
+
s = UNIXServer.new path.sub(/\A@/, "\0") # check for abstract UNIXSocket
|
344
429
|
s.listen backlog
|
345
430
|
@ios << s
|
346
431
|
ensure
|
@@ -359,13 +444,8 @@ module Puma
|
|
359
444
|
end
|
360
445
|
|
361
446
|
def inherit_unix_listener(path, fd)
|
362
|
-
|
447
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::UNIXServer.for_fd(fd)
|
363
448
|
|
364
|
-
if fd.kind_of? TCPServer
|
365
|
-
s = fd
|
366
|
-
else
|
367
|
-
s = UNIXServer.for_fd fd
|
368
|
-
end
|
369
449
|
@ios << s
|
370
450
|
|
371
451
|
env = @proto_env.dup
|
@@ -376,24 +456,24 @@ module Puma
|
|
376
456
|
end
|
377
457
|
|
378
458
|
def close_listeners
|
379
|
-
listeners.each do |l, io|
|
380
|
-
io.close unless io.closed?
|
381
|
-
uri = URI.parse
|
459
|
+
@listeners.each do |l, io|
|
460
|
+
io.close unless io.closed?
|
461
|
+
uri = URI.parse l
|
382
462
|
next unless uri.scheme == 'unix'
|
383
463
|
unix_path = "#{uri.host}#{uri.path}"
|
384
|
-
File.unlink unix_path if unix_paths.include? unix_path
|
464
|
+
File.unlink unix_path if @unix_paths.include?(unix_path) && File.exist?(unix_path)
|
385
465
|
end
|
386
466
|
end
|
387
467
|
|
388
468
|
def redirects_for_restart
|
389
|
-
redirects = listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h
|
469
|
+
redirects = @listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h
|
390
470
|
redirects[:close_others] = true
|
391
471
|
redirects
|
392
472
|
end
|
393
473
|
|
394
474
|
# @version 5.0.0
|
395
475
|
def redirects_for_restart_env
|
396
|
-
listeners.each_with_object({}).with_index do |(listen, memo), i|
|
476
|
+
@listeners.each_with_object({}).with_index do |(listen, memo), i|
|
397
477
|
memo["PUMA_INHERIT_#{i}"] = "#{listen[1].to_i}:#{listen[0]}"
|
398
478
|
end
|
399
479
|
end
|
@@ -407,6 +487,15 @@ module Puma
|
|
407
487
|
end.map { |addrinfo| addrinfo.ip_address }.uniq
|
408
488
|
end
|
409
489
|
|
490
|
+
def loc_addr_str(io)
|
491
|
+
loc_addr = io.to_io.local_address
|
492
|
+
if loc_addr.ipv6?
|
493
|
+
"[#{loc_addr.ip_unpack[0]}]:#{loc_addr.ip_unpack[1]}"
|
494
|
+
else
|
495
|
+
loc_addr.ip_unpack.join(':')
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
410
499
|
# @version 5.0.0
|
411
500
|
def socket_activation_fd(int)
|
412
501
|
int + 3 # 3 is the magic number you add to follow the SA protocol
|
data/lib/puma/cli.rb
CHANGED
@@ -11,16 +11,17 @@ require 'puma/events'
|
|
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
|
+
# @deprecated 6.0.0
|
24
25
|
KEYS_NOT_TO_PERSIST_IN_STATE = Launcher::KEYS_NOT_TO_PERSIST_IN_STATE
|
25
26
|
|
26
27
|
# Create a new CLI object using +argv+ as the command line
|
@@ -104,10 +105,19 @@ module Puma
|
|
104
105
|
user_config.bind arg
|
105
106
|
end
|
106
107
|
|
108
|
+
o.on "--bind-to-activated-sockets [only]", "Bind to all activated sockets" do |arg|
|
109
|
+
user_config.bind_to_activated_sockets(arg || true)
|
110
|
+
end
|
111
|
+
|
107
112
|
o.on "-C", "--config PATH", "Load PATH as a config file" do |arg|
|
108
113
|
file_config.load arg
|
109
114
|
end
|
110
115
|
|
116
|
+
# Identical to supplying --config "-", but more semantic
|
117
|
+
o.on "--no-config", "Prevent Puma from searching for a config file" do |arg|
|
118
|
+
file_config.load "-"
|
119
|
+
end
|
120
|
+
|
111
121
|
o.on "--control-url URL", "The bind url to use for the control server. Use 'auto' to use temp unix server" do |arg|
|
112
122
|
configure_control_url(arg)
|
113
123
|
end
|
@@ -175,6 +185,10 @@ module Puma
|
|
175
185
|
user_config.restart_command cmd
|
176
186
|
end
|
177
187
|
|
188
|
+
o.on "-s", "--silent", "Do not log prompt messages other than errors" do
|
189
|
+
@events = Events.new NullIO.new, $stderr
|
190
|
+
end
|
191
|
+
|
178
192
|
o.on "-S", "--state PATH", "Where to store the state details" do |arg|
|
179
193
|
user_config.state_path arg
|
180
194
|
end
|