puma 6.0.0 → 6.4.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 +184 -6
- data/LICENSE +0 -0
- data/README.md +58 -13
- data/bin/puma-wild +0 -0
- data/docs/architecture.md +0 -0
- data/docs/compile_options.md +0 -0
- data/docs/deployment.md +0 -0
- data/docs/fork_worker.md +0 -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 +0 -0
- data/docs/jungle/rc.d/puma.conf +0 -0
- data/docs/kubernetes.md +12 -0
- data/docs/nginx.md +1 -1
- data/docs/plugins.md +0 -0
- data/docs/rails_dev_mode.md +0 -0
- data/docs/restart.md +0 -0
- data/docs/signals.md +0 -0
- data/docs/stats.md +0 -0
- data/docs/systemd.md +1 -2
- data/docs/testing_benchmarks_local_files.md +0 -0
- data/docs/testing_test_rackup_ci_files.md +0 -0
- data/ext/puma_http11/PumaHttp11Service.java +0 -0
- data/ext/puma_http11/ext_help.h +0 -0
- data/ext/puma_http11/extconf.rb +0 -0
- data/ext/puma_http11/http11_parser.c +0 -0
- data/ext/puma_http11/http11_parser.h +0 -0
- data/ext/puma_http11/http11_parser.java.rl +0 -0
- data/ext/puma_http11/http11_parser.rl +0 -0
- data/ext/puma_http11/http11_parser_common.rl +0 -0
- data/ext/puma_http11/mini_ssl.c +91 -8
- 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 +0 -0
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +0 -0
- data/ext/puma_http11/puma_http11.c +0 -0
- data/lib/puma/app/status.rb +1 -1
- data/lib/puma/binder.rb +14 -11
- data/lib/puma/cli.rb +5 -1
- data/lib/puma/client.rb +53 -16
- data/lib/puma/cluster/worker.rb +5 -0
- data/lib/puma/cluster/worker_handle.rb +0 -0
- data/lib/puma/cluster.rb +5 -5
- data/lib/puma/commonlogger.rb +21 -14
- data/lib/puma/configuration.rb +5 -1
- data/lib/puma/const.rb +129 -88
- data/lib/puma/control_cli.rb +12 -5
- data/lib/puma/detect.rb +2 -0
- data/lib/puma/dsl.rb +147 -7
- data/lib/puma/error_logger.rb +2 -1
- data/lib/puma/events.rb +0 -0
- data/lib/puma/io_buffer.rb +10 -0
- data/lib/puma/jruby_restart.rb +0 -0
- data/lib/puma/json_serialization.rb +0 -0
- data/lib/puma/launcher/bundle_pruner.rb +0 -0
- data/lib/puma/launcher.rb +9 -22
- data/lib/puma/log_writer.rb +14 -4
- data/lib/puma/minissl/context_builder.rb +1 -0
- data/lib/puma/minissl.rb +17 -0
- data/lib/puma/plugin/systemd.rb +90 -0
- data/lib/puma/plugin/tmp_restart.rb +0 -0
- data/lib/puma/plugin.rb +0 -0
- data/lib/puma/rack/builder.rb +2 -2
- data/lib/puma/rack/urlmap.rb +1 -1
- data/lib/puma/rack_default.rb +18 -3
- data/lib/puma/reactor.rb +17 -8
- data/lib/puma/request.rb +189 -125
- data/lib/puma/runner.rb +16 -1
- data/lib/puma/sd_notify.rb +149 -0
- data/lib/puma/server.rb +74 -34
- data/lib/puma/single.rb +3 -1
- data/lib/puma/state_file.rb +0 -0
- data/lib/puma/thread_pool.rb +42 -7
- data/lib/puma/util.rb +0 -0
- data/lib/puma.rb +1 -3
- data/lib/rack/handler/puma.rb +113 -86
- data/tools/Dockerfile +0 -0
- data/tools/trickletest.rb +0 -0
- metadata +4 -3
- data/lib/puma/systemd.rb +0 -47
data/ext/puma_http11/mini_ssl.c
CHANGED
@@ -36,6 +36,12 @@ void raise_file_error(const char* caller, const char *filename) {
|
|
36
36
|
rb_raise(eError, "%s: error in file '%s': %s", caller, filename, ERR_error_string(ERR_get_error(), NULL));
|
37
37
|
}
|
38
38
|
|
39
|
+
NORETURN(void raise_param_error(const char* caller, const char *param));
|
40
|
+
|
41
|
+
void raise_param_error(const char* caller, const char *param) {
|
42
|
+
rb_raise(eError, "%s: error with parameter '%s': %s", caller, param, ERR_error_string(ERR_get_error(), NULL));
|
43
|
+
}
|
44
|
+
|
39
45
|
void engine_free(void *ptr) {
|
40
46
|
ms_conn *conn = ptr;
|
41
47
|
ms_cert_buf* cert_buf = (ms_cert_buf*)SSL_get_app_data(conn->ssl);
|
@@ -185,6 +191,18 @@ static int engine_verify_callback(int preverify_ok, X509_STORE_CTX* ctx) {
|
|
185
191
|
return preverify_ok;
|
186
192
|
}
|
187
193
|
|
194
|
+
static int password_callback(char *buf, int size, int rwflag, void *userdata) {
|
195
|
+
const char *password = (const char *) userdata;
|
196
|
+
size_t len = strlen(password);
|
197
|
+
|
198
|
+
if (len > (size_t) size) {
|
199
|
+
return 0;
|
200
|
+
}
|
201
|
+
|
202
|
+
memcpy(buf, password, len);
|
203
|
+
return (int) len;
|
204
|
+
}
|
205
|
+
|
188
206
|
static VALUE
|
189
207
|
sslctx_alloc(VALUE klass) {
|
190
208
|
SSL_CTX *ctx;
|
@@ -212,10 +230,12 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
|
|
212
230
|
SSL_CTX* ctx;
|
213
231
|
int ssl_options;
|
214
232
|
VALUE key, cert, ca, verify_mode, ssl_cipher_filter, no_tlsv1, no_tlsv1_1,
|
215
|
-
verification_flags, session_id_bytes, cert_pem, key_pem;
|
233
|
+
verification_flags, session_id_bytes, cert_pem, key_pem, key_password_command, key_password;
|
216
234
|
BIO *bio;
|
217
|
-
X509 *x509;
|
235
|
+
X509 *x509 = NULL;
|
218
236
|
EVP_PKEY *pkey;
|
237
|
+
pem_password_cb *password_cb = NULL;
|
238
|
+
const char *password = NULL;
|
219
239
|
#ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
|
220
240
|
int min;
|
221
241
|
#endif
|
@@ -235,6 +255,8 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
|
|
235
255
|
|
236
256
|
key = rb_funcall(mini_ssl_ctx, rb_intern_const("key"), 0);
|
237
257
|
|
258
|
+
key_password_command = rb_funcall(mini_ssl_ctx, rb_intern_const("key_password_command"), 0);
|
259
|
+
|
238
260
|
cert = rb_funcall(mini_ssl_ctx, rb_intern_const("cert"), 0);
|
239
261
|
|
240
262
|
ca = rb_funcall(mini_ssl_ctx, rb_intern_const("ca"), 0);
|
@@ -261,6 +283,18 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
|
|
261
283
|
}
|
262
284
|
}
|
263
285
|
|
286
|
+
if (!NIL_P(key_password_command)) {
|
287
|
+
key_password = rb_funcall(mini_ssl_ctx, rb_intern_const("key_password"), 0);
|
288
|
+
|
289
|
+
if (!NIL_P(key_password)) {
|
290
|
+
StringValue(key_password);
|
291
|
+
password_cb = password_callback;
|
292
|
+
password = RSTRING_PTR(key_password);
|
293
|
+
SSL_CTX_set_default_passwd_cb(ctx, password_cb);
|
294
|
+
SSL_CTX_set_default_passwd_cb_userdata(ctx, (void *) password);
|
295
|
+
}
|
296
|
+
}
|
297
|
+
|
264
298
|
if (!NIL_P(key)) {
|
265
299
|
StringValue(key);
|
266
300
|
|
@@ -270,22 +304,71 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
|
|
270
304
|
}
|
271
305
|
|
272
306
|
if (!NIL_P(cert_pem)) {
|
307
|
+
X509 *ca = NULL;
|
308
|
+
unsigned long err;
|
309
|
+
|
273
310
|
bio = BIO_new(BIO_s_mem());
|
274
311
|
BIO_puts(bio, RSTRING_PTR(cert_pem));
|
312
|
+
|
313
|
+
/**
|
314
|
+
* Much of this pulled as a simplified version of the `use_certificate_chain_file` method
|
315
|
+
* from openssl's `ssl_rsa.c` file.
|
316
|
+
*/
|
317
|
+
|
318
|
+
/* first read the cert as the first item in the pem file */
|
275
319
|
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
320
|
+
if (NULL == x509) {
|
321
|
+
BIO_free_all(bio);
|
322
|
+
raise_param_error("PEM_read_bio_X509", "cert_pem");
|
323
|
+
}
|
276
324
|
|
277
|
-
|
278
|
-
|
279
|
-
|
325
|
+
/* Add the cert to the context */
|
326
|
+
/* 1 is success - otherwise check the error codes */
|
327
|
+
if (1 != SSL_CTX_use_certificate(ctx, x509)) {
|
328
|
+
BIO_free_all(bio);
|
329
|
+
raise_param_error("SSL_CTX_use_certificate", "cert_pem");
|
330
|
+
}
|
331
|
+
|
332
|
+
X509_free(x509); /* no longer need our reference */
|
333
|
+
|
334
|
+
/* Now lets load up the rest of the certificate chain */
|
335
|
+
/* 1 is success 0 is error */
|
336
|
+
if (0 == SSL_CTX_clear_chain_certs(ctx)) {
|
337
|
+
BIO_free_all(bio);
|
338
|
+
raise_param_error("SSL_CTX_clear_chain_certs","cert_pem");
|
339
|
+
}
|
340
|
+
|
341
|
+
while (1) {
|
342
|
+
ca = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
343
|
+
|
344
|
+
if (NULL == ca) {
|
345
|
+
break;
|
346
|
+
}
|
347
|
+
|
348
|
+
if (0 == SSL_CTX_add0_chain_cert(ctx, ca)) {
|
349
|
+
BIO_free_all(bio);
|
350
|
+
raise_param_error("SSL_CTX_add0_chain_cert","cert_pem");
|
351
|
+
}
|
352
|
+
/* don't free ca - its now owned by the context */
|
353
|
+
}
|
354
|
+
|
355
|
+
/* ca is NULL - so its either the end of the file or an error */
|
356
|
+
err = ERR_peek_last_error();
|
357
|
+
|
358
|
+
/* If its the end of the file - then we are done, in any case free the bio */
|
359
|
+
BIO_free_all(bio);
|
360
|
+
|
361
|
+
if ((ERR_GET_LIB(err) == ERR_LIB_PEM) && (ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) {
|
362
|
+
ERR_clear_error();
|
363
|
+
} else {
|
364
|
+
raise_param_error("PEM_read_bio_X509","cert_pem");
|
280
365
|
}
|
281
|
-
X509_free(x509);
|
282
|
-
BIO_free(bio);
|
283
366
|
}
|
284
367
|
|
285
368
|
if (!NIL_P(key_pem)) {
|
286
369
|
bio = BIO_new(BIO_s_mem());
|
287
370
|
BIO_puts(bio, RSTRING_PTR(key_pem));
|
288
|
-
pkey = PEM_read_bio_PrivateKey(bio, NULL,
|
371
|
+
pkey = PEM_read_bio_PrivateKey(bio, NULL, password_cb, (void *) password);
|
289
372
|
|
290
373
|
if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1) {
|
291
374
|
BIO_free(bio);
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/lib/puma/app/status.rb
CHANGED
@@ -80,7 +80,7 @@ module Puma
|
|
80
80
|
|
81
81
|
def authenticate(env)
|
82
82
|
return true unless @auth_token
|
83
|
-
env['QUERY_STRING'].to_s.split(
|
83
|
+
env['QUERY_STRING'].to_s.split('&;').include? "token=#{@auth_token}"
|
84
84
|
end
|
85
85
|
|
86
86
|
def rack_response(status, body, content_type='application/json')
|
data/lib/puma/binder.rb
CHANGED
@@ -48,7 +48,6 @@ module Puma
|
|
48
48
|
|
49
49
|
@envs = {}
|
50
50
|
@ios = []
|
51
|
-
localhost_authority
|
52
51
|
end
|
53
52
|
|
54
53
|
attr_reader :ios
|
@@ -158,10 +157,10 @@ module Puma
|
|
158
157
|
ios_len = @ios.length
|
159
158
|
params = Util.parse_query uri.query
|
160
159
|
|
161
|
-
|
160
|
+
low_latency = params.key?('low_latency') && params['low_latency'] != 'false'
|
162
161
|
backlog = params.fetch('backlog', 1024).to_i
|
163
162
|
|
164
|
-
io = add_tcp_listener uri.host, uri.port,
|
163
|
+
io = add_tcp_listener uri.host, uri.port, low_latency, backlog
|
165
164
|
|
166
165
|
@ios[ios_len..-1].each do |i|
|
167
166
|
addr = loc_addr_str i
|
@@ -251,7 +250,8 @@ module Puma
|
|
251
250
|
else
|
252
251
|
ios_len = @ios.length
|
253
252
|
backlog = params.fetch('backlog', 1024).to_i
|
254
|
-
|
253
|
+
low_latency = params['low_latency'] != 'false'
|
254
|
+
io = add_ssl_listener uri.host, uri.port, ctx, low_latency, backlog
|
255
255
|
|
256
256
|
@ios[ios_len..-1].each do |i|
|
257
257
|
addr = loc_addr_str i
|
@@ -330,7 +330,7 @@ module Puma
|
|
330
330
|
return
|
331
331
|
end
|
332
332
|
|
333
|
-
host = host[1..-2] if host
|
333
|
+
host = host[1..-2] if host&.start_with? '['
|
334
334
|
tcp_server = TCPServer.new(host, port)
|
335
335
|
|
336
336
|
if optimize_for_latency
|
@@ -364,7 +364,7 @@ module Puma
|
|
364
364
|
return
|
365
365
|
end
|
366
366
|
|
367
|
-
host = host[1..-2] if host
|
367
|
+
host = host[1..-2] if host&.start_with? '['
|
368
368
|
s = TCPServer.new(host, port)
|
369
369
|
if optimize_for_latency
|
370
370
|
s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
@@ -450,11 +450,14 @@ module Puma
|
|
450
450
|
|
451
451
|
def close_listeners
|
452
452
|
@listeners.each do |l, io|
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
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
|
458
461
|
end
|
459
462
|
end
|
460
463
|
|
data/lib/puma/cli.rb
CHANGED
@@ -93,7 +93,7 @@ module Puma
|
|
93
93
|
#
|
94
94
|
|
95
95
|
def setup_options
|
96
|
-
@conf = Configuration.new do |user_config, file_config|
|
96
|
+
@conf = Configuration.new({}, {events: @events}) do |user_config, file_config|
|
97
97
|
@parser = OptionParser.new do |o|
|
98
98
|
o.on "-b", "--bind URI", "URI to bind to (tcp://, unix://, ssl://)" do |arg|
|
99
99
|
user_config.bind arg
|
@@ -144,6 +144,10 @@ module Puma
|
|
144
144
|
$LOAD_PATH.unshift(*arg.split(':'))
|
145
145
|
end
|
146
146
|
|
147
|
+
o.on "--idle-timeout SECONDS", "Number of seconds until the next request before automatic shutdown" do |arg|
|
148
|
+
user_config.idle_timeout arg
|
149
|
+
end
|
150
|
+
|
147
151
|
o.on "-p", "--port PORT", "Define the TCP port to bind to",
|
148
152
|
"Use -b for more advanced options" do |arg|
|
149
153
|
user_config.bind "tcp://#{Configuration::DEFAULTS[:tcp_host]}:#{arg}"
|
data/lib/puma/client.rb
CHANGED
@@ -9,8 +9,8 @@ class IO
|
|
9
9
|
end
|
10
10
|
|
11
11
|
require_relative 'detect'
|
12
|
+
require_relative 'io_buffer'
|
12
13
|
require 'tempfile'
|
13
|
-
require 'forwardable'
|
14
14
|
|
15
15
|
if Puma::IS_JRUBY
|
16
16
|
# We have to work around some OpenSSL buffer/io-readiness bugs
|
@@ -48,7 +48,8 @@ module Puma
|
|
48
48
|
|
49
49
|
# chunked body validation
|
50
50
|
CHUNK_SIZE_INVALID = /[^\h]/.freeze
|
51
|
-
CHUNK_VALID_ENDING =
|
51
|
+
CHUNK_VALID_ENDING = Const::LINE_END
|
52
|
+
CHUNK_VALID_ENDING_SIZE = CHUNK_VALID_ENDING.bytesize
|
52
53
|
|
53
54
|
# Content-Length header value validation
|
54
55
|
CONTENT_LENGTH_VALUE_INVALID = /[^\d]/.freeze
|
@@ -60,13 +61,13 @@ module Puma
|
|
60
61
|
EmptyBody = NullIO.new
|
61
62
|
|
62
63
|
include Puma::Const
|
63
|
-
extend Forwardable
|
64
64
|
|
65
65
|
def initialize(io, env=nil)
|
66
66
|
@io = io
|
67
67
|
@to_io = io.to_io
|
68
|
+
@io_buffer = IOBuffer.new
|
68
69
|
@proto_env = env
|
69
|
-
@env = env
|
70
|
+
@env = env&.dup
|
70
71
|
|
71
72
|
@parser = HttpParser.new
|
72
73
|
@parsed_bytes = 0
|
@@ -84,6 +85,9 @@ module Puma
|
|
84
85
|
@requests_served = 0
|
85
86
|
@hijacked = false
|
86
87
|
|
88
|
+
@http_content_length_limit = nil
|
89
|
+
@http_content_length_limit_exceeded = false
|
90
|
+
|
87
91
|
@peerip = nil
|
88
92
|
@peer_family = nil
|
89
93
|
@listener = nil
|
@@ -93,16 +97,22 @@ module Puma
|
|
93
97
|
@body_remain = 0
|
94
98
|
|
95
99
|
@in_last_chunk = false
|
100
|
+
|
101
|
+
# need unfrozen ASCII-8BIT, +'' is UTF-8
|
102
|
+
@read_buffer = String.new # rubocop: disable Performance/UnfreezeString
|
96
103
|
end
|
97
104
|
|
98
105
|
attr_reader :env, :to_io, :body, :io, :timeout_at, :ready, :hijacked,
|
99
|
-
:tempfile
|
106
|
+
:tempfile, :io_buffer, :http_content_length_limit_exceeded
|
100
107
|
|
101
|
-
attr_writer :peerip
|
108
|
+
attr_writer :peerip, :http_content_length_limit
|
102
109
|
|
103
110
|
attr_accessor :remote_addr_header, :listener
|
104
111
|
|
105
|
-
|
112
|
+
# Remove in Puma 7?
|
113
|
+
def closed?
|
114
|
+
@to_io.closed?
|
115
|
+
end
|
106
116
|
|
107
117
|
# Test to see if io meets a bare minimum of functioning, @to_io needs to be
|
108
118
|
# used for MiniSSL::Socket
|
@@ -138,6 +148,7 @@ module Puma
|
|
138
148
|
|
139
149
|
def reset(fast_check=true)
|
140
150
|
@parser.reset
|
151
|
+
@io_buffer.reset
|
141
152
|
@read_header = true
|
142
153
|
@read_proxy = !!@expect_proxy_proto
|
143
154
|
@env = @proto_env.dup
|
@@ -148,6 +159,7 @@ module Puma
|
|
148
159
|
@body_remain = 0
|
149
160
|
@peerip = nil if @remote_addr_header
|
150
161
|
@in_last_chunk = false
|
162
|
+
@http_content_length_limit_exceeded = false
|
151
163
|
|
152
164
|
if @buffer
|
153
165
|
return false unless try_to_parse_proxy_protocol
|
@@ -207,6 +219,17 @@ module Puma
|
|
207
219
|
end
|
208
220
|
|
209
221
|
def try_to_finish
|
222
|
+
if env[CONTENT_LENGTH] && above_http_content_limit(env[CONTENT_LENGTH].to_i)
|
223
|
+
@http_content_length_limit_exceeded = true
|
224
|
+
end
|
225
|
+
|
226
|
+
if @http_content_length_limit_exceeded
|
227
|
+
@buffer = nil
|
228
|
+
@body = EmptyBody
|
229
|
+
set_ready
|
230
|
+
return true
|
231
|
+
end
|
232
|
+
|
210
233
|
return read_body if in_data_phase
|
211
234
|
|
212
235
|
begin
|
@@ -236,6 +259,10 @@ module Puma
|
|
236
259
|
|
237
260
|
@parsed_bytes = @parser.execute(@env, @buffer, @parsed_bytes)
|
238
261
|
|
262
|
+
if @parser.finished? && above_http_content_limit(@parser.body.bytesize)
|
263
|
+
@http_content_length_limit_exceeded = true
|
264
|
+
end
|
265
|
+
|
239
266
|
if @parser.finished?
|
240
267
|
return setup_body
|
241
268
|
elsif @parsed_bytes >= MAX_HEADER
|
@@ -357,8 +384,8 @@ module Puma
|
|
357
384
|
cl = @env[CONTENT_LENGTH]
|
358
385
|
|
359
386
|
if cl
|
360
|
-
# cannot contain characters that are not \d
|
361
|
-
if CONTENT_LENGTH_VALUE_INVALID.match? cl
|
387
|
+
# cannot contain characters that are not \d, or be empty
|
388
|
+
if CONTENT_LENGTH_VALUE_INVALID.match?(cl) || cl.empty?
|
362
389
|
raise HttpParserError, "Invalid Content-Length: #{cl.inspect}"
|
363
390
|
end
|
364
391
|
else
|
@@ -411,7 +438,7 @@ module Puma
|
|
411
438
|
end
|
412
439
|
|
413
440
|
begin
|
414
|
-
chunk = @io.read_nonblock(want)
|
441
|
+
chunk = @io.read_nonblock(want, @read_buffer)
|
415
442
|
rescue IO::WaitReadable
|
416
443
|
return false
|
417
444
|
rescue SystemCallError, IOError
|
@@ -443,7 +470,7 @@ module Puma
|
|
443
470
|
def read_chunked_body
|
444
471
|
while true
|
445
472
|
begin
|
446
|
-
chunk = @io.read_nonblock(4096)
|
473
|
+
chunk = @io.read_nonblock(4096, @read_buffer)
|
447
474
|
rescue IO::WaitReadable
|
448
475
|
return false
|
449
476
|
rescue SystemCallError, IOError
|
@@ -519,7 +546,7 @@ module Puma
|
|
519
546
|
|
520
547
|
while !io.eof?
|
521
548
|
line = io.gets
|
522
|
-
if line.end_with?(
|
549
|
+
if line.end_with?(CHUNK_VALID_ENDING)
|
523
550
|
# Puma doesn't process chunk extensions, but should parse if they're
|
524
551
|
# present, which is the reason for the semicolon regex
|
525
552
|
chunk_hex = line.strip[/\A[^;]+/]
|
@@ -531,13 +558,19 @@ module Puma
|
|
531
558
|
@in_last_chunk = true
|
532
559
|
@body.rewind
|
533
560
|
rest = io.read
|
534
|
-
|
535
|
-
if rest.bytesize < last_crlf_size
|
561
|
+
if rest.bytesize < CHUNK_VALID_ENDING_SIZE
|
536
562
|
@buffer = nil
|
537
|
-
@partial_part_left =
|
563
|
+
@partial_part_left = CHUNK_VALID_ENDING_SIZE - rest.bytesize
|
538
564
|
return false
|
539
565
|
else
|
540
|
-
|
566
|
+
# if the next character is a CRLF, set buffer to everything after that CRLF
|
567
|
+
start_of_rest = if rest.start_with?(CHUNK_VALID_ENDING)
|
568
|
+
CHUNK_VALID_ENDING_SIZE
|
569
|
+
else # we have started a trailer section, which we do not support. skip it!
|
570
|
+
rest.index(CHUNK_VALID_ENDING*2) + CHUNK_VALID_ENDING_SIZE*2
|
571
|
+
end
|
572
|
+
|
573
|
+
@buffer = rest[start_of_rest..-1]
|
541
574
|
@buffer = nil if @buffer.empty?
|
542
575
|
set_ready
|
543
576
|
return true
|
@@ -591,5 +624,9 @@ module Puma
|
|
591
624
|
@requests_served += 1
|
592
625
|
@ready = true
|
593
626
|
end
|
627
|
+
|
628
|
+
def above_http_content_limit(value)
|
629
|
+
@http_content_length_limit&.< value
|
630
|
+
end
|
594
631
|
end
|
595
632
|
end
|
data/lib/puma/cluster/worker.rb
CHANGED
@@ -115,6 +115,11 @@ module Puma
|
|
115
115
|
|
116
116
|
while restart_server.pop
|
117
117
|
server_thread = server.run
|
118
|
+
|
119
|
+
if @log_writer.debug? && index == 0
|
120
|
+
debug_loaded_extensions "Loaded Extensions - worker 0:"
|
121
|
+
end
|
122
|
+
|
118
123
|
stat_thread ||= Thread.new(@worker_write) do |io|
|
119
124
|
Puma.set_thread_name "stat pld"
|
120
125
|
base_payload = "p#{Process.pid}"
|
File without changes
|
data/lib/puma/cluster.rb
CHANGED
@@ -6,8 +6,6 @@ require_relative 'plugin'
|
|
6
6
|
require_relative 'cluster/worker_handle'
|
7
7
|
require_relative 'cluster/worker'
|
8
8
|
|
9
|
-
require 'time'
|
10
|
-
|
11
9
|
module Puma
|
12
10
|
# This class is instantiated by the `Puma::Launcher` and used
|
13
11
|
# to boot and serve a Ruby application when puma "workers" are needed
|
@@ -252,18 +250,18 @@ module Puma
|
|
252
250
|
old_worker_count = @workers.count { |w| w.phase != @phase }
|
253
251
|
worker_status = @workers.map do |w|
|
254
252
|
{
|
255
|
-
started_at: w.started_at
|
253
|
+
started_at: utc_iso8601(w.started_at),
|
256
254
|
pid: w.pid,
|
257
255
|
index: w.index,
|
258
256
|
phase: w.phase,
|
259
257
|
booted: w.booted?,
|
260
|
-
last_checkin: w.last_checkin
|
258
|
+
last_checkin: utc_iso8601(w.last_checkin),
|
261
259
|
last_status: w.last_status,
|
262
260
|
}
|
263
261
|
end
|
264
262
|
|
265
263
|
{
|
266
|
-
started_at: @started_at
|
264
|
+
started_at: utc_iso8601(@started_at),
|
267
265
|
workers: @workers.size,
|
268
266
|
phase: @phase,
|
269
267
|
booted_workers: worker_status.count { |w| w[:booted] },
|
@@ -469,6 +467,7 @@ module Puma
|
|
469
467
|
@events.fire(:ping!, w)
|
470
468
|
if !booted && @workers.none? {|worker| worker.last_status.empty?}
|
471
469
|
@events.fire_on_booted!
|
470
|
+
debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
|
472
471
|
booted = true
|
473
472
|
end
|
474
473
|
end
|
@@ -478,6 +477,7 @@ module Puma
|
|
478
477
|
end
|
479
478
|
if in_phased_restart && workers_not_booted.zero?
|
480
479
|
@events.fire_on_booted!
|
480
|
+
debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
|
481
481
|
in_phased_restart = false
|
482
482
|
end
|
483
483
|
|
data/lib/puma/commonlogger.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module Puma
|
4
4
|
# Rack::CommonLogger forwards every request to the given +app+, and
|
5
5
|
# logs a line in the
|
6
|
-
# {Apache common log format}[https://httpd.apache.org/docs/
|
6
|
+
# {Apache common log format}[https://httpd.apache.org/docs/2.4/logs.html#common]
|
7
7
|
# to the +logger+.
|
8
8
|
#
|
9
9
|
# If +logger+ is nil, CommonLogger will fall back +rack.errors+, which is
|
@@ -16,7 +16,7 @@ module Puma
|
|
16
16
|
# (which is called without arguments in order to make the error appear for
|
17
17
|
# sure)
|
18
18
|
class CommonLogger
|
19
|
-
# Common Log Format: https://httpd.apache.org/docs/
|
19
|
+
# Common Log Format: https://httpd.apache.org/docs/2.4/logs.html#common
|
20
20
|
#
|
21
21
|
# lilith.local - - [07/Aug/2006 23:58:02 -0400] "GET / HTTP/1.1" 500 -
|
22
22
|
#
|
@@ -25,10 +25,17 @@ module Puma
|
|
25
25
|
|
26
26
|
HIJACK_FORMAT = %{%s - %s [%s] "%s %s%s %s" HIJACKED -1 %0.4f\n}
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
LOG_TIME_FORMAT = '%d/%b/%Y:%H:%M:%S %z'
|
29
|
+
|
30
|
+
CONTENT_LENGTH = 'Content-Length' # should be lower case from app,
|
31
|
+
# Util::HeaderHash allows mixed
|
32
|
+
HTTP_VERSION = Const::HTTP_VERSION
|
33
|
+
HTTP_X_FORWARDED_FOR = Const::HTTP_X_FORWARDED_FOR
|
34
|
+
PATH_INFO = Const::PATH_INFO
|
35
|
+
QUERY_STRING = Const::QUERY_STRING
|
36
|
+
REMOTE_ADDR = Const::REMOTE_ADDR
|
37
|
+
REMOTE_USER = 'REMOTE_USER'
|
38
|
+
REQUEST_METHOD = Const::REQUEST_METHOD
|
32
39
|
|
33
40
|
def initialize(app, logger=nil)
|
34
41
|
@app = app
|
@@ -57,13 +64,13 @@ module Puma
|
|
57
64
|
now = Time.now
|
58
65
|
|
59
66
|
msg = HIJACK_FORMAT % [
|
60
|
-
env[
|
61
|
-
env[
|
62
|
-
now.strftime(
|
67
|
+
env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR] || "-",
|
68
|
+
env[REMOTE_USER] || "-",
|
69
|
+
now.strftime(LOG_TIME_FORMAT),
|
63
70
|
env[REQUEST_METHOD],
|
64
71
|
env[PATH_INFO],
|
65
72
|
env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
|
66
|
-
env[
|
73
|
+
env[HTTP_VERSION],
|
67
74
|
now - began_at ]
|
68
75
|
|
69
76
|
write(msg)
|
@@ -74,13 +81,13 @@ module Puma
|
|
74
81
|
length = extract_content_length(header)
|
75
82
|
|
76
83
|
msg = FORMAT % [
|
77
|
-
env[
|
78
|
-
env[
|
79
|
-
now.strftime(
|
84
|
+
env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR] || "-",
|
85
|
+
env[REMOTE_USER] || "-",
|
86
|
+
now.strftime(LOG_TIME_FORMAT),
|
80
87
|
env[REQUEST_METHOD],
|
81
88
|
env[PATH_INFO],
|
82
89
|
env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
|
83
|
-
env[
|
90
|
+
env[HTTP_VERSION],
|
84
91
|
status.to_s[0..3],
|
85
92
|
length,
|
86
93
|
now - began_at ]
|
data/lib/puma/configuration.rb
CHANGED
@@ -133,8 +133,10 @@ module Puma
|
|
133
133
|
debug: false,
|
134
134
|
early_hints: nil,
|
135
135
|
environment: 'development'.freeze,
|
136
|
-
# Number of seconds to wait until we get the first data for the request
|
136
|
+
# Number of seconds to wait until we get the first data for the request.
|
137
137
|
first_data_timeout: 30,
|
138
|
+
# Number of seconds to wait until the next request before shutting down.
|
139
|
+
idle_timeout: nil,
|
138
140
|
io_selector_backend: :auto,
|
139
141
|
log_requests: false,
|
140
142
|
logger: STDOUT,
|
@@ -157,6 +159,7 @@ module Puma
|
|
157
159
|
reaping_time: 1,
|
158
160
|
remote_address: :socket,
|
159
161
|
silence_single_worker_warning: false,
|
162
|
+
silence_fork_callback_warning: false,
|
160
163
|
tag: File.basename(Dir.getwd),
|
161
164
|
tcp_host: '0.0.0.0'.freeze,
|
162
165
|
tcp_port: 9292,
|
@@ -167,6 +170,7 @@ module Puma
|
|
167
170
|
worker_shutdown_timeout: 30,
|
168
171
|
worker_timeout: 60,
|
169
172
|
workers: 0,
|
173
|
+
http_content_length_limit: nil
|
170
174
|
}
|
171
175
|
|
172
176
|
def initialize(user_options={}, default_options = {}, &block)
|