puma 3.11.4 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7f451a7278034b23b2a1537d6bde82ae318cd58750dbab2c0a6aa4eb0e72efb0
4
- data.tar.gz: 3841d52680598006e72b92c37c358d0f185980bfca81cdc55c19e25673156f61
3
+ metadata.gz: da843833fd17b4bb2283f4c5161a1aa9367a6613455b8fbf31bae49393db4f80
4
+ data.tar.gz: bd9259270bd27f8421827c66e7f515044f51a7672c3dc755836d2a6b1240e84d
5
5
  SHA512:
6
- metadata.gz: 6273588bb47a3f85b40a358dc445733de687707111dd5aaffe4004be32a82eef00c227495abd9e68bd0b3f398395b64b685e8b78f5b428e3aee87be50f91cb1f
7
- data.tar.gz: 167dc9b6bdcf05050cff4d80af977ede8c4cd9b5fb0dd3397f8075e4657402cf5e7e084c248c2e23d06669cbce52cd13a84155612a3451c0ac5df6d0cff36730
6
+ metadata.gz: 74d807145c97b7714c04ebf7858af57b1cdf00e87217b8a88428494718893f7670ffd27216c31164f57bd96984cd8e79f3c7f856d39c1b54c192965fe8ecdec8
7
+ data.tar.gz: e0616e41dceddc3b8aad69a5baab5b49007053d151bf2689de173495f3160900269bab94c539a47fe2bbdd2db1aab98a0df8177ece857a06bea6261c5d37a704
data/History.md CHANGED
@@ -1,3 +1,48 @@
1
+ ## Master
2
+
3
+ * x features
4
+
5
+ * x bugfixes
6
+
7
+
8
+ ## 4.3.3 and 3.12.4 / 2020-02-28
9
+ * Bugfixes
10
+ * Fix: Fixes a problem where we weren't splitting headers correctly on newlines (#2132)
11
+ * Security
12
+ * Fix: Prevent HTTP Response splitting via CR in early hints.
13
+
14
+ ## 4.3.2 and 3.12.3 / 2020-02-27
15
+
16
+ * Security
17
+ * Fix: Prevent HTTP Response splitting via CR/LF in header values. CVE-2020-5247.
18
+
19
+ ## 4.3.1 and 3.12.2 / 2019-12-05
20
+
21
+ * Security
22
+ * Fix: a poorly-behaved client could use keepalive requests to monopolize Puma's reactor and create a denial of service attack. CVE-2019-16770.
23
+
24
+ ## 3.12.1 / 2019-03-19
25
+
26
+ * 1 features
27
+ * Internal strings are frozen (#1649)
28
+ * 3 bugfixes
29
+ * Fix chunked ending check (#1607)
30
+ * Rack handler should use provided default host (#1700)
31
+ * Better support for detecting runtimes that support `fork` (#1630)
32
+
33
+ ## 3.12.0 / 2018-07-13
34
+
35
+ * 5 features:
36
+ * You can now specify which SSL ciphers the server should support, default is unchanged (#1478)
37
+ * The setting for Puma's `max_threads` is now in `Puma.stats` (#1604)
38
+ * Pool capacity is now in `Puma.stats` (#1579)
39
+ * Installs restricted to Ruby 2.2+ (#1506)
40
+ * `--control` is now deprecated in favor of `--control-url` (#1487)
41
+
42
+ * 2 bugfixes:
43
+ * Workers will no longer accept more web requests than they have capacity to process. This prevents an issue where one worker would accept lots of requests while starving other workers (#1563)
44
+ * In a test env puma now emits the stack on an exception (#1557)
45
+
1
46
  ## 3.11.4 / 2018-04-12
2
47
 
3
48
  * 2 features:
@@ -7,7 +52,7 @@
7
52
  * Fix parsing CLI options (#1482)
8
53
  * Order of stderr and stdout is made before redirecting to a log file (#1511)
9
54
  * Init.d fix of `ps -p` to check if pid exists (#1545)
10
- * Early hits bugfix (#1550)
55
+ * Early hints bugfix (#1550)
11
56
  * Purge interrupt queue when closing socket fails (#1553)
12
57
 
13
58
  ## 3.11.3 / 2018-03-05
data/README.md CHANGED
@@ -157,17 +157,27 @@ $ puma -b 'unix:///var/run/puma.sock?umask=0111'
157
157
  ```
158
158
 
159
159
  Need a bit of security? Use SSL sockets:
160
-
161
160
  ```
162
161
  $ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert'
163
162
  ```
163
+ #### Controlling SSL Cipher Suites
164
+ Need to use or avoid specific SSL cipher suites? Use ssl_cipher_filter or ssl_cipher_list options.
165
+ #####Ruby:
166
+ ```
167
+ $ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&ssl_cipher_filter=!aNULL:AES+SHA'
168
+ ```
169
+ #####JRuby:
170
+ ```
171
+ $ puma -b 'ssl://127.0.0.1:9292?keystore=path_to_keystore&keystore-pass=keystore_password&ssl_cipher_list=TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA'
172
+ ```
173
+ See https://www.openssl.org/docs/man1.0.2/apps/ciphers.html for cipher filter format and full list of cipher suites.
164
174
 
165
175
  ### Control/Status Server
166
176
 
167
177
  Puma has a built-in status/control app that can be used to query and control Puma itself.
168
178
 
169
179
  ```
170
- $ puma --control tcp://127.0.0.1:9293 --control-token foo
180
+ $ puma --control-url tcp://127.0.0.1:9293 --control-token foo
171
181
  ```
172
182
 
173
183
  Puma will start the control server on localhost port 9293. All requests to the control server will need to include `token=foo` as a query parameter. This allows for simple authentication. Check out [status.rb](https://github.com/puma/puma/blob/master/lib/puma/app/status.rb) to see what the app has available.
@@ -175,7 +185,7 @@ Puma will start the control server on localhost port 9293. All requests to the c
175
185
  You can also interact with the control server via `pumactl`. This command will restart Puma:
176
186
 
177
187
  ```
178
- $ pumactl -C 'tcp://127.0.0.1:9293' --control-token foo restart
188
+ $ pumactl --control-url 'tcp://127.0.0.1:9293' --control-token foo restart
179
189
  ```
180
190
 
181
191
  To see a list of `pumactl` options, use `pumactl --help`.
@@ -217,10 +227,10 @@ Some platforms do not support all Puma features.
217
227
 
218
228
  ## Known Bugs
219
229
 
220
- For MRI versions 2.2.7, 2.2.8, 2.2.9, 2.3.4 and 2.4.1, you may see ```stream closed in another thread (IOError)```. It may be caused by a [Ruby bug](https://bugs.ruby-lang.org/issues/13632). It can be fixed with the gem https://rubygems.org/gems/stopgap_13632:
230
+ For MRI versions 2.2.7, 2.2.8, 2.2.9, 2.2.10 2.3.4 and 2.4.1, you may see ```stream closed in another thread (IOError)```. It may be caused by a [Ruby bug](https://bugs.ruby-lang.org/issues/13632). It can be fixed with the gem https://rubygems.org/gems/stopgap_13632:
221
231
 
222
232
  ```ruby
223
- if %w(2.2.7 2.2.8 2.2.9 2.3.4 2.4.1).include? RUBY_VERSION
233
+ if %w(2.2.7 2.2.8 2.2.9 2.2.10 2.3.4 2.4.1).include? RUBY_VERSION
224
234
  begin
225
235
  require 'stopgap_13632'
226
236
  rescue LoadError
Binary file
Binary file
@@ -14,12 +14,14 @@
14
14
 
15
15
  /*
16
16
  * capitalizes all lower-case ASCII characters,
17
- * converts dashes to underscores.
17
+ * converts dashes to underscores, and underscores to commas.
18
18
  */
19
19
  static void snake_upcase_char(char *c)
20
20
  {
21
21
  if (*c >= 'a' && *c <= 'z')
22
22
  *c &= ~0x20;
23
+ else if (*c == '_')
24
+ *c = ',';
23
25
  else if (*c == '-')
24
26
  *c = '_';
25
27
  }
@@ -12,12 +12,14 @@
12
12
 
13
13
  /*
14
14
  * capitalizes all lower-case ASCII characters,
15
- * converts dashes to underscores.
15
+ * converts dashes to underscores, and underscores to commas.
16
16
  */
17
17
  static void snake_upcase_char(char *c)
18
18
  {
19
19
  if (*c >= 'a' && *c <= 'z')
20
20
  *c &= ~0x20;
21
+ else if (*c == '_')
22
+ *c = ',';
21
23
  else if (*c == '-')
22
24
  *c = '_';
23
25
  }
@@ -161,6 +161,9 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
161
161
  ID sym_verify_mode = rb_intern("verify_mode");
162
162
  VALUE verify_mode = rb_funcall(mini_ssl_ctx, sym_verify_mode, 0);
163
163
 
164
+ ID sym_ssl_cipher_filter = rb_intern("ssl_cipher_filter");
165
+ VALUE ssl_cipher_filter = rb_funcall(mini_ssl_ctx, sym_ssl_cipher_filter, 0);
166
+
164
167
  ctx = SSL_CTX_new(SSLv23_server_method());
165
168
  conn->ctx = ctx;
166
169
 
@@ -175,7 +178,13 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
175
178
  SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION);
176
179
  SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
177
180
 
178
- SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL@STRENGTH");
181
+ if (!NIL_P(ssl_cipher_filter)) {
182
+ StringValue(ssl_cipher_filter);
183
+ SSL_CTX_set_cipher_list(ctx, RSTRING_PTR(ssl_cipher_filter));
184
+ }
185
+ else {
186
+ SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL@STRENGTH");
187
+ }
179
188
 
180
189
  DH *dh = get_dh1024();
181
190
  SSL_CTX_set_tmp_dh(ctx, dh);
@@ -424,6 +433,18 @@ void Init_mini_ssl(VALUE puma) {
424
433
  mod = rb_define_module_under(puma, "MiniSSL");
425
434
  eng = rb_define_class_under(mod, "Engine", rb_cObject);
426
435
 
436
+ // OpenSSL Build / Runtime/Load versions
437
+
438
+ /* Version of OpenSSL that Puma was compiled with */
439
+ rb_define_const(mod, "OPENSSL_VERSION", rb_str_new2(OPENSSL_VERSION_TEXT));
440
+
441
+ #if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000
442
+ /* Version of OpenSSL that Puma loaded with */
443
+ rb_define_const(mod, "OPENSSL_LIBRARY_VERSION", rb_str_new2(OpenSSL_version(OPENSSL_VERSION)));
444
+ #else
445
+ rb_define_const(mod, "OPENSSL_LIBRARY_VERSION", rb_str_new2(SSLeay_version(SSLEAY_VERSION)));
446
+ #endif
447
+
427
448
  rb_define_singleton_method(mod, "check", noop, 0);
428
449
 
429
450
  eError = rb_define_class_under(mod, "SSLError", rb_eStandardError);
@@ -170,6 +170,12 @@ public class MiniSSL extends RubyObject {
170
170
  engine.setNeedClientAuth(true);
171
171
  }
172
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
+
173
179
  SSLSession session = engine.getSession();
174
180
  inboundNetData = new MiniSSLBuffer(session.getPacketBufferSize());
175
181
  outboundAppData = new MiniSSLBuffer(session.getApplicationBufferSize());
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,14 +112,12 @@ 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
123
  backlog = 1024
@@ -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
@@ -313,6 +316,7 @@ module Puma
313
316
  s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
314
317
  s.listen backlog
315
318
 
319
+
316
320
  ssl = MiniSSL::Server.new s, ctx
317
321
  env = @proto_env.dup
318
322
  env[HTTPS_KEY] = HTTPS
data/lib/puma/cli.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'optparse'
2
4
  require 'uri'
3
5
 
@@ -84,6 +86,14 @@ module Puma
84
86
  raise UnsupportedOption
85
87
  end
86
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
+
87
97
  # Build the OptionParser object to handle the available options.
88
98
  #
89
99
 
@@ -98,13 +108,13 @@ module Puma
98
108
  file_config.load arg
99
109
  end
100
110
 
101
- o.on "--control URL", "The bind url to use for the control server",
102
- "Use 'auto' to use temp unix server" do |arg|
103
- if arg
104
- @control_url = arg
105
- elsif Puma.jruby?
106
- unsupported "No default url available on JRuby"
107
- end
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)
108
118
  end
109
119
 
110
120
  o.on "--control-token TOKEN",
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
@@ -157,6 +170,7 @@ module Puma
157
170
  if len == 0
158
171
  @body.rewind
159
172
  rest = io.read
173
+ rest = rest[2..-1] if rest.start_with?("\r\n")
160
174
  @buffer = rest.empty? ? nil : rest
161
175
  @requests_served += 1
162
176
  @ready = true
@@ -230,8 +244,16 @@ module Puma
230
244
 
231
245
  te = @env[TRANSFER_ENCODING2]
232
246
 
233
- if te && CHUNKED.casecmp(te) == 0
234
- return setup_chunked_body(body)
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
235
257
  end
236
258
 
237
259
  @chunked_body = false
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
 
@@ -281,7 +294,9 @@ module Puma
281
294
  begin
282
295
  b = server.backlog || 0
283
296
  r = server.running || 0
284
- payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r} }\n!
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!
285
300
  io << payload
286
301
  rescue IOError
287
302
  Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Puma
2
4
  # Rack::CommonLogger forwards every request to the given +app+, and
3
5
  # logs a line in the
@@ -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'