puma 6.2.2 → 6.3.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puma might be problematic. Click here for more details.

Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +33 -2
  3. data/LICENSE +0 -0
  4. data/README.md +24 -0
  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 +0 -0
  17. data/docs/nginx.md +0 -0
  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 +0 -0
  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 +30 -2
  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 +8 -6
  42. data/lib/puma/cli.rb +0 -0
  43. data/lib/puma/client.rb +3 -2
  44. data/lib/puma/cluster/worker.rb +0 -0
  45. data/lib/puma/cluster/worker_handle.rb +0 -0
  46. data/lib/puma/cluster.rb +0 -0
  47. data/lib/puma/commonlogger.rb +0 -0
  48. data/lib/puma/configuration.rb +0 -0
  49. data/lib/puma/const.rb +58 -9
  50. data/lib/puma/control_cli.rb +0 -0
  51. data/lib/puma/detect.rb +0 -0
  52. data/lib/puma/dsl.rb +34 -1
  53. data/lib/puma/error_logger.rb +0 -0
  54. data/lib/puma/events.rb +0 -0
  55. data/lib/puma/io_buffer.rb +0 -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 +0 -0
  60. data/lib/puma/log_writer.rb +1 -1
  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 +0 -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 +0 -0
  68. data/lib/puma/rack_default.rb +1 -1
  69. data/lib/puma/reactor.rb +16 -7
  70. data/lib/puma/request.rb +12 -6
  71. data/lib/puma/runner.rb +0 -0
  72. data/lib/puma/sd_notify.rb +0 -0
  73. data/lib/puma/server.rb +16 -4
  74. data/lib/puma/single.rb +0 -0
  75. data/lib/puma/state_file.rb +0 -0
  76. data/lib/puma/thread_pool.rb +7 -3
  77. data/lib/puma/util.rb +0 -0
  78. data/lib/puma.rb +0 -0
  79. data/lib/rack/handler/puma.rb +6 -2
  80. data/tools/Dockerfile +0 -0
  81. data/tools/trickletest.rb +0 -0
  82. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d5bf04e3d05e490359fd54acb971ea4316fbf6b51eec1c4d572359dd985eea92
4
- data.tar.gz: a85be9bb75fd16c13600f918fedad71cdd9950293af84464d488ad5adbdc7cbf
3
+ metadata.gz: fee07e30f79d6f40104c1265c78bba2ba7f91cd6aff53d4df7d7bcbe62f052da
4
+ data.tar.gz: 3f3d8d6107481ffc4e545c7f976ec3480c2c5876a4ba8b7c50e1f7b5a7b3d286
5
5
  SHA512:
