puma 3.8.2 → 3.12.6
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 -5
- data/History.md +153 -0
- data/README.md +140 -230
- data/docs/architecture.md +36 -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/plugins.md +28 -0
- data/docs/restart.md +39 -0
- data/docs/signals.md +56 -3
- data/docs/systemd.md +112 -37
- data/ext/puma_http11/http11_parser.c +87 -85
- data/ext/puma_http11/http11_parser.rl +12 -10
- data/ext/puma_http11/mini_ssl.c +31 -5
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +13 -16
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +15 -2
- data/lib/puma/app/status.rb +8 -0
- data/lib/puma/binder.rb +22 -17
- data/lib/puma/cli.rb +22 -7
- data/lib/puma/client.rb +41 -2
- data/lib/puma/cluster.rb +28 -7
- data/lib/puma/commonlogger.rb +2 -0
- data/lib/puma/configuration.rb +21 -14
- data/lib/puma/const.rb +17 -2
- data/lib/puma/control_cli.rb +16 -14
- data/lib/puma/convenient.rb +2 -0
- data/lib/puma/daemon_ext.rb +2 -0
- data/lib/puma/delegation.rb +2 -0
- data/lib/puma/detect.rb +2 -0
- data/lib/puma/dsl.rb +46 -9
- data/lib/puma/events.rb +3 -2
- data/lib/puma/io_buffer.rb +2 -0
- data/lib/puma/java_io_buffer.rb +2 -0
- data/lib/puma/jruby_restart.rb +2 -1
- data/lib/puma/launcher.rb +42 -20
- data/lib/puma/minissl.rb +67 -28
- data/lib/puma/null_io.rb +2 -0
- data/lib/puma/plugin/tmp_restart.rb +0 -1
- data/lib/puma/plugin.rb +2 -0
- data/lib/puma/rack/builder.rb +2 -1
- data/lib/puma/reactor.rb +137 -0
- data/lib/puma/runner.rb +16 -3
- data/lib/puma/server.rb +145 -29
- data/lib/puma/single.rb +14 -3
- data/lib/puma/state_file.rb +2 -0
- data/lib/puma/tcp_logger.rb +2 -0
- data/lib/puma/thread_pool.rb +55 -6
- data/lib/puma/util.rb +1 -0
- data/lib/puma.rb +8 -0
- data/lib/rack/handler/puma.rb +13 -2
- data/tools/jungle/README.md +12 -2
- data/tools/jungle/init.d/README.md +2 -0
- data/tools/jungle/init.d/puma +2 -2
- data/tools/jungle/init.d/run-puma +1 -1
- data/tools/jungle/rc.d/README.md +74 -0
- data/tools/jungle/rc.d/puma +61 -0
- data/tools/jungle/rc.d/puma.conf +10 -0
- data/tools/trickletest.rb +1 -1
- metadata +21 -95
- data/.github/issue_template.md +0 -20
- data/Gemfile +0 -12
- data/Manifest.txt +0 -78
- data/Rakefile +0 -158
- data/Release.md +0 -9
- data/gemfiles/2.1-Gemfile +0 -12
- data/puma.gemspec +0 -52
- /data/{DEPLOYMENT.md → docs/deployment.md} +0 -0
@@ -182,9 +182,6 @@ static final int puma_parser_start = 1;
|
|
182
182
|
static final int puma_parser_first_final = 47;
|
183
183
|
static final int puma_parser_error = 0;
|
184
184
|
|
185
|
-
static final int puma_parser_en_main = 1;
|
186
|
-
|
187
|
-
|
188
185
|
// line 69 "ext/puma_http11/http11_parser.java.rl"
|
189
186
|
|
190
187
|
public static interface ElementCB {
|
@@ -220,7 +217,7 @@ static final int puma_parser_en_main = 1;
|
|
220
217
|
public void init() {
|
221
218
|
cs = 0;
|
222
219
|
|
223
|
-
|
220
|
+
|
224
221
|
// line 225 "ext/puma_http11/org/jruby/puma/Http11Parser.java"
|
225
222
|
{
|
226
223
|
cs = puma_parser_start;
|
@@ -252,7 +249,7 @@ static final int puma_parser_en_main = 1;
|
|
252
249
|
byte[] data = buffer.bytes();
|
253
250
|
parser.buffer = buffer;
|
254
251
|
|
255
|
-
|
252
|
+
|
256
253
|
// line 257 "ext/puma_http11/org/jruby/puma/Http11Parser.java"
|
257
254
|
{
|
258
255
|
int _klen;
|
@@ -347,7 +344,7 @@ case 1:
|
|
347
344
|
break;
|
348
345
|
case 3:
|
349
346
|
// line 17 "ext/puma_http11/http11_parser.java.rl"
|
350
|
-
{
|
347
|
+
{
|
351
348
|
parser.field_len = p-parser.field_start;
|
352
349
|
}
|
353
350
|
break;
|
@@ -357,7 +354,7 @@ case 1:
|
|
357
354
|
break;
|
358
355
|
case 5:
|
359
356
|
// line 22 "ext/puma_http11/http11_parser.java.rl"
|
360
|
-
{
|
357
|
+
{
|
361
358
|
if(parser.http_field != null) {
|
362
359
|
parser.http_field.call(parser.data, parser.field_start, parser.field_len, parser.mark, p-parser.mark);
|
363
360
|
}
|
@@ -365,21 +362,21 @@ case 1:
|
|
365
362
|
break;
|
366
363
|
case 6:
|
367
364
|
// line 27 "ext/puma_http11/http11_parser.java.rl"
|
368
|
-
{
|
369
|
-
if(parser.request_method != null)
|
365
|
+
{
|
366
|
+
if(parser.request_method != null)
|
370
367
|
parser.request_method.call(parser.data, parser.mark, p-parser.mark);
|
371
368
|
}
|
372
369
|
break;
|
373
370
|
case 7:
|
374
371
|
// line 31 "ext/puma_http11/http11_parser.java.rl"
|
375
|
-
{
|
372
|
+
{
|
376
373
|
if(parser.request_uri != null)
|
377
374
|
parser.request_uri.call(parser.data, parser.mark, p-parser.mark);
|
378
375
|
}
|
379
376
|
break;
|
380
377
|
case 8:
|
381
378
|
// line 35 "ext/puma_http11/http11_parser.java.rl"
|
382
|
-
{
|
379
|
+
{
|
383
380
|
if(parser.fragment != null)
|
384
381
|
parser.fragment.call(parser.data, parser.mark, p-parser.mark);
|
385
382
|
}
|
@@ -390,14 +387,14 @@ case 1:
|
|
390
387
|
break;
|
391
388
|
case 10:
|
392
389
|
// line 41 "ext/puma_http11/http11_parser.java.rl"
|
393
|
-
{
|
390
|
+
{
|
394
391
|
if(parser.query_string != null)
|
395
392
|
parser.query_string.call(parser.data, parser.query_start, p-parser.query_start);
|
396
393
|
}
|
397
394
|
break;
|
398
395
|
case 11:
|
399
396
|
// line 46 "ext/puma_http11/http11_parser.java.rl"
|
400
|
-
{
|
397
|
+
{
|
401
398
|
if(parser.http_version != null)
|
402
399
|
parser.http_version.call(parser.data, parser.mark, p-parser.mark);
|
403
400
|
}
|
@@ -411,8 +408,8 @@ case 1:
|
|
411
408
|
break;
|
412
409
|
case 13:
|
413
410
|
// line 56 "ext/puma_http11/http11_parser.java.rl"
|
414
|
-
{
|
415
|
-
parser.body_start = p + 1;
|
411
|
+
{
|
412
|
+
parser.body_start = p + 1;
|
416
413
|
if(parser.header_done != null)
|
417
414
|
parser.header_done.call(parser.data, p + 1, pe - p - 1);
|
418
415
|
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
|
@@ -442,7 +439,7 @@ case 5:
|
|
442
439
|
|
443
440
|
parser.cs = cs;
|
444
441
|
parser.nread += (p - off);
|
445
|
-
|
442
|
+
|
446
443
|
assert p <= pe : "buffer overflow after parsing execute";
|
447
444
|
assert parser.nread <= len : "nread longer than length";
|
448
445
|
assert parser.body_start <= len : "body starts after buffer end";
|
@@ -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.javasupport.JavaEmbedUtils;
|
9
10
|
import org.jruby.runtime.Block;
|
10
11
|
import org.jruby.runtime.ObjectAllocator;
|
11
12
|
import org.jruby.runtime.ThreadContext;
|
@@ -18,6 +19,7 @@ import javax.net.ssl.SSLContext;
|
|
18
19
|
import javax.net.ssl.SSLEngine;
|
19
20
|
import javax.net.ssl.SSLEngineResult;
|
20
21
|
import javax.net.ssl.SSLException;
|
22
|
+
import javax.net.ssl.SSLPeerUnverifiedException;
|
21
23
|
import javax.net.ssl.SSLSession;
|
22
24
|
import java.io.FileInputStream;
|
23
25
|
import java.io.IOException;
|
@@ -27,6 +29,7 @@ import java.security.KeyStore;
|
|
27
29
|
import java.security.KeyStoreException;
|
28
30
|
import java.security.NoSuchAlgorithmException;
|
29
31
|
import java.security.UnrecoverableKeyException;
|
32
|
+
import java.security.cert.CertificateEncodingException;
|
30
33
|
import java.security.cert.CertificateException;
|
31
34
|
|
32
35
|
import static javax.net.ssl.SSLEngineResult.Status;
|
@@ -167,6 +170,12 @@ public class MiniSSL extends RubyObject {
|
|
167
170
|
engine.setNeedClientAuth(true);
|
168
171
|
}
|
169
172
|
|
173
|
+
IRubyObject sslCipherListObject = miniSSLContext.callMethod(threadContext, "ssl_cipher_list");
|
174
|
+
if (!sslCipherListObject.isNil()) {
|
175
|
+
String[] sslCipherList = sslCipherListObject.convertToString().asJavaString().split(",");
|
176
|
+
engine.setEnabledCipherSuites(sslCipherList);
|
177
|
+
}
|
178
|
+
|
170
179
|
SSLSession session = engine.getSession();
|
171
180
|
inboundNetData = new MiniSSLBuffer(session.getPacketBufferSize());
|
172
181
|
outboundAppData = new MiniSSLBuffer(session.getApplicationBufferSize());
|
@@ -333,7 +342,11 @@ public class MiniSSL extends RubyObject {
|
|
333
342
|
}
|
334
343
|
|
335
344
|
@JRubyMethod
|
336
|
-
public IRubyObject peercert() {
|
337
|
-
|
345
|
+
public IRubyObject peercert() throws CertificateEncodingException {
|
346
|
+
try {
|
347
|
+
return JavaEmbedUtils.javaToRuby(getRuntime(), engine.getSession().getPeerCertificates()[0].getEncoded());
|
348
|
+
} catch (SSLPeerUnverifiedException ex) {
|
349
|
+
return getRuntime().getNil();
|
350
|
+
}
|
338
351
|
}
|
339
352
|
}
|
data/lib/puma/app/status.rb
CHANGED
@@ -55,6 +55,14 @@ module Puma
|
|
55
55
|
return rack_response(200, OK_STATUS)
|
56
56
|
end
|
57
57
|
|
58
|
+
when /\/gc$/
|
59
|
+
GC.start
|
60
|
+
return rack_response(200, OK_STATUS)
|
61
|
+
|
62
|
+
when /\/gc-stats$/
|
63
|
+
json = "{" + GC.stat.map { |k, v| "\"#{k}\": #{v}" }.join(",") + "}"
|
64
|
+
return rack_response(200, json)
|
65
|
+
|
58
66
|
when /\/stats$/
|
59
67
|
return rack_response(200, @cli.stats)
|
60
68
|
else
|
data/lib/puma/binder.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'uri'
|
2
4
|
require 'socket'
|
3
5
|
|
@@ -90,19 +92,19 @@ module Puma
|
|
90
92
|
case uri.scheme
|
91
93
|
when "tcp"
|
92
94
|
if fd = @inherited_fds.delete(str)
|
93
|
-
logger.log "* Inherited #{str}"
|
94
95
|
io = inherit_tcp_listener uri.host, uri.port, fd
|
96
|
+
logger.log "* Inherited #{str}"
|
95
97
|
elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
|
96
|
-
logger.log "* Activated #{str}"
|
97
98
|
io = inherit_tcp_listener uri.host, uri.port, sock
|
99
|
+
logger.log "* Activated #{str}"
|
98
100
|
else
|
99
101
|
params = Util.parse_query uri.query
|
100
102
|
|
101
103
|
opt = params.key?('low_latency')
|
102
104
|
bak = params.fetch('backlog', 1024).to_i
|
103
105
|
|
104
|
-
logger.log "* Listening on #{str}"
|
105
106
|
io = add_tcp_listener uri.host, uri.port, opt, bak
|
107
|
+
logger.log "* Listening on #{str}"
|
106
108
|
end
|
107
109
|
|
108
110
|
@listeners << [str, io] if io
|
@@ -110,17 +112,15 @@ module Puma
|
|
110
112
|
path = "#{uri.host}#{uri.path}".gsub("%20", " ")
|
111
113
|
|
112
114
|
if fd = @inherited_fds.delete(str)
|
113
|
-
logger.log "* Inherited #{str}"
|
114
115
|
io = inherit_unix_listener path, fd
|
116
|
+
logger.log "* Inherited #{str}"
|
115
117
|
elsif sock = @activated_sockets.delete([ :unix, path ])
|
116
|
-
logger.log "* Activated #{str}"
|
117
118
|
io = inherit_unix_listener path, sock
|
119
|
+
logger.log "* Activated #{str}"
|
118
120
|
else
|
119
|
-
logger.log "* Listening on #{str}"
|
120
|
-
|
121
121
|
umask = nil
|
122
122
|
mode = nil
|
123
|
-
backlog =
|
123
|
+
backlog = 1024
|
124
124
|
|
125
125
|
if uri.query
|
126
126
|
params = Util.parse_query uri.query
|
@@ -139,6 +139,7 @@ module Puma
|
|
139
139
|
end
|
140
140
|
|
141
141
|
io = add_unix_listener path, umask, mode, backlog
|
142
|
+
logger.log "* Listening on #{str}"
|
142
143
|
end
|
143
144
|
|
144
145
|
@listeners << [str, io]
|
@@ -162,6 +163,7 @@ module Puma
|
|
162
163
|
end
|
163
164
|
|
164
165
|
ctx.keystore_pass = params['keystore-pass']
|
166
|
+
ctx.ssl_cipher_list = params['ssl_cipher_list'] if params['ssl_cipher_list']
|
165
167
|
else
|
166
168
|
unless params['key']
|
167
169
|
@events.error "Please specify the SSL key via 'key='"
|
@@ -182,6 +184,7 @@ module Puma
|
|
182
184
|
end
|
183
185
|
|
184
186
|
ctx.ca = params['ca'] if params['ca']
|
187
|
+
ctx.ssl_cipher_filter = params['ssl_cipher_filter'] if params['ssl_cipher_filter']
|
185
188
|
end
|
186
189
|
|
187
190
|
if params['verify_mode']
|
@@ -202,11 +205,11 @@ module Puma
|
|
202
205
|
logger.log "* Inherited #{str}"
|
203
206
|
io = inherit_ssl_listener fd, ctx
|
204
207
|
elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
|
205
|
-
logger.log "* Activated #{str}"
|
206
208
|
io = inherit_ssl_listener sock, ctx
|
209
|
+
logger.log "* Activated #{str}"
|
207
210
|
else
|
208
|
-
logger.log "* Listening on #{str}"
|
209
211
|
io = add_ssl_listener uri.host, uri.port, ctx
|
212
|
+
logger.log "* Listening on #{str}"
|
210
213
|
end
|
211
214
|
|
212
215
|
@listeners << [str, io] if io
|
@@ -245,9 +248,10 @@ module Puma
|
|
245
248
|
end
|
246
249
|
end
|
247
250
|
|
248
|
-
def
|
249
|
-
|
250
|
-
|
251
|
+
def loopback_addresses
|
252
|
+
Socket.ip_address_list.select do |addrinfo|
|
253
|
+
addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
|
254
|
+
end.map { |addrinfo| addrinfo.ip_address }.uniq
|
251
255
|
end
|
252
256
|
|
253
257
|
# Tell the server to listen on host +host+, port +port+.
|
@@ -259,7 +263,7 @@ module Puma
|
|
259
263
|
#
|
260
264
|
def add_tcp_listener(host, port, optimize_for_latency=true, backlog=1024)
|
261
265
|
if host == "localhost"
|
262
|
-
|
266
|
+
loopback_addresses.each do |addr|
|
263
267
|
add_tcp_listener addr, port, optimize_for_latency, backlog
|
264
268
|
end
|
265
269
|
return
|
@@ -298,7 +302,7 @@ module Puma
|
|
298
302
|
MiniSSL.check
|
299
303
|
|
300
304
|
if host == "localhost"
|
301
|
-
|
305
|
+
loopback_addresses.each do |addr|
|
302
306
|
add_ssl_listener addr, port, ctx, optimize_for_latency, backlog
|
303
307
|
end
|
304
308
|
return
|
@@ -312,6 +316,7 @@ module Puma
|
|
312
316
|
s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
313
317
|
s.listen backlog
|
314
318
|
|
319
|
+
|
315
320
|
ssl = MiniSSL::Server.new s, ctx
|
316
321
|
env = @proto_env.dup
|
317
322
|
env[HTTPS_KEY] = HTTPS
|
@@ -343,7 +348,7 @@ module Puma
|
|
343
348
|
|
344
349
|
# Tell the server to listen on +path+ as a UNIX domain socket.
|
345
350
|
#
|
346
|
-
def add_unix_listener(path, umask=nil, mode=nil, backlog=
|
351
|
+
def add_unix_listener(path, umask=nil, mode=nil, backlog=1024)
|
347
352
|
@unix_paths << path
|
348
353
|
|
349
354
|
# Let anyone connect by default
|
@@ -364,7 +369,7 @@ module Puma
|
|
364
369
|
end
|
365
370
|
|
366
371
|
s = UNIXServer.new(path)
|
367
|
-
s.listen backlog
|
372
|
+
s.listen backlog
|
368
373
|
@ios << s
|
369
374
|
ensure
|
370
375
|
File.umask old_mask
|
data/lib/puma/cli.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'optparse'
|
2
4
|
require 'uri'
|
3
5
|
|
6
|
+
require 'puma'
|
4
7
|
require 'puma/configuration'
|
5
8
|
require 'puma/launcher'
|
6
9
|
require 'puma/const'
|
@@ -83,6 +86,14 @@ module Puma
|
|
83
86
|
raise UnsupportedOption
|
84
87
|
end
|
85
88
|
|
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"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
86
97
|
# Build the OptionParser object to handle the available options.
|
87
98
|
#
|
88
99
|
|
@@ -97,13 +108,13 @@ module Puma
|
|
97
108
|
file_config.load arg
|
98
109
|
end
|
99
110
|
|
100
|
-
o.on "--control URL", "The bind url to use for the control server"
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
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
|
114
|
+
|
115
|
+
# alias --control-url for backwards-compatibility
|
116
|
+
o.on "--control URL", "DEPRECATED alias for --control-url" do |arg|
|
117
|
+
configure_control_url(arg)
|
107
118
|
end
|
108
119
|
|
109
120
|
o.on "--control-token TOKEN",
|
@@ -181,6 +192,10 @@ module Puma
|
|
181
192
|
user_config.tcp_mode!
|
182
193
|
end
|
183
194
|
|
195
|
+
o.on "--early-hints", "Enable early hints support" do
|
196
|
+
user_config.early_hints
|
197
|
+
end
|
198
|
+
|
184
199
|
o.on "-V", "--version", "Print the version information" do
|
185
200
|
puts "puma version #{Puma::Const::VERSION}"
|
186
201
|
exit 0
|
data/lib/puma/client.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class IO
|
2
4
|
# We need to use this for a jruby work around on both 1.8 and 1.9.
|
3
5
|
# So this either creates the constant (on 1.8), or harmlessly
|
@@ -21,6 +23,17 @@ module Puma
|
|
21
23
|
|
22
24
|
class ConnectionError < RuntimeError; end
|
23
25
|
|
26
|
+
# An instance of this class represents a unique request from a client.
|
27
|
+
# For example a web request from a browser or from CURL. This
|
28
|
+
#
|
29
|
+
# An instance of `Puma::Client` can be used as if it were an IO object
|
30
|
+
# for example it is passed into `IO.select` inside of the `Puma::Reactor`.
|
31
|
+
# This is accomplished by the `to_io` method which gets called on any
|
32
|
+
# non-IO objects being used with the IO api such as `IO.select.
|
33
|
+
#
|
34
|
+
# Instances of this class are responsible for knowing if
|
35
|
+
# the header and body are fully buffered via the `try_to_finish` method.
|
36
|
+
# They can be used to "time out" a response via the `timeout_at` reader.
|
24
37
|
class Client
|
25
38
|
include Puma::Const
|
26
39
|
extend Puma::Delegation
|
@@ -111,6 +124,7 @@ module Puma
|
|
111
124
|
begin
|
112
125
|
@io.close
|
113
126
|
rescue IOError
|
127
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
114
128
|
end
|
115
129
|
end
|
116
130
|
|
@@ -156,6 +170,7 @@ module Puma
|
|
156
170
|
if len == 0
|
157
171
|
@body.rewind
|
158
172
|
rest = io.read
|
173
|
+
rest = rest[2..-1] if rest.start_with?("\r\n")
|
159
174
|
@buffer = rest.empty? ? nil : rest
|
160
175
|
@requests_served += 1
|
161
176
|
@ready = true
|
@@ -229,8 +244,16 @@ module Puma
|
|
229
244
|
|
230
245
|
te = @env[TRANSFER_ENCODING2]
|
231
246
|
|
232
|
-
if te
|
233
|
-
|
247
|
+
if te
|
248
|
+
if te.include?(",")
|
249
|
+
te.split(",").each do |part|
|
250
|
+
if CHUNKED.casecmp(part.strip) == 0
|
251
|
+
return setup_chunked_body(body)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
elsif CHUNKED.casecmp(te) == 0
|
255
|
+
return setup_chunked_body(body)
|
256
|
+
end
|
234
257
|
end
|
235
258
|
|
236
259
|
@chunked_body = false
|
@@ -283,6 +306,14 @@ module Puma
|
|
283
306
|
raise ConnectionError, "Connection error detected during read"
|
284
307
|
end
|
285
308
|
|
309
|
+
# No data means a closed socket
|
310
|
+
unless data
|
311
|
+
@buffer = nil
|
312
|
+
@requests_served += 1
|
313
|
+
@ready = true
|
314
|
+
raise EOFError
|
315
|
+
end
|
316
|
+
|
286
317
|
if @buffer
|
287
318
|
@buffer << data
|
288
319
|
else
|
@@ -312,6 +343,14 @@ module Puma
|
|
312
343
|
raise e
|
313
344
|
end
|
314
345
|
|
346
|
+
# No data means a closed socket
|
347
|
+
unless data
|
348
|
+
@buffer = nil
|
349
|
+
@requests_served += 1
|
350
|
+
@ready = true
|
351
|
+
raise EOFError
|
352
|
+
end
|
353
|
+
|
315
354
|
if @buffer
|
316
355
|
@buffer << data
|
317
356
|
else
|
data/lib/puma/cluster.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'puma/runner'
|
2
4
|
require 'puma/util'
|
3
5
|
require 'puma/plugin'
|
@@ -5,6 +7,17 @@ require 'puma/plugin'
|
|
5
7
|
require 'time'
|
6
8
|
|
7
9
|
module Puma
|
10
|
+
# This class is instantiated by the `Puma::Launcher` and used
|
11
|
+
# to boot and serve a Ruby application when puma "workers" are needed
|
12
|
+
# i.e. when using multi-processes. For example `$ puma -w 5`
|
13
|
+
#
|
14
|
+
# At the core of this class is running an instance of `Puma::Server` which
|
15
|
+
# gets created via the `start_server` method from the `Puma::Runner` class
|
16
|
+
# that this inherits from.
|
17
|
+
#
|
18
|
+
# An instance of this class will spawn the number of processes passed in
|
19
|
+
# via the `spawn_workers` method call. Each worker will have it's own
|
20
|
+
# instance of a `Puma::Server`.
|
8
21
|
class Cluster < Runner
|
9
22
|
WORKER_CHECK_INTERVAL = 5
|
10
23
|
|
@@ -24,7 +37,7 @@ module Puma
|
|
24
37
|
@workers.each { |x| x.term }
|
25
38
|
|
26
39
|
begin
|
27
|
-
Process.
|
40
|
+
@workers.each { |w| Process.waitpid(w.pid) }
|
28
41
|
rescue Interrupt
|
29
42
|
log "! Cancelled waiting for workers"
|
30
43
|
end
|
@@ -224,12 +237,13 @@ module Puma
|
|
224
237
|
begin
|
225
238
|
@wakeup.write "!" unless @wakeup.closed?
|
226
239
|
rescue SystemCallError, IOError
|
240
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
227
241
|
end
|
228
242
|
end
|
229
243
|
|
230
244
|
def worker(index, master)
|
231
|
-
title
|
232
|
-
title
|
245
|
+
title = "puma: cluster worker #{index}: #{master}"
|
246
|
+
title += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
|
233
247
|
$0 = title
|
234
248
|
|
235
249
|
Signal.trap "SIGINT", "IGNORE"
|
@@ -267,6 +281,7 @@ module Puma
|
|
267
281
|
begin
|
268
282
|
@worker_write << "b#{Process.pid}\n"
|
269
283
|
rescue SystemCallError, IOError
|
284
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
270
285
|
STDERR.puts "Master seems to have exited, exiting."
|
271
286
|
return
|
272
287
|
end
|
@@ -277,11 +292,14 @@ module Puma
|
|
277
292
|
while true
|
278
293
|
sleep WORKER_CHECK_INTERVAL
|
279
294
|
begin
|
280
|
-
b = server.backlog
|
281
|
-
r = server.running
|
282
|
-
|
295
|
+
b = server.backlog || 0
|
296
|
+
r = server.running || 0
|
297
|
+
t = server.pool_capacity || 0
|
298
|
+
m = server.max_threads || 0
|
299
|
+
payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads": #{m} }\n!
|
283
300
|
io << payload
|
284
301
|
rescue IOError
|
302
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
285
303
|
break
|
286
304
|
end
|
287
305
|
end
|
@@ -337,7 +355,7 @@ module Puma
|
|
337
355
|
def stats
|
338
356
|
old_worker_count = @workers.count { |w| w.phase != @phase }
|
339
357
|
booted_worker_count = @workers.count { |w| w.booted? }
|
340
|
-
worker_status = '[' + @workers.map{ |w| %Q!{ "pid": #{w.pid}, "index": #{w.index}, "phase": #{w.phase}, "booted": #{w.booted?}, "last_checkin": "#{w.last_checkin.utc.iso8601}", "last_status": #{w.last_status} }!}.join(",") + ']'
|
358
|
+
worker_status = '[' + @workers.map { |w| %Q!{ "pid": #{w.pid}, "index": #{w.index}, "phase": #{w.phase}, "booted": #{w.booted?}, "last_checkin": "#{w.last_checkin.utc.iso8601}", "last_status": #{w.last_status} }!}.join(",") + ']'
|
341
359
|
%Q!{ "workers": #{@workers.size}, "phase": #{@phase}, "booted_workers": #{booted_worker_count}, "old_workers": #{old_worker_count}, "worker_status": #{worker_status} }!
|
342
360
|
end
|
343
361
|
|
@@ -372,7 +390,10 @@ module Puma
|
|
372
390
|
log "Early termination of worker"
|
373
391
|
exit! 0
|
374
392
|
else
|
393
|
+
stop_workers
|
375
394
|
stop
|
395
|
+
|
396
|
+
raise SignalException, "SIGTERM"
|
376
397
|
end
|
377
398
|
end
|
378
399
|
end
|
data/lib/puma/commonlogger.rb
CHANGED
data/lib/puma/configuration.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'puma/rack/builder'
|
2
4
|
require 'puma/plugin'
|
3
5
|
require 'puma/const'
|
@@ -180,30 +182,31 @@ module Puma
|
|
180
182
|
:worker_shutdown_timeout => DefaultWorkerShutdownTimeout,
|
181
183
|
:remote_address => :socket,
|
182
184
|
:tag => method(:infer_tag),
|
183
|
-
:environment => ->{ ENV['RACK_ENV'] || "development" },
|
185
|
+
:environment => -> { ENV['RACK_ENV'] || "development" },
|
184
186
|
:rackup => DefaultRackup,
|
185
187
|
:logger => STDOUT,
|
186
|
-
:persistent_timeout => Const::PERSISTENT_TIMEOUT
|
188
|
+
:persistent_timeout => Const::PERSISTENT_TIMEOUT,
|
189
|
+
:first_data_timeout => Const::FIRST_DATA_TIMEOUT
|
187
190
|
}
|
188
191
|
end
|
189
192
|
|
190
193
|
def load
|
194
|
+
config_files.each { |config_file| @file_dsl._load_from(config_file) }
|
195
|
+
|
196
|
+
@options
|
197
|
+
end
|
198
|
+
|
199
|
+
def config_files
|
191
200
|
files = @options.all_of(:config_files)
|
192
201
|
|
193
|
-
if files
|
194
|
-
|
195
|
-
File.exist?(f)
|
196
|
-
}
|
202
|
+
return [] if files == ['-']
|
203
|
+
return files if files.any?
|
197
204
|
|
198
|
-
|
199
|
-
|
200
|
-
files = []
|
205
|
+
first_default_file = %W(config/puma/#{environment_str}.rb config/puma.rb).find do |f|
|
206
|
+
File.exist?(f)
|
201
207
|
end
|
202
208
|
|
203
|
-
|
204
|
-
@file_dsl._load_from(f)
|
205
|
-
end
|
206
|
-
@options
|
209
|
+
[first_default_file]
|
207
210
|
end
|
208
211
|
|
209
212
|
# Call once all configuration (included from rackup files)
|
@@ -263,6 +266,10 @@ module Puma
|
|
263
266
|
@options[:environment]
|
264
267
|
end
|
265
268
|
|
269
|
+
def environment_str
|
270
|
+
environment.respond_to?(:call) ? environment.call : environment
|
271
|
+
end
|
272
|
+
|
266
273
|
def load_plugin(name)
|
267
274
|
@plugins.create name
|
268
275
|
end
|
@@ -340,7 +347,7 @@ module Puma
|
|
340
347
|
end
|
341
348
|
|
342
349
|
if bytes
|
343
|
-
token = ""
|
350
|
+
token = "".dup
|
344
351
|
bytes.each_byte { |b| token << b.to_s(16) }
|
345
352
|
else
|
346
353
|
token = (0..count).to_a.map { rand(255).to_s(16) }.join
|
data/lib/puma/const.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
#encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
module Puma
|
3
5
|
class UnsupportedOption < RuntimeError
|
4
6
|
end
|
@@ -53,6 +55,8 @@ module Puma
|
|
53
55
|
415 => 'Unsupported Media Type',
|
54
56
|
416 => 'Range Not Satisfiable',
|
55
57
|
417 => 'Expectation Failed',
|
58
|
+
418 => 'I\'m A Teapot',
|
59
|
+
421 => 'Misdirected Request',
|
56
60
|
422 => 'Unprocessable Entity',
|
57
61
|
423 => 'Locked',
|
58
62
|
424 => 'Failed Dependency',
|
@@ -60,6 +64,7 @@ module Puma
|
|
60
64
|
428 => 'Precondition Required',
|
61
65
|
429 => 'Too Many Requests',
|
62
66
|
431 => 'Request Header Fields Too Large',
|
67
|
+
451 => 'Unavailable For Legal Reasons',
|
63
68
|
500 => 'Internal Server Error',
|
64
69
|
501 => 'Not Implemented',
|
65
70
|
502 => 'Bad Gateway',
|
@@ -95,8 +100,8 @@ module Puma
|
|
95
100
|
# too taxing on performance.
|
96
101
|
module Const
|
97
102
|
|
98
|
-
PUMA_VERSION = VERSION = "3.
|
99
|
-
CODE_NAME = "
|
103
|
+
PUMA_VERSION = VERSION = "3.12.6".freeze
|
104
|
+
CODE_NAME = "Llamas in Pajamas".freeze
|
100
105
|
PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
|
101
106
|
|
102
107
|
FAST_TRACK_KA_TIMEOUT = 0.2
|
@@ -113,6 +118,13 @@ module Puma
|
|
113
118
|
# sending data back
|
114
119
|
WRITE_TIMEOUT = 10
|
115
120
|
|
121
|
+
# How many requests to attempt inline before sending a client back to
|
122
|
+
# the reactor to be subject to normal ordering. The idea here is that
|
123
|
+
# we amortize the cost of going back to the reactor for a well behaved
|
124
|
+
# but very "greedy" client across 10 requests. This prevents a not
|
125
|
+
# well behaved client from monopolizing the thread forever.
|
126
|
+
MAX_FAST_INLINE = 10
|
127
|
+
|
116
128
|
# The original URI requested by the client.
|
117
129
|
REQUEST_URI= 'REQUEST_URI'.freeze
|
118
130
|
REQUEST_PATH = 'REQUEST_PATH'.freeze
|
@@ -216,9 +228,12 @@ module Puma
|
|
216
228
|
COLON = ": ".freeze
|
217
229
|
|
218
230
|
NEWLINE = "\n".freeze
|
231
|
+
HTTP_INJECTION_REGEX = /[\r\n]/.freeze
|
219
232
|
|
220
233
|
HIJACK_P = "rack.hijack?".freeze
|
221
234
|
HIJACK = "rack.hijack".freeze
|
222
235
|
HIJACK_IO = "rack.hijack_io".freeze
|
236
|
+
|
237
|
+
EARLY_HINTS = "rack.early_hints".freeze
|
223
238
|
end
|
224
239
|
end
|