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.

Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +184 -6
  3. data/LICENSE +0 -0
  4. data/README.md +58 -13
  5. data/bin/puma-wild +0 -0
  6. data/docs/architecture.md +0 -0
  7. data/docs/compile_options.md +0 -0
  8. data/docs/deployment.md +0 -0
  9. data/docs/fork_worker.md +0 -0
  10. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  11. data/docs/images/puma-connection-flow.png +0 -0
  12. data/docs/images/puma-general-arch.png +0 -0
  13. data/docs/jungle/README.md +0 -0
  14. data/docs/jungle/rc.d/README.md +0 -0
  15. data/docs/jungle/rc.d/puma.conf +0 -0
  16. data/docs/kubernetes.md +12 -0
  17. data/docs/nginx.md +1 -1
  18. data/docs/plugins.md +0 -0
  19. data/docs/rails_dev_mode.md +0 -0
  20. data/docs/restart.md +0 -0
  21. data/docs/signals.md +0 -0
  22. data/docs/stats.md +0 -0
  23. data/docs/systemd.md +1 -2
  24. data/docs/testing_benchmarks_local_files.md +0 -0
  25. data/docs/testing_test_rackup_ci_files.md +0 -0
  26. data/ext/puma_http11/PumaHttp11Service.java +0 -0
  27. data/ext/puma_http11/ext_help.h +0 -0
  28. data/ext/puma_http11/extconf.rb +0 -0
  29. data/ext/puma_http11/http11_parser.c +0 -0
  30. data/ext/puma_http11/http11_parser.h +0 -0
  31. data/ext/puma_http11/http11_parser.java.rl +0 -0
  32. data/ext/puma_http11/http11_parser.rl +0 -0
  33. data/ext/puma_http11/http11_parser_common.rl +0 -0
  34. data/ext/puma_http11/mini_ssl.c +91 -8
  35. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +0 -0
  36. data/ext/puma_http11/org/jruby/puma/Http11.java +0 -0
  37. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +0 -0
  38. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +0 -0
  39. data/ext/puma_http11/puma_http11.c +0 -0
  40. data/lib/puma/app/status.rb +1 -1
  41. data/lib/puma/binder.rb +14 -11
  42. data/lib/puma/cli.rb +5 -1
  43. data/lib/puma/client.rb +53 -16
  44. data/lib/puma/cluster/worker.rb +5 -0
  45. data/lib/puma/cluster/worker_handle.rb +0 -0
  46. data/lib/puma/cluster.rb +5 -5
  47. data/lib/puma/commonlogger.rb +21 -14
  48. data/lib/puma/configuration.rb +5 -1
  49. data/lib/puma/const.rb +129 -88
  50. data/lib/puma/control_cli.rb +12 -5
  51. data/lib/puma/detect.rb +2 -0
  52. data/lib/puma/dsl.rb +147 -7
  53. data/lib/puma/error_logger.rb +2 -1
  54. data/lib/puma/events.rb +0 -0
  55. data/lib/puma/io_buffer.rb +10 -0
  56. data/lib/puma/jruby_restart.rb +0 -0
  57. data/lib/puma/json_serialization.rb +0 -0
  58. data/lib/puma/launcher/bundle_pruner.rb +0 -0
  59. data/lib/puma/launcher.rb +9 -22
  60. data/lib/puma/log_writer.rb +14 -4
  61. data/lib/puma/minissl/context_builder.rb +1 -0
  62. data/lib/puma/minissl.rb +17 -0
  63. data/lib/puma/plugin/systemd.rb +90 -0
  64. data/lib/puma/plugin/tmp_restart.rb +0 -0
  65. data/lib/puma/plugin.rb +0 -0
  66. data/lib/puma/rack/builder.rb +2 -2
  67. data/lib/puma/rack/urlmap.rb +1 -1
  68. data/lib/puma/rack_default.rb +18 -3
  69. data/lib/puma/reactor.rb +17 -8
  70. data/lib/puma/request.rb +189 -125
  71. data/lib/puma/runner.rb +16 -1
  72. data/lib/puma/sd_notify.rb +149 -0
  73. data/lib/puma/server.rb +74 -34
  74. data/lib/puma/single.rb +3 -1
  75. data/lib/puma/state_file.rb +0 -0
  76. data/lib/puma/thread_pool.rb +42 -7
  77. data/lib/puma/util.rb +0 -0
  78. data/lib/puma.rb +1 -3
  79. data/lib/rack/handler/puma.rb +113 -86
  80. data/tools/Dockerfile +0 -0
  81. data/tools/trickletest.rb +0 -0
  82. metadata +4 -3
  83. data/lib/puma/systemd.rb +0 -47
@@ -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
- if (SSL_CTX_use_certificate(ctx, x509) != 1) {
278
- BIO_free(bio);
279
- raise_file_error("SSL_CTX_use_certificate", RSTRING_PTR(cert_pem));
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, NULL, 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
@@ -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(/&;/).include?("token=#{@auth_token}")
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
- opt = params.key?('low_latency') && params['low_latency'] != 'false'
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, opt, backlog
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
- io = add_ssl_listener uri.host, uri.port, ctx, optimize_for_latency = true, backlog
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 and host[0..0] == '['
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[0..0] == '['
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
- io.close unless io.closed?
454
- uri = URI.parse l
455
- next unless uri.scheme == 'unix'
456
- unix_path = "#{uri.host}#{uri.path}"
457
- File.unlink unix_path if @unix_paths.include?(unix_path) && File.exist?(unix_path)
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 = "\r\n".freeze
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 ? env.dup : nil
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
- def_delegators :@io, :closed?
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?("\r\n")
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
- last_crlf_size = "\r\n".bytesize
535
- if rest.bytesize < last_crlf_size
561
+ if rest.bytesize < CHUNK_VALID_ENDING_SIZE
536
562
  @buffer = nil
537
- @partial_part_left = last_crlf_size - rest.bytesize
563
+ @partial_part_left = CHUNK_VALID_ENDING_SIZE - rest.bytesize
538
564
  return false
539
565
  else
540
- @buffer = rest[last_crlf_size..-1]
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
@@ -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.utc.iso8601,
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.utc.iso8601,
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.utc.iso8601,
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
 
@@ -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/1.3/logs.html#common]
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/1.3/logs.html#common
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
- CONTENT_LENGTH = 'Content-Length'.freeze
29
- PATH_INFO = 'PATH_INFO'.freeze
30
- QUERY_STRING = 'QUERY_STRING'.freeze
31
- REQUEST_METHOD = 'REQUEST_METHOD'.freeze
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['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
61
- env["REMOTE_USER"] || "-",
62
- now.strftime("%d/%b/%Y %H:%M:%S"),
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["HTTP_VERSION"],
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['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
78
- env["REMOTE_USER"] || "-",
79
- now.strftime("%d/%b/%Y:%H:%M:%S %z"),
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["HTTP_VERSION"],
90
+ env[HTTP_VERSION],
84
91
  status.to_s[0..3],
85
92
  length,
86
93
  now - began_at ]
@@ -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)