6
- metadata.gz: db99880324d78555ff6de88b5fd9eb37b5e169b2109c7021ca2a0f6325ce704f56d6ea2155099e7cee2bc71a8db73bf9fd12b97142c461c9e20ecbf4e7821463
7
- data.tar.gz: 210d6a0a0cdd3922a792fd5311a4ae95ea6f3f1b04d5bd74daf316a55259f2c0f890b156d17812266ebf31a1a42ac860085f25bf5262a8556d987e4f5d3bc0f1
6
+ metadata.gz: a312fd8f5e8b8a146d40bca643fd22639bb3a67f7e2acd995e000e4c82b159c68b78547e3b48340cf747c0c2666cd3897581e19ff5a4a752dcb6c07cf8618562
7
+ data.tar.gz: a8136c073d50d1c1b8f7f4ff831bcd2f2aa0cfea1a233e9c88bf4900c120f3131e61245632dd55f5357ca5217203d64d7cef4285d19adce33bff4c5e18640f05
data/History.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## 6.3.0 / 2023-05-31
2
+
3
+ * Features
4
+ * Add dsl method `supported_http_methods` ([#3106], [#3014])
5
+ * Puma error responses no longer have any fingerprints to indicate Puma ([#3161], [#3037])
6
+ * Support decryption of SSL key ([#3133], [#3132])
7
+
8
+ * Bugfixes
9
+ * Don't send 103 early hints response when only invalid headers are used ([#3163])
10
+ * Handle malformed request path ([#3155], [#3148])
11
+ * Misc lib file fixes - trapping additional errors, CI helper ([#3129])
12
+ * Fixup req form data file upload with "r\n" line endings ([#3137])
13
+ * Restore rack 1.6 compatibility Restore rack 1.6 compatibility ([#3156])
14
+
15
+ * Refactor
16
+ * const.rb - Update Puma::HTTP_STATUS_CODES ([#3162])
17
+ * Clarify Reactor#initialize ([#3151])
18
+
1
19
  ## 6.2.2 / 2023-04-17
2
20
 
3
21
  * Bugfixes
@@ -22,7 +40,6 @@
22
40
  * commonlogger.rb - fix HIJACK time format, use constants, not strings ([#3074])
23
41
  * Fixed some edge cases regarding request hijacking ([#3072])
24
42
 
25
-
26
43
  ## 6.1.1 / 2023-02-28
27
44
 
28
45
  * Bugfixes
@@ -1979,6 +1996,20 @@ be added back in a future date when a java Puma::MiniSSL is added.
1979
1996
  * Bugfixes
1980
1997
  * Your bugfix goes here <Most recent on the top, like GitHub> (#Github Number)
1981
1998
 
1999
+ [#3106]:https://github.com/puma/puma/pull/3106 "PR by @MSP-Greg, merged 2023-05-29"
2000
+ [#3014]:https://github.com/puma/puma/issues/3014 "Issue by @kyledrake, closed 2023-05-29"
2001
+ [#3161]:https://github.com/puma/puma/pull/3161 "PR by @MSP-Greg, merged 2023-05-27"
2002
+ [#3037]:https://github.com/puma/puma/issues/3037 "Issue by @daisy1754, closed 2023-05-27"
2003
+ [#3133]:https://github.com/puma/puma/pull/3133 "PR by @stanhu, merged 2023-04-30"
2004
+ [#3132]:https://github.com/puma/puma/issues/3132 "Issue by @stanhu, closed 2023-04-30"
2005
+ [#3163]:https://github.com/puma/puma/pull/3163 "PR by @MSP-Greg, merged 2023-05-27"
2006
+ [#3155]:https://github.com/puma/puma/pull/3155 "PR by @dentarg, merged 2023-05-14"
2007
+ [#3148]:https://github.com/puma/puma/issues/3148 "Issue by @dentarg, closed 2023-05-14"
2008
+ [#3129]:https://github.com/puma/puma/pull/3129 "PR by @MSP-Greg, merged 2023-05-02"
2009
+ [#3137]:https://github.com/puma/puma/pull/3137 "PR by @MSP-Greg, merged 2023-04-30"
2010
+ [#3156]:https://github.com/puma/puma/pull/3156 "PR by @severin, merged 2023-05-16"
2011
+ [#3162]:https://github.com/puma/puma/pull/3162 "PR by @MSP-Greg, merged 2023-05-23"
2012
+ [#3151]:https://github.com/puma/puma/pull/3151 "PR by @nateberkopec, merged 2023-05-12"
1982
2013
  [#3118]:https://github.com/puma/puma/pull/3118 "PR by @ninoseki, merged 2023-04-01"
1983
2014
  [#3117]:https://github.com/puma/puma/issues/3117 "Issue by @ninoseki, closed 2023-04-01"
1984
2015
  [#3109]:https://github.com/puma/puma/pull/3109 "PR by @ahorek, merged 2023-03-31"
@@ -2089,7 +2120,7 @@ be added back in a future date when a java Puma::MiniSSL is added.
2089
2120
  [#2794]:https://github.com/puma/puma/pull/2794 "PR by @johnnyshields, merged 2022-01-10"
2090
2121
  [#2759]:https://github.com/puma/puma/pull/2759 "PR by @ob-stripe, merged 2021-12-11"
2091
2122
  [#2731]:https://github.com/puma/puma/pull/2731 "PR by @baelter, merged 2021-11-02"
2092
- [#2341]:https://github.com/puma/puma/issues/2341 "Issue by @cjlarose, closed 2021-11-02"
2123
+ [#2341]:https://github.com/puma/puma/issues/2341 "Issue by @cjlarose, opened 2020-08-18"
2093
2124
  [#2728]:https://github.com/puma/puma/pull/2728 "PR by @dalibor, merged 2021-10-31"
2094
2125
  [#2733]:https://github.com/puma/puma/pull/2733 "PR by @ob-stripe, merged 2021-12-12"
2095
2126
  [#2807]:https://github.com/puma/puma/pull/2807 "PR by @MSP-Greg, merged 2022-01-25"
data/LICENSE CHANGED
File without changes
data/README.md CHANGED
@@ -279,6 +279,30 @@ $ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&verification_f
279
279
  List of available flags: `USE_CHECK_TIME`, `CRL_CHECK`, `CRL_CHECK_ALL`, `IGNORE_CRITICAL`, `X509_STRICT`, `ALLOW_PROXY_CERTS`, `POLICY_CHECK`, `EXPLICIT_POLICY`, `INHIBIT_ANY`, `INHIBIT_MAP`, `NOTIFY_POLICY`, `EXTENDED_CRL_SUPPORT`, `USE_DELTAS`, `CHECK_SS_SIGNATURE`, `TRUSTED_FIRST`, `SUITEB_128_LOS_ONLY`, `SUITEB_192_LOS`, `SUITEB_128_LOS`, `PARTIAL_CHAIN`, `NO_ALT_CHAINS`, `NO_CHECK_TIME`
280
280
  (see https://www.openssl.org/docs/manmaster/man3/X509_VERIFY_PARAM_set_hostflags.html#VERIFICATION-FLAGS).
281
281
 
282
+ #### Controlling OpenSSL Password Decryption
283
+
284
+ To enable runtime decryption of an encrypted SSL key (not available for JRuby), use `key_password_command`:
285
+
286
+ ```
287
+ $ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&key_password_command=/path/to/command.sh'
288
+ ```
289
+
290
+ `key_password_command` must:
291
+
292
+ 1. Be executable by Puma.
293
+ 2. Print the decryption password to stdout.
294
+
295
+ For example:
296
+
297
+ ```shell
298
+ #!/bin/sh
299
+
300
+ echo "this is my password"
301
+ ```
302
+
303
+ `key_password_command` can be used with `key` or `key_pem`. If the key
304
+ is not encrypted, the executable will not be called.
305
+
282
306
  ### Control/Status Server
283
307
 
284
308
  Puma has a built-in status and control app that can be used to query and control Puma.
data/bin/puma-wild CHANGED
File without changes
data/docs/architecture.md CHANGED
File without changes
File without changes
data/docs/deployment.md CHANGED
File without changes
data/docs/fork_worker.md CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
data/docs/kubernetes.md CHANGED
File without changes
data/docs/nginx.md CHANGED
File without changes
data/docs/plugins.md CHANGED
File without changes
File without changes
data/docs/restart.md CHANGED
File without changes
data/docs/signals.md CHANGED
File without changes
data/docs/stats.md CHANGED
File without changes
data/docs/systemd.md CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -185,6 +185,18 @@ static int engine_verify_callback(int preverify_ok, X509_STORE_CTX* ctx) {
185
185
  return preverify_ok;
186
186
  }
187
187
 
188
+ static int password_callback(char *buf, int size, int rwflag, void *userdata) {
189
+ const char *password = (const char *) userdata;
190
+ size_t len = strlen(password);
191
+
192
+ if (len > (size_t) size) {
193
+ return 0;
194
+ }
195
+
196
+ memcpy(buf, password, len);
197
+ return (int) len;
198
+ }
199
+
188
200
  static VALUE
189
201
  sslctx_alloc(VALUE klass) {
190
202
  SSL_CTX *ctx;
@@ -212,10 +224,12 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
212
224
  SSL_CTX* ctx;
213
225
  int ssl_options;
214
226
  VALUE key, cert, ca, verify_mode, ssl_cipher_filter, no_tlsv1, no_tlsv1_1,
215
- verification_flags, session_id_bytes, cert_pem, key_pem;
227
+ verification_flags, session_id_bytes, cert_pem, key_pem, key_password_command, key_password;
216
228
  BIO *bio;
217
229
  X509 *x509;
218
230
  EVP_PKEY *pkey;
231
+ pem_password_cb *password_cb = NULL;
232
+ const char *password = NULL;
219
233
  #ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
220
234
  int min;
221
235
  #endif
@@ -235,6 +249,8 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
235
249
 
236
250
  key = rb_funcall(mini_ssl_ctx, rb_intern_const("key"), 0);
237
251
 
252
+ key_password_command = rb_funcall(mini_ssl_ctx, rb_intern_const("key_password_command"), 0);
253
+
238
254
  cert = rb_funcall(mini_ssl_ctx, rb_intern_const("cert"), 0);
239
255
 
240
256
  ca = rb_funcall(mini_ssl_ctx, rb_intern_const("ca"), 0);
@@ -261,6 +277,18 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
261
277
  }
262
278
  }
263
279
 
280
+ if (!NIL_P(key_password_command)) {
281
+ key_password = rb_funcall(mini_ssl_ctx, rb_intern_const("key_password"), 0);
282
+
283
+ if (!NIL_P(key_password)) {
284
+ StringValue(key_password);
285
+ password_cb = password_callback;
286
+ password = RSTRING_PTR(key_password);
287
+ SSL_CTX_set_default_passwd_cb(ctx, password_cb);
288
+ SSL_CTX_set_default_passwd_cb_userdata(ctx, (void *) password);
289
+ }
290
+ }
291
+
264
292
  if (!NIL_P(key)) {
265
293
  StringValue(key);
266
294
 
@@ -285,7 +313,7 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
285
313
  if (!NIL_P(key_pem)) {
286
314
  bio = BIO_new(BIO_s_mem());
287
315
  BIO_puts(bio, RSTRING_PTR(key_pem));
288
- pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
316
+ pkey = PEM_read_bio_PrivateKey(bio, NULL, password_cb, (void *) password);
289
317
 
290
318
  if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1) {
291
319
  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
@@ -451,11 +450,14 @@ module Puma
451
450
 
452
451
  def close_listeners
453
452
  @listeners.each do |l, io|
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)
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
459
461
  end
460
462
  end
461
463
 
data/lib/puma/cli.rb CHANGED
File without changes
data/lib/puma/client.rb CHANGED
@@ -68,7 +68,7 @@ module Puma
68
68
  @to_io = io.to_io
69
69
  @io_buffer = IOBuffer.new
70
70
  @proto_env = env
71
- @env = env ? env.dup : nil
71
+ @env = env&.dup
72
72
 
73
73
  @parser = HttpParser.new
74
74
  @parsed_bytes = 0
@@ -99,7 +99,8 @@ module Puma
99
99
 
100
100
  @in_last_chunk = false
101
101
 
102
- @read_buffer = +""
102
+ # need unfrozen ASCII-8BIT, +'' is UTF-8
103
+ @read_buffer = String.new # rubocop: disable Performance/UnfreezeString
103
104
  end
104
105
 
105
106
  attr_reader :env, :to_io, :body, :io, :timeout_at, :ready, :hijacked,
File without changes
File without changes
data/lib/puma/cluster.rb CHANGED
File without changes
File without changes
File without changes
data/lib/puma/const.rb CHANGED
@@ -18,6 +18,7 @@ module Puma
18
18
  100 => 'Continue',
19
19
  101 => 'Switching Protocols',
20
20
  102 => 'Processing',
21
+ 103 => 'Early Hints',
21
22
  200 => 'OK',
22
23
  201 => 'Created',
23
24
  202 => 'Accepted',
@@ -49,16 +50,16 @@ module Puma
49
50
  410 => 'Gone',
50
51
  411 => 'Length Required',
51
52
  412 => 'Precondition Failed',
52
- 413 => 'Payload Too Large',
53
+ 413 => 'Content Too Large',
53
54
  414 => 'URI Too Long',
54
55
  415 => 'Unsupported Media Type',
55
56
  416 => 'Range Not Satisfiable',
56
57
  417 => 'Expectation Failed',
57
- 418 => 'I\'m A Teapot',
58
58
  421 => 'Misdirected Request',
59
- 422 => 'Unprocessable Entity',
59
+ 422 => 'Unprocessable Content',
60
60
  423 => 'Locked',
61
61
  424 => 'Failed Dependency',
62
+ 425 => 'Too Early',
62
63
  426 => 'Upgrade Required',
63
64
  428 => 'Precondition Required',
64
65
  429 => 'Too Many Requests',
@@ -73,7 +74,7 @@ module Puma
73
74
  506 => 'Variant Also Negotiates',
74
75
  507 => 'Insufficient Storage',
75
76
  508 => 'Loop Detected',
76
- 510 => 'Not Extended',
77
+ 510 => 'Not Extended (OBSOLETED)',
77
78
  511 => 'Network Authentication Required'
78
79
  }.freeze
79
80
 
@@ -99,8 +100,8 @@ module Puma
99
100
  # too taxing on performance.
100
101
  module Const
101
102
 
102
- PUMA_VERSION = VERSION = "6.2.2"
103
- CODE_NAME = "Speaking of Now"
103
+ PUMA_VERSION = VERSION = "6.3.0"
104
+ CODE_NAME = "Mugi No Toki Itaru"
104
105
 
105
106
  PUMA_SERVER_STRING = ["puma", PUMA_VERSION, CODE_NAME].join(" ").freeze
106
107
 
@@ -124,15 +125,15 @@ module Puma
124
125
  # Indicate that we couldn't parse the request
125
126
  400 => "HTTP/1.1 400 Bad Request\r\n\r\n",
126
127
  # The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
127
- 404 => "HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\nNOT FOUND",
128
+ 404 => "HTTP/1.1 404 Not Found\r\nConnection: close\r\n\r\n",
128
129
  # The standard empty 408 response for requests that timed out.
129
- 408 => "HTTP/1.1 408 Request Timeout\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\n",
130
+ 408 => "HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n\r\n",
130
131
  # Indicate that there was an internal error, obviously.
131
132
  500 => "HTTP/1.1 500 Internal Server Error\r\n\r\n",
132
133
  # Incorrect or invalid header value
133
134
  501 => "HTTP/1.1 501 Not Implemented\r\n\r\n",
134
135
  # A common header for indicating the server is too busy. Not used yet.
135
- 503 => "HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY"
136
+ 503 => "HTTP/1.1 503 Service Unavailable\r\n\r\n"
136
137
  }.freeze
137
138
 
138
139
  # The basic max request size we'll try to read.
@@ -147,7 +148,55 @@ module Puma
147
148
 
148
149
  REQUEST_METHOD = "REQUEST_METHOD"
149
150
  HEAD = "HEAD"
151
+
152
+ # based on https://www.rfc-editor.org/rfc/rfc9110.html#name-overview,
153
+ # with CONNECT removed, and PATCH added
150
154
  SUPPORTED_HTTP_METHODS = %w[HEAD GET POST PUT DELETE OPTIONS TRACE PATCH].freeze
155
+
156
+ # list from https://www.iana.org/assignments/http-methods/http-methods.xhtml
157
+ # as of 04-May-23
158
+ IANA_HTTP_METHODS = %w[
159
+ ACL
160
+ BASELINE-CONTROL
161
+ BIND
162
+ CHECKIN
163
+ CHECKOUT
164
+ CONNECT
165
+ COPY
166
+ DELETE
167
+ GET
168
+ HEAD
169
+ LABEL
170
+ LINK
171
+ LOCK
172
+ MERGE
173
+ MKACTIVITY
174
+ MKCALENDAR
175
+ MKCOL
176
+ MKREDIRECTREF
177
+ MKWORKSPACE
178
+ MOVE
179
+ OPTIONS
180
+ ORDERPATCH
181
+ PATCH
182
+ POST
183
+ PRI
184
+ PROPFIND
185
+ PROPPATCH
186
+ PUT
187
+ REBIND
188
+ REPORT
189
+ SEARCH
190
+ TRACE
191
+ UNBIND
192
+ UNCHECKOUT
193
+ UNLINK
194
+ UNLOCK
195
+ UPDATE
196
+ UPDATEREDIRECTREF
197
+ VERSION-CONTROL
198
+ ].freeze
199
+
151
200
  # ETag is based on the apache standard of hex mtime-size-inode (inode is 0 on win32)
152
201
  LINE_END = "\r\n"
153
202
  REMOTE_ADDR = "REMOTE_ADDR"
File without changes
data/lib/puma/detect.rb CHANGED
File without changes
data/lib/puma/dsl.rb CHANGED
@@ -89,6 +89,7 @@ module Puma
89
89
 
90
90
  cert_flags = (cert = opts[:cert]) ? "cert=#{Puma::Util.escape(cert)}" : nil
91
91
  key_flags = (key = opts[:key]) ? "&key=#{Puma::Util.escape(key)}" : nil
92
+ password_flags = (password_command = opts[:key_password_command]) ? "&key_password_command=#{Puma::Util.escape(password_command)}" : nil
92
93
 
93
94
  reuse_flag =
94
95
  if (reuse = opts[:reuse])
@@ -114,7 +115,7 @@ module Puma
114
115
  nil
115
116
  end
116
117
 
117
- "ssl://#{host}:#{port}?#{cert_flags}#{key_flags}#{ssl_cipher_filter}" \
118
+ "ssl://#{host}:#{port}?#{cert_flags}#{key_flags}#{password_flags}#{ssl_cipher_filter}" \
118
119
  "#{reuse_flag}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}#{backlog_str}#{low_latency_str}"
119
120
  end
120
121
  end
@@ -1066,6 +1067,38 @@ module Puma
1066
1067
  @options[:http_content_length_limit] = limit
1067
1068
  end
1068
1069
 
1070
+ # Supported http methods, which will replace `Puma::Const::SUPPORTED_HTTP_METHODS`.
1071
+ # The value of `:any` will allows all methods, otherwise, the value must be
1072
+ # an array of strings. Note that methods are all uppercase.
1073
+ #
1074
+ # `Puma::Const::SUPPORTED_HTTP_METHODS` is conservative, if you want a
1075
+ # complete set of methods, the methods defined by the
1076
+ # [IANA Method Registry](https://www.iana.org/assignments/http-methods/http-methods.xhtml)
1077
+ # are pre-defined as the constant `Puma::Const::IANA_HTTP_METHODS`.
1078
+ #
1079
+ # @note If the `methods` value is `:any`, no method check with be performed,
1080
+ # similar to Puma v5 and earlier.
1081
+ #
1082
+ # @example Adds 'PROPFIND' to existing supported methods
1083
+ # supported_http_methods(Puma::Const::SUPPORTED_HTTP_METHODS + ['PROPFIND'])
1084
+ # @example Restricts methods to the array elements
1085
+ # supported_http_methods %w[HEAD GET POST PUT DELETE OPTIONS PROPFIND]
1086
+ # @example Restricts methods to the methods in the IANA Registry
1087
+ # supported_http_methods Puma::Const::IANA_HTTP_METHODS
1088
+ # @example Allows any method
1089
+ # supported_http_methods :any
1090
+ #
1091
+ def supported_http_methods(methods)
1092
+ if methods == :any
1093
+ @options[:supported_http_methods] = :any
1094
+ elsif Array === methods && methods == (ary = methods.grep(String).uniq) &&
1095
+ !ary.empty?
1096
+ @options[:supported_http_methods] = ary
1097
+ else
1098
+ raise "supported_http_methods must be ':any' or a unique array of strings"
1099
+ end
1100
+ end
1101
+
1069
1102
  private
1070
1103
 
1071
1104
  # To avoid adding cert_pem and key_pem as URI params, we store them on the
File without changes
data/lib/puma/events.rb CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
data/lib/puma/launcher.rb CHANGED
File without changes
@@ -125,7 +125,7 @@ module Puma
125
125
  def ssl_error(error, ssl_socket)
126
126
  peeraddr = ssl_socket.peeraddr.last rescue "<unknown>"
127
127
  peercert = ssl_socket.peercert
128
- subject = peercert ? peercert.subject : nil
128
+ subject = peercert&.subject
129
129
  @error_logger.info(error: error, text: "SSL error, peer: #{peeraddr}, peer cert: #{subject}")
130
130
  end
131
131
 
@@ -38,6 +38,7 @@ module Puma
38
38
 
39
39
  ctx.key = params['key'] if params['key']
40
40
  ctx.key_pem = params['key_pem'] if params['key_pem']
41
+ ctx.key_password_command = params['key_password_command'] if params['key_password_command']
41
42
 
42
43
  if params['cert'].nil? && params['cert_pem'].nil?
43
44
  log_writer.error "Please specify the SSL cert via 'cert=' or 'cert_pem='"
data/lib/puma/minissl.rb CHANGED
@@ -5,6 +5,7 @@ begin
5
5
  rescue LoadError
6
6
  end
7
7
 
8
+ require 'open3'
8
9
  # need for Puma::MiniSSL::OPENSSL constants used in `HAS_TLS1_3`
9
10
  # use require, see https://github.com/puma/puma/pull/2381
10
11
  require 'puma/puma_http11'
@@ -277,6 +278,7 @@ module Puma
277
278
  else
278
279
  # non-jruby Context properties
279
280
  attr_reader :key
281
+ attr_reader :key_password_command
280
282
  attr_reader :cert
281
283
  attr_reader :ca
282
284
  attr_reader :cert_pem
@@ -291,6 +293,10 @@ module Puma
291
293
  @key = key
292
294
  end
293
295
 
296
+ def key_password_command=(key_password_command)
297
+ @key_password_command = key_password_command
298
+ end
299
+
294
300
  def cert=(cert)
295
301
  check_file cert, 'Cert'
296
302
  @cert = cert
@@ -316,6 +322,17 @@ module Puma
316
322
  raise "Cert not configured" if @cert.nil? && @cert_pem.nil?
317
323
  end
318
324
 
325
+ # Executes the command to return the password needed to decrypt the key.
326
+ def key_password
327
+ raise "Key password command not configured" if @key_password_command.nil?
328
+
329
+ stdout_str, stderr_str, status = Open3.capture3(@key_password_command)
330
+
331
+ return stdout_str.chomp if status.success?
332
+
333
+ raise "Key password failed with code #{status.exitstatus}: #{stderr_str}"
334
+ end
335
+
319
336
  # Controls session reuse. Allowed values are as follows:
320
337
  # * 'off' - matches the behavior of Puma 5.6 and earlier. This is included
321
338
  # in case reuse 'on' is made the default in future Puma versions.
File without changes
File without changes
data/lib/puma/plugin.rb CHANGED
File without changes
@@ -173,7 +173,7 @@ module Puma::Rack
173
173
  TOPLEVEL_BINDING, file, 0
174
174
  end
175
175
 
176
- def initialize(default_app = nil,&block)
176
+ def initialize(default_app = nil, &block)
177
177
  @use, @map, @run, @warmup = [], nil, default_app, nil
178
178
 
179
179
  # Conditionally load rack now, so that any rack middlewares,
@@ -183,7 +183,7 @@ module Puma::Rack
183
183
  rescue LoadError
184
184
  end
185
185
 
186
- instance_eval(&block) if block_given?
186
+ instance_eval(&block) if block
187
187
  end
188
188
 
189
189
  def self.app(default_app = nil, &block)
File without changes
@@ -11,7 +11,7 @@ if Object.const_defined? :Rackup
11
11
  end
12
12
  end
13
13
  end
14
- elsif Object.const_defined?(:Rack) && Rack::RELEASE < '3'
14
+ elsif Object.const_defined?(:Rack) && Rack.release < '3'
15
15
  module Rack
16
16
  module Handler
17
17
  def self.default(options = {})
data/lib/puma/reactor.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'queue_close' unless ::Queue.instance_methods.include? :close
4
-
5
3
  module Puma
6
4
  class UnsupportedBackend < StandardError; end
7
5
 
@@ -22,10 +20,12 @@ module Puma
22
20
  # its timeout elapses, or when the Reactor shuts down.
23
21
  def initialize(backend, &block)
24
22
  require 'nio'
25
- unless backend == :auto || NIO::Selector.backends.include?(backend)
26
- raise "unsupported IO selector backend: #{backend} (available backends: #{NIO::Selector.backends.join(', ')})"
23
+ valid_backends = [:auto, *::NIO::Selector.backends]
24
+ unless valid_backends.include?(backend)
25
+ raise ArgumentError.new("unsupported IO selector backend: #{backend} (available backends: #{valid_backends.join(', ')})")
27
26
  end
28
- @selector = backend == :auto ? NIO::Selector.new : NIO::Selector.new(backend)
27
+
28
+ @selector = ::NIO::Selector.new(NIO::Selector.backends.delete(backend))
29
29
  @input = Queue.new
30
30
  @timeouts = []
31
31
  @block = block
@@ -67,6 +67,7 @@ module Puma
67
67
  private
68
68
 
69
69
  def select_loop
70
+ close_selector = true
70
71
  begin
71
72
  until @input.closed? && @input.empty?
72
73
  # Wakeup any registered object that receives incoming data.
@@ -89,11 +90,19 @@ module Puma
89
90
  rescue StandardError => e
90
91
  STDERR.puts "Error in reactor loop escaped: #{e.message} (#{e.class})"
91
92
  STDERR.puts e.backtrace
92
- retry
93
+
94
+ # NoMethodError may be rarely raised when calling @selector.select, which
95
+ # is odd. Regardless, it may continue for thousands of calls if retried.
96
+ # Also, when it raises, @selector.close also raises an error.
97
+ if NoMethodError === e
98
+ close_selector = false
99
+ else
100
+ retry
101
+ end
93
102
  end
94
103
  # Wakeup all remaining objects on shutdown.
95
104
  @timeouts.each(&@block)
96
- @selector.close
105
+ @selector.close if close_selector
97
106
  end
98
107
 
99
108
  # Start monitoring the object.
data/lib/puma/request.rb CHANGED
@@ -77,7 +77,9 @@ module Puma
77
77
  if @early_hints
78
78
  env[EARLY_HINTS] = lambda { |headers|
79
79
  begin
80
- fast_write_str socket, str_early_hints(headers)
80
+ unless (str = str_early_hints headers).empty?
81
+ fast_write_str socket, "HTTP/1.1 103 Early Hints\r\n#{str}\r\n"
82
+ end
81
83
  rescue ConnectionError => e
82
84
  @log_writer.debug_error e
83
85
  # noop, if we lost the socket we just won't send the early hints
@@ -93,7 +95,7 @@ module Puma
93
95
  env[RACK_AFTER_REPLY] ||= []
94
96
 
95
97
  begin
96
- if SUPPORTED_HTTP_METHODS.include?(env[REQUEST_METHOD])
98
+ if @supported_http_methods == :any || @supported_http_methods.key?(env[REQUEST_METHOD])
97
99
  status, headers, app_body = @thread_pool.with_force_shutdown do
98
100
  @app.call(env)
99
101
  end
@@ -418,7 +420,11 @@ module Puma
418
420
 
419
421
  unless env[REQUEST_PATH]
420
422
  # it might be a dumbass full host request header
421
- uri = URI.parse(env[REQUEST_URI])
423
+ uri = begin
424
+ URI.parse(env[REQUEST_URI])
425
+ rescue URI::InvalidURIError
426
+ raise Puma::HttpParserError
427
+ end
422
428
  env[REQUEST_PATH] = uri.path
423
429
 
424
430
  # A nil env value will cause a LintError (and fatal errors elsewhere),
@@ -525,7 +531,7 @@ module Puma
525
531
  # @version 5.0.3
526
532
  #
527
533
  def str_early_hints(headers)
528
- eh_str = +"HTTP/1.1 103 Early Hints\r\n"
534
+ eh_str = +""
529
535
  headers.each_pair do |k, vs|
530
536
  next if illegal_header_key?(k)
531
537
 
@@ -534,11 +540,11 @@ module Puma
534
540
  next if illegal_header_value?(v)
535
541
  eh_str << "#{k}: #{v}\r\n"
536
542
  end
537
- else
543
+ elsif !(vs.to_s.empty? || !illegal_header_value?(vs))
538
544
  eh_str << "#{k}: #{vs}\r\n"
539
545
  end
540
546
  end
541
- "#{eh_str}\r\n".freeze
547
+ eh_str.freeze
542
548
  end
543
549
  private :str_early_hints
544
550
 
data/lib/puma/runner.rb CHANGED
File without changes
File without changes
data/lib/puma/server.rb CHANGED
@@ -51,7 +51,7 @@ module Puma
51
51
  def_delegators :@binder, :add_tcp_listener, :add_ssl_listener,
52
52
  :add_unix_listener, :connected_ports
53
53
 
54
- ThreadLocalKey = :puma_server
54
+ THREAD_LOCAL_KEY = :puma_server
55
55
 
56
56
  # Create a server for the rack app +app+.
57
57
  #
@@ -97,6 +97,18 @@ module Puma
97
97
  @io_selector_backend = @options[:io_selector_backend]
98
98
  @http_content_length_limit = @options[:http_content_length_limit]
99
99
 
100
+ # make this a hash, since we prefer `key?` over `include?`
101
+ @supported_http_methods =
102
+ if @options[:supported_http_methods] == :any
103
+ :any
104
+ else
105
+ if (ary = @options[:supported_http_methods])
106
+ ary
107
+ else
108
+ SUPPORTED_HTTP_METHODS
109
+ end.sort.product([nil]).to_h.freeze
110
+ end
111
+
100
112
  temp = !!(@options[:environment] =~ /\A(development|test)\z/)
101
113
  @leak_stack_on_error = @options[:environment] ? temp : true
102
114
 
@@ -118,7 +130,7 @@ module Puma
118
130
  class << self
119
131
  # @!attribute [r] current
120
132
  def current
121
- Thread.current[ThreadLocalKey]
133
+ Thread.current[THREAD_LOCAL_KEY]
122
134
  end
123
135
 
124
136
  # :nodoc:
@@ -404,7 +416,7 @@ module Puma
404
416
  # Return true if one or more requests were processed.
405
417
  def process_client(client)
406
418
  # Advertise this server into the thread
407
- Thread.current[ThreadLocalKey] = self
419
+ Thread.current[THREAD_LOCAL_KEY] = self
408
420
 
409
421
  clean_thread_locals = @options[:clean_thread_locals]
410
422
  close_socket = true
@@ -566,7 +578,7 @@ module Puma
566
578
 
567
579
  def notify_safely(message)
568
580
  @notify << message
569
- rescue IOError, NoMethodError, Errno::EPIPE
581
+ rescue IOError, NoMethodError, Errno::EPIPE, Errno::EBADF
570
582
  # The server, in another thread, is shutting down
571
583
  Puma::Util.purge_interrupt_queue
572
584
  rescue RuntimeError => e
data/lib/puma/single.rb CHANGED
File without changes
File without changes
@@ -44,6 +44,10 @@ module Puma
44
44
  @name = name
45
45
  @min = Integer(options[:min_threads])
46
46
  @max = Integer(options[:max_threads])
47
+ # Not an 'exposed' option, options[:pool_shutdown_grace_time] is used in CI
48
+ # to shorten @shutdown_grace_time from SHUTDOWN_GRACE_TIME. Parallel CI
49
+ # makes stubbing constants difficult.
50
+ @shutdown_grace_time = Float(options[:pool_shutdown_grace_time] || SHUTDOWN_GRACE_TIME)
47
51
  @block = block
48
52
  @out_of_band = options[:out_of_band]
49
53
  @clean_thread_locals = options[:clean_thread_locals]
@@ -344,8 +348,8 @@ module Puma
344
348
 
345
349
  # Tell all threads in the pool to exit and wait for them to finish.
346
350
  # Wait +timeout+ seconds then raise +ForceShutdown+ in remaining threads.
347
- # Next, wait an extra +grace+ seconds then force-kill remaining threads.
348
- # Finally, wait +kill_grace+ seconds for remaining threads to exit.
351
+ # Next, wait an extra +@shutdown_grace_time+ seconds then force-kill remaining
352
+ # threads. Finally, wait 1 second for remaining threads to exit.
349
353
  #
350
354
  def shutdown(timeout=-1)
351
355
  threads = with_mutex do
@@ -382,7 +386,7 @@ module Puma
382
386
  t.raise ForceShutdown if t[:with_force_shutdown]
383
387
  end
384
388
  end
385
- join.call(SHUTDOWN_GRACE_TIME)
389
+ join.call(@shutdown_grace_time)
386
390
 
387
391
  # If threads are _still_ running, forcefully kill them and wait to finish.
388
392
  threads.each(&:kill)
data/lib/puma/util.rb CHANGED
File without changes
data/lib/puma.rb CHANGED
File without changes
@@ -31,7 +31,11 @@ module Puma
31
31
 
32
32
  conf = ::Puma::Configuration.new(options, default_options.merge({events: @events})) do |user_config, file_config, default_config|
33
33
  if options.delete(:Verbose)
34
- require 'rack/common_logger'
34
+ begin
35
+ require 'rack/commonlogger' # Rack 1.x
36
+ rescue LoadError
37
+ require 'rack/common_logger' # Rack 2 and later
38
+ end
35
39
  app = ::Rack::CommonLogger.new(app, STDOUT)
36
40
  end
37
41
 
@@ -123,7 +127,7 @@ if Object.const_defined? :Rackup
123
127
  end
124
128
  end
125
129
  else
126
- do_register = Object.const_defined?(:Rack) && Rack::RELEASE < '3'
130
+ do_register = Object.const_defined?(:Rack) && Rack.release < '3'
127
131
  module Rack
128
132
  module Handler
129
133
  module Puma
data/tools/Dockerfile CHANGED
File without changes
data/tools/trickletest.rb CHANGED
File without changes
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.2.2
4
+ version: 6.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Phoenix
@@ -145,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
145
145
  - !ruby/object:Gem::Version
146
146
  version: '0'
147
147
  requirements: []
148
- rubygems_version: 3.3.20
148
+ rubygems_version: 3.4.12
149
149
  signing_key:
150
150
  specification_version: 4
151
151
  summary: Puma is a simple, fast, threaded, and highly parallel HTTP 1.1 server for