Hello community, here is the log from the commit of package rubygem-puma for openSUSE:Factory checked in at 2019-08-16 15:45:15 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rubygem-puma (Old) and /work/SRC/openSUSE:Factory/.rubygem-puma.new.22127 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-puma" Fri Aug 16 15:45:15 2019 rev:33 rq:723544 version:4.1.0 Changes: -------- --- /work/SRC/openSUSE:Factory/rubygem-puma/rubygem-puma.changes 2019-08-06 15:10:17.683776855 +0200 +++ /work/SRC/openSUSE:Factory/.rubygem-puma.new.22127/rubygem-puma.changes 2019-08-16 15:45:19.361705028 +0200 @@ -1,0 +2,30 @@ +Thu Aug 15 13:33:10 UTC 2019 - Manuel Schnitzer <[email protected]> + +- updated to version 4.1.0 + + * 4 features + * Add REQUEST_PATH on parse error message (#1831) + * You can now easily add custom log formatters with the `log_formatter` config option (#1816) + * Puma.stats now provides process start times (#1844) + * Add support for disabling TLSv1.1 (#1836) + + * 7 bugfixes + * Fix issue where Puma was creating zombie process entries (#1887) + * Fix bugs with line-endings and chunked encoding (#1812) + * RACK_URL_SCHEME is now set correctly in all conditions (#1491) + * We no longer mutate global STDOUT/STDERR, particularly the sync setting (#1837) + * SSL read_nonblock no longer blocks (#1857) + * Swallow connection errors when sending early hints (#1822) + * Backtrace no longer dumped when invalid pumactl commands are run (#1863) + + * 5 other + * Avoid casting worker_timeout twice (#1838) + * Removed a call to private that wasn't doing anything (#1882) + * README, Rakefile, docs and test cleanups (#1848, #1847, #1846, #1853, #1859, #1850, #1866, #1870, #1872, #1833, #1888) + * Puma.io has proper documentation now (https://puma.io/puma/) + * Added the Contributor Covenant CoC + + * 1 known issue + * Some users are still experiencing issues surrounding socket activation and Unix sockets (#1842) + +------------------------------------------------------------------- Old: ---- puma-4.0.1.gem New: ---- puma-4.1.0.gem ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rubygem-puma.spec ++++++ --- /var/tmp/diff_new_pack.Pmhwnk/_old 2019-08-16 15:45:21.037704540 +0200 +++ /var/tmp/diff_new_pack.Pmhwnk/_new 2019-08-16 15:45:21.037704540 +0200 @@ -24,7 +24,7 @@ # Name: rubygem-puma -Version: 4.0.1 +Version: 4.1.0 Release: 0 %define mod_name puma %define mod_full_name %{mod_name}-%{version} ++++++ puma-4.0.1.gem -> puma-4.1.0.gem ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/History.md new/History.md --- old/History.md 2019-07-11 19:49:53.000000000 +0200 +++ new/History.md 2019-08-08 21:28:03.000000000 +0200 @@ -6,11 +6,39 @@ * Bugfixes * Your bugfix goes here (#Github Number) +## 4.1.0 / 2019-08-08 + +* 4 features + * Add REQUEST_PATH on parse error message (#1831) + * You can now easily add custom log formatters with the `log_formatter` config option (#1816) + * Puma.stats now provides process start times (#1844) + * Add support for disabling TLSv1.1 (#1836) + +* 7 bugfixes + * Fix issue where Puma was creating zombie process entries (#1887) + * Fix bugs with line-endings and chunked encoding (#1812) + * RACK_URL_SCHEME is now set correctly in all conditions (#1491) + * We no longer mutate global STDOUT/STDERR, particularly the sync setting (#1837) + * SSL read_nonblock no longer blocks (#1857) + * Swallow connection errors when sending early hints (#1822) + * Backtrace no longer dumped when invalid pumactl commands are run (#1863) + +* 5 other + * Avoid casting worker_timeout twice (#1838) + * Removed a call to private that wasn't doing anything (#1882) + * README, Rakefile, docs and test cleanups (#1848, #1847, #1846, #1853, #1859, #1850, #1866, #1870, #1872, #1833, #1888) + * Puma.io has proper documentation now (https://puma.io/puma/) + * Added the Contributor Covenant CoC + +* 1 known issue + * Some users are still experiencing issues surrounding socket activation and Unix sockets (#1842) + ## 4.0.1 / 2019-07-11 * 2 bugfixes * Fix socket removed after reload - should fix problems with systemd socket activation. (#1829) * Add extconf tests for DTLS_method & TLS_server_method, use in minissl.rb. Should fix "undefined symbol: DTLS_method" when compiling against old OpenSSL versions. (#1832) +* 1 other * Removed unnecessary RUBY_VERSION checks. (#1827) ## 4.0.0 / 2019-06-25 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/README.md new/README.md --- old/README.md 2019-07-11 19:49:53.000000000 +0200 +++ new/README.md 2019-08-08 21:28:03.000000000 +0200 @@ -10,36 +10,38 @@ [](https://codeclimate.com/github/puma/puma) [](https://dependabot.com/compatibility-score.html?dependency-name=puma&package-manager=bundler&version-scheme=semver) -Puma is a **simple, fast, threaded, and highly concurrent HTTP 1.1 server for Ruby/Rack applications** in development and production. +Puma is a **simple, fast, multi-threaded, and highly concurrent HTTP 1.1 server for Ruby/Rack applications**. ## Built For Speed & Concurrency -Under the hood, Puma processes requests using a C-optimized Ragel extension (inherited from Mongrel) that provides fast, accurate HTTP 1.1 protocol parsing in a portable way. Puma then serves the request in a thread from an internal thread pool. Since each request is served in a separate thread, truly concurrent Ruby implementations (JRuby, Rubinius) will use all available CPU cores. +Puma processes requests using a C-optimized Ragel extension (inherited from Mongrel) that provides fast, accurate HTTP 1.1 protocol parsing in a portable way. Puma then serves the request using a thread pool. Each request is served in a separate thread, so truly concurrent Ruby implementations (JRuby, Rubinius) will use all available CPU cores. Puma was designed to be the go-to server for [Rubinius](https://rubini.us), but also works well with JRuby and MRI. -On MRI, there is a Global VM Lock (GVL) that ensures only one thread can run Ruby code at a time. But if you're doing a lot of blocking IO (such as HTTP calls to external APIs like Twitter), Puma still improves MRI's throughput by allowing blocking IO to be run concurrently. +On MRI, there is a Global VM Lock (GVL) that ensures only one thread can run Ruby code at a time. But if you're doing a lot of blocking IO (such as HTTP calls to external APIs like Twitter), Puma still improves MRI's throughput by allowing IO waiting to be done in parallel. ## Quick Start ``` $ gem install puma -$ puma <any rackup (*.ru) file> +$ puma ``` +Without arguments, puma will look for a rackup (.ru) file in the current working directory called `config.ru`. + ## Frameworks ### Rails -Puma is the default server for Rails, and should already be included in your Gemfile. +Puma is the default server for Rails, included in the generated Gemfile. -Then start your server with the `rails` command: +Start your server with the `rails` command: ``` -$ rails s +$ rails server ``` -Many configuration options are not available when using `rails s`. It is recommended that you use Puma's executable instead: +Many configuration options and Puma features are not available when using `rails server`. It is recommended that you use Puma's executable instead: ``` $ bundle exec puma @@ -53,7 +55,7 @@ $ ruby app.rb -s Puma ``` -Or you can configure your application to always use Puma: +Or you can configure your Sinatra application to always use Puma: ```ruby require 'sinatra' @@ -64,6 +66,9 @@ Puma provides numerous options. Consult `puma -h` (or `puma --help`) for a full list of CLI options, or see [dsl.rb](https://github.com/puma/puma/blob/master/lib/puma/dsl.rb). +You can also find several configuration examples as part of the +[test](test/config) suite. + ### Thread Pool Puma uses a thread pool. You can set the minimum and maximum number of threads that are available in the pool with the `-t` (or `--threads`) flag: @@ -72,9 +77,9 @@ $ puma -t 8:32 ``` -Puma will automatically scale the number of threads, from the minimum until it caps out at the maximum, based on how much traffic is present. The current default is `0:16`. Feel free to experiment, but be careful not to set the number of maximum threads to a large number, as you may exhaust resources on the system (or hit resource limits). +Puma will automatically scale the number of threads, from the minimum until it caps out at the maximum, based on how much traffic is present. The current default is `0:16`. Feel free to experiment, but be careful not to set the number of maximum threads to a large number, as you may exhaust resources on the system (or cause contention for the Global VM Lock, when using MRI). -Be aware that additionally Puma creates threads on its own for internal purposes (e.g. handling slow clients). So even if you specify -t 1:1, expect around 7 threads created in your application. +Be aware that additionally Puma creates threads on its own for internal purposes (e.g. handling slow clients). So, even if you specify -t 1:1, expect around 7 threads created in your application. ### Clustered mode @@ -84,9 +89,9 @@ $ puma -t 8:32 -w 3 ``` -Note that threads are still used in clustered mode, and the `-t` thread flag setting is per worker, so `-w 2 -t 16:16` will spawn 32 threads in total. +Note that threads are still used in clustered mode, and the `-t` thread flag setting is per worker, so `-w 2 -t 16:16` will spawn 32 threads in total, with 16 in each worker process. -In clustered mode, Puma may "preload" your application. This loads all the application code *prior* to forking. Preloading reduces total memory usage of your application via an operating system feature called [copy-on-write](https://en.wikipedia.org/wiki/Copy-on-write) (Ruby 2.0+ only). Use the `--preload` flag from the command line: +In clustered mode, Puma can "preload" your application. This loads all the application code *prior* to forking. Preloading reduces total memory usage of your application via an operating system feature called [copy-on-write](https://en.wikipedia.org/wiki/Copy-on-write) (Ruby 2.0+ only). Use the `--preload` flag from the command line: ``` $ puma -w 3 --preload @@ -111,8 +116,7 @@ This code can be used to setup the process before booting the application, allowing you to do some Puma-specific things that you don't want to embed in your application. -For instance, you could fire a log notification that a worker booted or send something to statsd. -This can be called multiple times. +For instance, you could fire a log notification that a worker booted or send something to statsd. This can be called multiple times. If you're preloading your application and using ActiveRecord, it's recommended that you setup your connection pool here: @@ -125,7 +129,7 @@ end ``` -On top of that, you can specify a block in your configuration file that will be run before workers are forked: +`before_fork` specifies a block to be run before workers are forked: ```ruby # config/puma.rb @@ -136,15 +140,29 @@ Preloading can’t be used with phased restart, since phased restart kills and restarts workers one-by-one, and preload_app copies the code of master into the workers. +### Error handling + +If puma encounters an error outside of the context of your application, it will respond with a 500 and a simple +textual error message (see `lowlevel_error` in [this file](https://github.com/puma/puma/blob/master/lib/puma/server.rb)). +You can specify custom behavior for this scenario. For example, you can report the error to your third-party +error-tracking service (in this example, [rollbar](http://rollbar.com)): + +```ruby +lowlevel_error_handler do |e| + Rollbar.critical(e) + [500, {}, ["An error has occurred, and engineers have been informed. Please reload the page. If you continue to have problems, contact [email protected]\n"]] +end +``` + ### Binding TCP / Sockets -In contrast to many other server configs which require multiple flags, Puma simply uses one URI parameter with the `-b` (or `--bind`) flag: +Bind Puma to a socket with the `-b` (or `--bind`) flag: ``` $ puma -b tcp://127.0.0.1:9292 ``` -Want to use UNIX Sockets instead of TCP (which can provide a 5-10% performance boost)? +To use a UNIX Socket instead of TCP: ``` $ puma -b unix:///var/run/puma.sock @@ -157,13 +175,14 @@ ``` Need a bit of security? Use SSL sockets: + ``` $ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert' ``` #### Controlling SSL Cipher Suites -Need to use or avoid specific SSL cipher suites? Use `ssl_cipher_filter` or `ssl_cipher_list` options. +To use or avoid specific SSL cipher suites, use `ssl_cipher_filter` or `ssl_cipher_list` options. ##### Ruby: @@ -179,7 +198,7 @@ See https://www.openssl.org/docs/man1.0.2/apps/ciphers.html for cipher filter format and full list of cipher suites. -Don't want to use insecure TLSv1.0 ? +Disable TLS v1 with the `no_tlsv1` option: ``` $ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&no_tlsv1=true' @@ -187,13 +206,13 @@ ### Control/Status Server -Puma has a built-in status/control app that can be used to query and control Puma itself. +Puma has a built-in status and control app that can be used to query and control Puma. ``` $ puma --control-url tcp://127.0.0.1:9293 --control-token foo ``` -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. +Puma will start the control server on localhost port 9293. All requests to the control server will need to include control token (in this case, `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 status app has available. You can also interact with the control server via `pumactl`. This command will restart Puma: @@ -205,13 +224,13 @@ ### Configuration File -You can also provide a configuration file which Puma will use with the `-C` (or `--config`) flag: +You can also provide a configuration file with the `-C` (or `--config`) flag: ``` $ puma -C /path/to/config ``` -If no configuration file is specified, Puma will look for a configuration file at `config/puma.rb`. If an environment is specified, either via the `-e` and `--environment` flags, or through the `RACK_ENV` environment variable, the default file location will be `config/puma/environment_name.rb`. +If no configuration file is specified, Puma will look for a configuration file at `config/puma.rb`. If an environment is specified, either via the `-e` and `--environment` flags, or through the `RACK_ENV` environment variable, Puma looks for configuration at `config/puma/<environment_name>.rb`. If you want to prevent Puma from looking for a configuration file in those locations, provide a dash as the argument to the `-C` (or `--config`) flag: @@ -219,7 +238,7 @@ $ puma -C "-" ``` -Take the following [sample configuration](https://github.com/puma/puma/blob/master/examples/config.rb) as inspiration or check out [dsl.rb](https://github.com/puma/puma/blob/master/lib/puma/dsl.rb) to see all available options. +Check out [dsl.rb](https://github.com/puma/puma/blob/master/lib/puma/dsl.rb) to see all available options. ## Restart @@ -236,7 +255,7 @@ Some platforms do not support all Puma features. * **JRuby**, **Windows**: server sockets are not seamless on restart, they must be closed and reopened. These platforms have no way to pass descriptors into a new process that is exposed to Ruby. Also, cluster mode is not supported due to a lack of fork(2). - * **Windows**: daemon mode is not supported due to a lack of fork(2). + * **Windows**: Cluster mode is not supported due to a lack of fork(2). ## Known Bugs @@ -278,6 +297,24 @@ $ bundle exec rake ``` +To run a single test file, use the `TEST` environment variable: + +```bash +$ TEST=test/test_binder.rb bundle exec rake test +``` + +Or use [`m`](https://github.com/qrush/m): + +``` +$ bundle exec m test/test_binder.rb +``` + +Which can also be used to run a single test case: + +``` +$ bundle exec m test/test_binder.rb:37 +``` + ## License Puma is copyright Evan Phoenix and contributors, licensed under the BSD 3-Clause license. See the included LICENSE file for details. Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ext/puma_http11/mini_ssl.c new/ext/puma_http11/mini_ssl.c --- old/ext/puma_http11/mini_ssl.c 2019-07-11 19:49:53.000000000 +0200 +++ new/ext/puma_http11/mini_ssl.c 2019-08-08 21:28:03.000000000 +0200 @@ -142,7 +142,7 @@ VALUE obj; SSL_CTX* ctx; SSL* ssl; - int ssl_options; + int min, ssl_options; ms_conn* conn = engine_alloc(self, &obj); @@ -168,6 +168,9 @@ ID sym_no_tlsv1 = rb_intern("no_tlsv1"); VALUE no_tlsv1 = rb_funcall(mini_ssl_ctx, sym_no_tlsv1, 0); + ID sym_no_tlsv1_1 = rb_intern("no_tlsv1_1"); + VALUE no_tlsv1_1 = rb_funcall(mini_ssl_ctx, sym_no_tlsv1_1, 0); + #ifdef HAVE_TLS_SERVER_METHOD ctx = SSL_CTX_new(TLS_server_method()); #else @@ -183,12 +186,36 @@ SSL_CTX_load_verify_locations(ctx, RSTRING_PTR(ca), NULL); } - ssl_options = 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; + ssl_options = SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION; - if(RTEST(no_tlsv1)) { +#ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION + if (RTEST(no_tlsv1_1)) { + min = TLS1_2_VERSION; + } + else if (RTEST(no_tlsv1)) { + min = TLS1_1_VERSION; + } + else { + min = TLS1_VERSION; + } + + SSL_CTX_set_min_proto_version(ctx, min); + + SSL_CTX_set_options(ctx, ssl_options); + +#else + /* As of 1.0.2f, SSL_OP_SINGLE_DH_USE key use is always on */ + ssl_options |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE; + + if (RTEST(no_tlsv1)) { ssl_options |= SSL_OP_NO_TLSv1; } + if(RTEST(no_tlsv1_1)) { + ssl_options |= SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1; + } SSL_CTX_set_options(ctx, ssl_options); +#endif + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); if (!NIL_P(ssl_cipher_filter)) { @@ -458,14 +485,35 @@ // OpenSSL Build / Runtime/Load versions /* Version of OpenSSL that Puma was compiled with */ - rb_define_const(mod, "OPENSSL_VERSION", rb_str_new2(OPENSSL_VERSION_TEXT)); + rb_define_const(mod, "OPENSSL_VERSION", rb_str_new2(OPENSSL_VERSION_TEXT)); #if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000 - /* Version of OpenSSL that Puma loaded with */ - rb_define_const(mod, "OPENSSL_LIBRARY_VERSION", rb_str_new2(OpenSSL_version(OPENSSL_VERSION))); + /* Version of OpenSSL that Puma loaded with */ + rb_define_const(mod, "OPENSSL_LIBRARY_VERSION", rb_str_new2(OpenSSL_version(OPENSSL_VERSION))); #else - rb_define_const(mod, "OPENSSL_LIBRARY_VERSION", rb_str_new2(SSLeay_version(SSLEAY_VERSION))); + rb_define_const(mod, "OPENSSL_LIBRARY_VERSION", rb_str_new2(SSLeay_version(SSLEAY_VERSION))); #endif + +#if defined(OPENSSL_NO_SSL3) || defined(OPENSSL_NO_SSL3_METHOD) + /* True if SSL3 is not available */ + rb_define_const(mod, "OPENSSL_NO_SSL3", Qtrue); +#else + rb_define_const(mod, "OPENSSL_NO_SSL3", Qfalse); +#endif + +#if defined(OPENSSL_NO_TLS1) || defined(OPENSSL_NO_TLS1_METHOD) + /* True if TLS1 is not available */ + rb_define_const(mod, "OPENSSL_NO_TLS1", Qtrue); +#else + rb_define_const(mod, "OPENSSL_NO_TLS1", Qfalse); +#endif + +#if defined(OPENSSL_NO_TLS1_1) || defined(OPENSSL_NO_TLS1_1_METHOD) + /* True if TLS1_1 is not available */ + rb_define_const(mod, "OPENSSL_NO_TLS1_1", Qtrue); +#else + rb_define_const(mod, "OPENSSL_NO_TLS1_1", Qfalse); +#endif rb_define_singleton_method(mod, "check", noop, 0); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ext/puma_http11/org/jruby/puma/MiniSSL.java new/ext/puma_http11/org/jruby/puma/MiniSSL.java --- old/ext/puma_http11/org/jruby/puma/MiniSSL.java 2019-07-11 19:49:53.000000000 +0200 +++ new/ext/puma_http11/org/jruby/puma/MiniSSL.java 2019-08-08 21:28:03.000000000 +0200 @@ -166,6 +166,10 @@ protocols = new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" }; } + if(miniSSLContext.callMethod(threadContext, "no_tlsv1_1").isTrue()) { + protocols = new String[] { "TLSv1.2" }; + } + engine.setEnabledProtocols(protocols); engine.setUseClientMode(false); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/accept_nonblock.rb new/lib/puma/accept_nonblock.rb --- old/lib/puma/accept_nonblock.rb 2019-07-11 19:49:53.000000000 +0200 +++ new/lib/puma/accept_nonblock.rb 2019-08-08 21:28:03.000000000 +0200 @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'openssl' module OpenSSL diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/app/status.rb new/lib/puma/app/status.rb --- old/lib/puma/app/status.rb 2019-07-11 19:49:53.000000000 +0200 +++ new/lib/puma/app/status.rb 2019-08-08 21:28:03.000000000 +0200 @@ -1,7 +1,11 @@ +# frozen_string_literal: true + require 'json' module Puma module App + # Check out {#call}'s source code to see what actions this web application + # can respond to. class Status def initialize(cli) @cli = cli diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/binder.rb new/lib/puma/binder.rb --- old/lib/puma/binder.rb 2019-07-11 19:49:53.000000000 +0200 +++ new/lib/puma/binder.rb 2019-08-08 21:28:03.000000000 +0200 @@ -195,6 +195,7 @@ end ctx.no_tlsv1 = true if params['no_tlsv1'] == 'true' + ctx.no_tlsv1_1 = true if params['no_tlsv1_1'] == 'true' if params['verify_mode'] ctx.verify_mode = case params['verify_mode'] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/client.rb new/lib/puma/client.rb --- old/lib/puma/client.rb 2019-07-11 19:49:53.000000000 +0200 +++ new/lib/puma/client.rb 2019-08-08 21:28:03.000000000 +0200 @@ -68,6 +68,8 @@ @remote_addr_header = nil @body_remain = 0 + + @in_last_chunk = false end attr_reader :env, :to_io, :body, :io, :timeout_at, :ready, :hijacked, @@ -108,6 +110,7 @@ @ready = false @body_remain = 0 @peerip = nil + @in_last_chunk = false if @buffer @parsed_bytes = @parser.execute(@env, @buffer, @parsed_bytes) @@ -166,7 +169,7 @@ chunk = chunk[@partial_part_left..-1] @partial_part_left = 0 else - @body << chunk + @body << chunk if @partial_part_left > 2 # don't include the last \r\n @partial_part_left -= chunk.size return false end @@ -184,12 +187,20 @@ if line.end_with?("\r\n") len = line.strip.to_i(16) if len == 0 + @in_last_chunk = true @body.rewind rest = io.read - rest = rest[2..-1] if rest.start_with?("\r\n") - @buffer = rest.empty? ? nil : rest - set_ready - return true + last_crlf_size = "\r\n".bytesize + if rest.bytesize < last_crlf_size + @buffer = nil + @partial_part_left = last_crlf_size - rest.bytesize + return false + else + @buffer = rest[last_crlf_size..-1] + @buffer = nil if @buffer.empty? + set_ready + return true + end end len += 2 @@ -219,7 +230,12 @@ end end - return false + if @in_last_chunk + set_ready + true + else + false + end end def read_chunked_body diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/cluster.rb new/lib/puma/cluster.rb --- old/lib/puma/cluster.rb 2019-07-11 19:49:53.000000000 +0200 +++ new/lib/puma/cluster.rb 2019-08-08 21:28:03.000000000 +0200 @@ -95,12 +95,13 @@ @signal = "TERM" @options = options @first_term_sent = nil + @started_at = Time.now @last_checkin = Time.now @last_status = '{}' @dead = false end - attr_reader :index, :pid, :phase, :signal, :last_checkin, :last_status + attr_reader :index, :pid, :phase, :signal, :last_checkin, :last_status, :started_at def booted? @stage == :booted @@ -226,9 +227,11 @@ # during this loop by giving the kernel time to kill them. sleep 1 if any - @workers.reject! { |w| Process.waitpid(w.pid, Process::WNOHANG) } - - @workers.reject!(&:dead?) + pids = [] + while pid = Process.waitpid(-1, Process::WNOHANG) do + pids << pid + end + @workers.reject! { |w| w.dead? || pids.include?(w.pid) } cull_workers spawn_workers @@ -376,8 +379,8 @@ def stats old_worker_count = @workers.count { |w| w.phase != @phase } booted_worker_count = @workers.count { |w| w.booted? } - worker_status = '[' + @workers.map { |w| %Q!{ "pid": #{w.pid}, "index": #{w.index}, "phase": #{w.phase}, "booted": #{w.booted?}, "last_checkin": "#{w.last_checkin.utc.iso8601}", "last_status": #{w.last_status} }!}.join(",") + ']' - %Q!{ "workers": #{@workers.size}, "phase": #{@phase}, "booted_workers": #{booted_worker_count}, "old_workers": #{old_worker_count}, "worker_status": #{worker_status} }! + worker_status = '[' + @workers.map { |w| %Q!{ "started_at": "#{w.started_at.utc.iso8601}", "pid": #{w.pid}, "index": #{w.index}, "phase": #{w.phase}, "booted": #{w.booted?}, "last_checkin": "#{w.last_checkin.utc.iso8601}", "last_status": #{w.last_status} }!}.join(",") + ']' + %Q!{ "started_at": "#{@started_at.utc.iso8601}", "workers": #{@workers.size}, "phase": #{@phase}, "booted_workers": #{booted_worker_count}, "old_workers": #{old_worker_count}, "worker_status": #{worker_status} }! end def preload? diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/configuration.rb new/lib/puma/configuration.rb --- old/lib/puma/configuration.rb 2019-07-11 19:49:53.000000000 +0200 +++ new/lib/puma/configuration.rb 2019-08-08 21:28:03.000000000 +0200 @@ -20,7 +20,7 @@ # In this class any "user" specified options take precedence over any # "file" specified options, take precedence over any "default" options. # - # User input is prefered over "defaults": + # User input is preferred over "defaults": # user_options = { foo: "bar" } # default_options = { foo: "zoo" } # options = UserFileDefaultOptions.new(user_options, default_options) @@ -32,7 +32,7 @@ # puts options.all_of(:foo) # # => ["bar", "zoo"] # - # A "file" option can be set. This config will be prefered over "default" options + # A "file" option can be set. This config will be preferred over "default" options # but will defer to any available "user" specified options. # # user_options = { foo: "bar" } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/const.rb new/lib/puma/const.rb --- old/lib/puma/const.rb 2019-07-11 19:49:53.000000000 +0200 +++ new/lib/puma/const.rb 2019-08-08 21:28:03.000000000 +0200 @@ -100,8 +100,8 @@ # too taxing on performance. module Const - PUMA_VERSION = VERSION = "4.0.1".freeze - CODE_NAME = "4 Fast 4 Furious".freeze + PUMA_VERSION = VERSION = "4.1.0".freeze + CODE_NAME = "Fourth and One".freeze PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze FAST_TRACK_KA_TIMEOUT = 0.2 @@ -160,6 +160,9 @@ LINE_END = "\r\n".freeze REMOTE_ADDR = "REMOTE_ADDR".freeze HTTP_X_FORWARDED_FOR = "HTTP_X_FORWARDED_FOR".freeze + HTTP_X_FORWARDED_SSL = "HTTP_X_FORWARDED_SSL".freeze + HTTP_X_FORWARDED_SCHEME = "HTTP_X_FORWARDED_SCHEME".freeze + HTTP_X_FORWARDED_PROTO = "HTTP_X_FORWARDED_PROTO".freeze SERVER_NAME = "SERVER_NAME".freeze SERVER_PORT = "SERVER_PORT".freeze diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/control_cli.rb new/lib/puma/control_cli.rb --- old/lib/puma/control_cli.rb 2019-07-11 19:49:53.000000000 +0200 +++ new/lib/puma/control_cli.rb 2019-08-08 21:28:03.000000000 +0200 @@ -101,7 +101,6 @@ rescue => e @stdout.puts e.message - @stdout.puts e.backtrace exit 1 end @@ -244,7 +243,6 @@ rescue => e message e.message - message e.backtrace exit 1 end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/dsl.rb new/lib/puma/dsl.rb --- old/lib/puma/dsl.rb 2019-07-11 19:49:53.000000000 +0200 +++ new/lib/puma/dsl.rb 2019-08-08 21:28:03.000000000 +0200 @@ -3,7 +3,7 @@ require 'puma/const' module Puma - # The methods that are available for use inside the config file. + # The methods that are available for use inside the configuration file. # These same methods are used in Puma cli and the rack handler # internally. # @@ -28,7 +28,8 @@ # puts config.options[:binds] # # => "tcp://127.0.0.1:3002" # - # Detailed docs can be found in `examples/config.rb` + # You can also find many examples being used by the test suite in + # +test/config+. class DSL include ConfigDefault @@ -81,9 +82,22 @@ @plugins << @config.load_plugin(name) end - # Use +obj+ or +block+ as the Rack app. This allows a config file to - # be the app itself. + # Use an object or block as the rack application. This allows the + # configuration file to be the application itself. # + # @example + # app do |env| + # body = 'Hello, World!' + # + # [ + # 200, + # { + # 'Content-Type' => 'text/plain', + # 'Content-Length' => body.length.to_s + # }, + # [body] + # ] + # end def app(obj=nil, &block) obj ||= block @@ -92,9 +106,20 @@ @options[:app] = obj end - # Start the Puma control rack app on +url+. This app can be communicated - # with to control the main server. - # + # Start the Puma control rack application on +url+. This application can + # be communicated with to control the main server. Additionally, you can + # provide an authentication token, so all requests to the control server + # will need to include that token as a query parameter. This allows for + # simple authentication. + # + # Check out {Puma::App::Status} to see what the app has available. + # + # @example + # activate_control_app 'unix:///var/run/pumactl.sock' + # @example + # activate_control_app 'unix:///var/run/pumactl.sock', { auth_token: '12345' } + # @example + # activate_control_app 'unix:///var/run/pumactl.sock', { no_token: true } def activate_control_app(url="auto", opts={}) if url == "auto" path = Configuration.temp_path @@ -127,22 +152,29 @@ @options[:config_files] << file end - # Adds a binding for the server to +url+. tcp://, unix://, and ssl:// are the only accepted - # protocols. Use query parameters within the url to specify options. - # - # @note multiple urls can be bound to, calling `bind` does not overwrite previous bindings. - # - # @example Explicitly the socket backlog depth (default is 1024) - # bind('unix:///var/run/puma.sock?backlog=2048') - # - # @example Set up ssl cert - # bind('ssl://127.0.0.1:9292?key=key.key&cert=cert.pem') - # - # @example Prefer low-latency over higher throughput (via `Socket::TCP_NODELAY`) - # bind('tcp://0.0.0.0:9292?low_latency=true') - # - # @example Set socket permissions - # bind('unix:///var/run/puma.sock?umask=0111') + # Bind the server to +url+. "tcp://", "unix://" and "ssl://" are the only + # accepted protocols. Multiple urls can be bound to, calling `bind` does + # not overwrite previous bindings. + # + # The default is "tcp://0.0.0.0:9292". + # + # You can use query parameters within the url to specify options: + # + # - Set the socket backlog depth with +backlog+, default is 1024. + # - Set up an SSL certificate with +key+ & +cert+. + # - Set whether to optimize for low latency instead of throughput with + # +low_latency+, default is to optimize for low latency. This is done + # via +Socket::TCP_NODELAY+. + # - Set socket permissions with +umask+. + # + # @example Backlog depth + # bind 'unix:///var/run/puma.sock?backlog=512' + # @example SSL cert + # bind 'ssl://127.0.0.1:9292?key=key.key&cert=cert.pem' + # @example Disable optimization for low latency + # bind 'tcp://0.0.0.0:9292?low_latency=false' + # @example Socket permissions + # bind 'unix:///var/run/puma.sock?umask=0111' def bind(url) @options[:binds] ||= [] @options[:binds] << url @@ -154,33 +186,40 @@ # Define the TCP port to bind to. Use +bind+ for more advanced options. # + # @example + # port 9292 def port(port, host=nil) host ||= default_host bind "tcp://#{host}:#{port}" end - # Define how long persistent connections can be idle before puma closes - # them - # + # Define how long persistent connections can be idle before Puma closes + # them. def persistent_timeout(seconds) @options[:persistent_timeout] = Integer(seconds) end - # Define how long the tcp socket stays open, if no data has been received - # + # Define how long the tcp socket stays open, if no data has been received. def first_data_timeout(seconds) @options[:first_data_timeout] = Integer(seconds) end # Work around leaky apps that leave garbage in Thread locals - # across requests - # + # across requests. def clean_thread_locals(which=true) @options[:clean_thread_locals] = which end - # Daemonize the server into the background. Highly suggest that - # this be combined with +pidfile+ and +stdout_redirect+. + # Daemonize the server into the background. It's highly recommended to + # use this in combination with +pidfile+ and +stdout_redirect+. + # + # The default is "false". + # + # @example + # daemonize + # + # @example + # daemonize false def daemonize(which=true) @options[:daemon] = which end @@ -193,7 +232,13 @@ @options[:drain_on_shutdown] = which end - # Set the environment in which the Rack's app will run. + # Set the environment in which the rack's app will run. The value must be + # a string. + # + # The default is "development". + # + # @example + # environment 'production' def environment(environment) @options[:environment] = environment end @@ -219,30 +264,41 @@ end # Code to run before doing a restart. This code should - # close logfiles, database connections, etc. + # close log files, database connections, etc. # # This can be called multiple times to add code each time. # + # @example + # on_restart do + # puts 'On restart...' + # end def on_restart(&block) @options[:on_restart] ||= [] @options[:on_restart] << block end - # Command to use to restart puma. This should be just how to - # load puma itself (ie. 'ruby -Ilib bin/puma'), not the arguments - # to puma, as those are the same as the original process. + # Command to use to restart Puma. This should be just how to + # load Puma itself (ie. 'ruby -Ilib bin/puma'), not the arguments + # to Puma, as those are the same as the original process. # + # @example + # restart_command '/u/app/lolcat/bin/restart_puma' def restart_command(cmd) @options[:restart_cmd] = cmd.to_s end - # Store the pid of the server in the file at +path+. + # Store the pid of the server in the file at "path". + # + # @example + # pidfile '/u/apps/lolcat/tmp/pids/puma.pid' def pidfile(path) @options[:pidfile] = path.to_s end - # Disable request logging. + # Disable request logging, if this isn't used it'll be enabled by default. # + # @example + # quiet def quiet(which=true) @options[:log_requests] = !which end @@ -261,6 +317,10 @@ # Load +path+ as a rackup file. # + # The default is "config.ru". + # + # @example + # rackup '/u/apps/lolcat/config.ru' def rackup(path) @options[:rackup] = path.to_s end @@ -275,16 +335,32 @@ @options[:early_hints] = answer end - # Redirect STDOUT and STDERR to files specified. + # Redirect STDOUT and STDERR to files specified. The +append+ parameter + # specifies whether the output is appended, the default is +false+. + # + # @example + # stdout_redirect '/app/lolcat/log/stdout', '/app/lolcat/log/stderr' + # @example + # stdout_redirect '/app/lolcat/log/stdout', '/app/lolcat/log/stderr', true def stdout_redirect(stdout=nil, stderr=nil, append=false) @options[:redirect_stdout] = stdout @options[:redirect_stderr] = stderr @options[:redirect_append] = append end + def log_formatter(&block) + @options[:log_formatter] = block + end + # Configure +min+ to be the minimum number of threads to use to answer # requests and +max+ the maximum. # + # The default is "0, 16". + # + # @example + # threads 0, 16 + # @example + # threads 5, 5 def threads(min, max) min = Integer(min) max = Integer(max) @@ -300,84 +376,135 @@ @options[:max_threads] = max end + # Instead of "bind 'ssl://127.0.0.1:9292?key=key_path&cert=cert_path'" you + # can also use the "ssl_bind" option. + # + # @example + # ssl_bind '127.0.0.1', '9292', { + # cert: path_to_cert, + # key: path_to_key, + # ssl_cipher_filter: cipher_filter, # optional + # verify_mode: verify_mode, # default 'none' + # } + # @example For JRuby additional keys are required: keystore & keystore_pass. + # ssl_bind '127.0.0.1', '9292', { + # cert: path_to_cert, + # key: path_to_key, + # ssl_cipher_filter: cipher_filter, # optional + # verify_mode: verify_mode, # default 'none' + # keystore: path_to_keystore, + # keystore_pass: password + # } def ssl_bind(host, port, opts) verify = opts.fetch(:verify_mode, 'none') no_tlsv1 = opts.fetch(:no_tlsv1, 'false') + no_tlsv1_1 = opts.fetch(:no_tlsv1_1, 'false') ca_additions = "&ca=#{opts[:ca]}" if ['peer', 'force_peer'].include?(verify) if defined?(JRUBY_VERSION) keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}" - bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}&#{keystore_additions}&verify_mode=#{verify}&no_tlsv1=#{no_tlsv1}#{ca_additions}" + bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}&#{keystore_additions}&verify_mode=#{verify}&no_tlsv1=#{no_tlsv1}&no_tlsv1_1=#{no_tlsv1_1}#{ca_additions}" else ssl_cipher_filter = "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" if opts[:ssl_cipher_filter] - bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}#{ssl_cipher_filter}&verify_mode=#{verify}&no_tlsv1=#{no_tlsv1}#{ca_additions}" + bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}#{ssl_cipher_filter}&verify_mode=#{verify}&no_tlsv1=#{no_tlsv1}&no_tlsv1_1=#{no_tlsv1_1}#{ca_additions}" end end # Use +path+ as the file to store the server info state. This is - # used by pumactl to query and control the server. + # used by +pumactl+ to query and control the server. # + # @example + # state_path '/u/apps/lolcat/tmp/pids/puma.state' def state_path(path) @options[:state] = path.to_s end - # *Cluster mode only* How many worker processes to run. + # How many worker processes to run. Typically this is set to + # to the number of available cores. + # + # The default is 0. # + # @note Cluster mode only. def workers(count) @options[:workers] = count.to_i end - # *Cluster mode only* Code to run immediately before master process + # Code to run immediately before master process # forks workers (once on boot). These hooks can block if necessary - # to wait for background operations unknown to puma to finish before + # to wait for background operations unknown to Puma to finish before # the process terminates. - # This can be used to close any connections to remote servers (database, redis, ...) - # that were opened when preloading the code + # This can be used to close any connections to remote servers (database, + # Redis, ...) that were opened when preloading the code. # - # This can be called multiple times to add hooks. + # This can be called multiple times to add several hooks. # + # @note Cluster mode only. + # @example + # before_fork do + # puts "Starting workers..." + # end def before_fork(&block) @options[:before_fork] ||= [] @options[:before_fork] << block end - # *Cluster mode only* Code to run in a worker when it boots to setup + # Code to run in a worker when it boots to setup # the process before booting the app. # - # This can be called multiple times to add hooks. + # This can be called multiple times to add several hooks. # + # @note Cluster mode only. + # @example + # on_worker_fork do + # puts 'Before worker fork...' + # end def on_worker_boot(&block) @options[:before_worker_boot] ||= [] @options[:before_worker_boot] << block end - # *Cluster mode only* Code to run immediately before a worker shuts + # Code to run immediately before a worker shuts # down (after it has finished processing HTTP requests). These hooks # can block if necessary to wait for background operations unknown - # to puma to finish before the process terminates. + # to Puma to finish before the process terminates. # - # This can be called multiple times to add hooks. + # This can be called multiple times to add several hooks. # + # @note Cluster mode only. + # @example + # on_worker_shutdown do + # puts 'On worker shutdown...' + # end def on_worker_shutdown(&block) @options[:before_worker_shutdown] ||= [] @options[:before_worker_shutdown] << block end - # *Cluster mode only* Code to run in the master when it is - # about to create the worker by forking itself. + # Code to run in the master right before a worker is started. The worker's + # index is passed as an argument. # - # This can be called multiple times to add hooks. + # This can be called multiple times to add several hooks. # + # @note Cluster mode only. + # @example + # on_worker_fork do + # puts 'Before worker fork...' + # end def on_worker_fork(&block) @options[:before_worker_fork] ||= [] @options[:before_worker_fork] << block end - # *Cluster mode only* Code to run in the master after it starts - # a worker. + # Code to run in the master after a worker has been started. The worker's + # index is passed as an argument. # - # This can be called multiple times to add hooks. + # This is called everytime a worker is to be started. # + # @note Cluster mode only. + # @example + # after_worker_fork do + # puts 'After worker fork...' + # end def after_worker_fork(&block) @options[:after_worker_fork] ||= [] @options[:after_worker_fork] = block @@ -393,14 +520,18 @@ # This hook is useful for running out-of-band garbage collection # or scheduling asynchronous tasks to execute after a response. # - # This can be called multiple times to add hooks. - # + # This can be called multiple times to add several hooks. def out_of_band(&block) @options[:out_of_band] ||= [] @options[:out_of_band] << block end # The directory to operate out of. + # + # The default is the current directory. + # + # @example + # directory '/u/apps/lolcat' def directory(dir) @options[:directory] = dir.to_s end @@ -411,22 +542,28 @@ directory dir end - # Run the app as a raw TCP app instead of an HTTP rack app + # Run the app as a raw TCP app instead of an HTTP rack app. def tcp_mode @options[:mode] = :tcp end - # *Cluster mode only* Preload the application before starting - # the workers and setting up the listen ports. This conflicts - # with using the phased restart feature, you can't use both. + # Preload the application before starting the workers; this conflicts with + # phased restart feature. This is off by default. # + # @note Cluster mode only. + # @example + # preload_app! def preload_app!(answer=true) @options[:preload_app] = answer end - # Use +obj+ or +block+ as the low level error handler. This allows a config file to - # change the default error on the server. + # Use +obj+ or +block+ as the low level error handler. This allows the + # configuration file to change the default error on the server. # + # @example + # lowlevel_error_handler do |err| + # [200, {}, ["error page"]] + # end def lowlevel_error_handler(obj=nil, &block) obj ||= block raise "Provide either a #call'able or a block" unless obj @@ -436,38 +573,57 @@ # This option is used to allow your app and its gems to be # properly reloaded when not using preload. # - # When set, if puma detects that it's been invoked in the + # When set, if Puma detects that it's been invoked in the # context of Bundler, it will cleanup the environment and # re-run itself outside the Bundler environment, but directly # using the files that Bundler has setup. # - # This means that puma is now decoupled from your Bundler + # This means that Puma is now decoupled from your Bundler # context and when each worker loads, it will be loading a # new Bundler context and thus can float around as the release # dictates. + # + # @note This is incompatible with +preload_app!+. def prune_bundler(answer=true) @options[:prune_bundler] = answer end - # In environments where SIGTERM is something expected, instructing - # puma to shutdown gracefully ( for example in Kubernetes, where - # rolling restart is guaranteed usually on infrastructure level ) - # SignalException should not be raised for SIGTERM + # By default, Puma will raise SignalException when SIGTERM is received. In + # environments where SIGTERM is something expected, you can suppress these + # with this option. # - # When set to false, if puma process receives SIGTERM, it won't raise SignalException + # This can be useful for example in Kubernetes, where rolling restart is + # guaranteed usually on infrastructure level. + # + # @example + # raise_exception_on_sigterm false def raise_exception_on_sigterm(answer=true) @options[:raise_exception_on_sigterm] = answer end - # Additional text to display in process listing + # Additional text to display in process listing. + # + # If you do not specify a tag, Puma will infer it. If you do not want Puma + # to add a tag, use an empty string. + # + # @example + # tag 'app name' + # @example + # tag '' def tag(string) @options[:tag] = string.to_s end - # *Cluster mode only* Set the timeout for workers in seconds - # When set the master process will terminate any workers - # that have not checked in within the given +timeout+. - # This mitigates hung processes. Default value is 60 seconds. + # Verifies that all workers have checked in to the master process within + # the given timeout. If not the worker process will be restarted. This is + # not a request timeout, it is to protect against a hung or dead process. + # Setting this value will not protect against slow requests. + # + # The minimum value is 6 seconds, the default value is 60 seconds. + # + # @note Cluster mode only. + # @example + # worker_timeout 60 def worker_timeout(timeout) timeout = Integer(timeout) min = Const::WORKER_CHECK_INTERVAL @@ -476,15 +632,23 @@ raise "The minimum worker_timeout must be greater than the worker reporting interval (#{min})" end - @options[:worker_timeout] = Integer(timeout) + @options[:worker_timeout] = timeout end - # *Cluster mode only* Set the timeout for workers to boot + # Change the default worker timeout for booting. + # + # If unspecified, this defaults to the value of worker_timeout. + # + # @note Cluster mode only. + # @example: + # worker_boot_timeout 60 def worker_boot_timeout(timeout) @options[:worker_boot_timeout] = Integer(timeout) end - # *Cluster mode only* Set the timeout for worker shutdown + # Set the timeout for worker shutdown + # + # @note Cluster mode only. def worker_shutdown_timeout(timeout) @options[:worker_shutdown_timeout] = Integer(timeout) end @@ -501,7 +665,7 @@ # Note that setting this to false disables HTTP keepalive and # slow clients will occupy a handler thread while the request # is being sent. A reverse proxy, such as nginx, can handle - # slow clients and queue requests before they reach puma. + # slow clients and queue requests before they reach Puma. def queue_requests(answer=true) @options[:queue_requests] = answer end @@ -530,7 +694,7 @@ # is used, allowing headers such as X-Forwarded-For # to be used as well. # * Any string - this allows you to hardcode remote address to any value - # you wish. Because puma never uses this field anyway, it's + # you wish. Because Puma never uses this field anyway, it's # format is entirely in your hands. def set_remote_address(val=:socket) case val diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/events.rb new/lib/puma/events.rb --- old/lib/puma/events.rb 2019-07-11 19:49:53.000000000 +0200 +++ new/lib/puma/events.rb 2019-08-08 21:28:03.000000000 +0200 @@ -29,8 +29,8 @@ # def initialize(stdout, stderr) @formatter = DefaultFormatter.new - @stdout = stdout - @stderr = stderr + @stdout = stdout.dup + @stderr = stderr.dup @stdout.sync = true @stderr.sync = true @@ -93,7 +93,10 @@ # parsing exception. # def parse_error(server, env, error) - @stderr.puts "#{Time.now}: HTTP parse error, malformed request (#{env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR]}): #{error.inspect}\n---\n" + @stderr.puts "#{Time.now}: HTTP parse error, malformed request " \ + "(#{env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR]}#{env[REQUEST_PATH]}): " \ + "#{error.inspect}" \ + "\n---\n" end # An SSL error has occurred. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/launcher.rb new/lib/puma/launcher.rb --- old/lib/puma/launcher.rb 2019-07-11 19:49:53.000000000 +0200 +++ new/lib/puma/launcher.rb 2019-08-08 21:28:03.000000000 +0200 @@ -63,6 +63,9 @@ @options = @config.options @config.clamp + @events.formatter = Events::PidFormatter.new if clustered? + @events.formatter = options[:log_formatter] if @options[:log_formatter] + generate_restart_data if clustered? && !Process.respond_to?(:fork) @@ -81,7 +84,6 @@ set_rack_environment if clustered? - @events.formatter = Events::PidFormatter.new @options[:logger] = @events @runner = Cluster.new(self, @events) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/minissl.rb new/lib/puma/minissl.rb --- old/lib/puma/minissl.rb 2019-07-11 19:49:53.000000000 +0200 +++ new/lib/puma/minissl.rb 2019-08-08 21:28:03.000000000 +0200 @@ -54,22 +54,21 @@ output = engine_read_all return output if output - begin - data = @socket.read_nonblock(size, exception: false) - if data == :wait_readable || data == :wait_writable - if @socket.to_io.respond_to?(data) - @socket.to_io.__send__(data) - elsif data == :wait_readable - IO.select([@socket.to_io]) - else - IO.select(nil, [@socket.to_io]) - end - elsif !data - return nil - else - break - end - end while true + data = @socket.read_nonblock(size, exception: false) + if data == :wait_readable || data == :wait_writable + # It would make more sense to let @socket.read_nonblock raise + # EAGAIN if necessary but it seems like it'll misbehave on Windows. + # I don't have a Windows machine to debug this so I can't explain + # exactly whats happening in that OS. Please let me know if you + # find out! + # + # In the meantime, we can emulate the correct behavior by + # capturing :wait_readable & :wait_writable and raising EAGAIN + # ourselves. + raise IO::EAGAINWaitReadable + elsif data.nil? + return nil + end @engine.inject(data) output = engine_read_all @@ -177,10 +176,11 @@ class Context attr_accessor :verify_mode - attr_reader :no_tlsv1 + attr_reader :no_tlsv1, :no_tlsv1_1 def initialize - @no_tlsv1 = false + @no_tlsv1 = false + @no_tlsv1_1 = false end if defined?(JRUBY_VERSION) @@ -220,18 +220,24 @@ @ca = ca end - def check raise "Key not configured" unless @key raise "Cert not configured" unless @cert end end + # disables TLSv1 def no_tlsv1=(tlsv1) raise ArgumentError, "Invalid value of no_tlsv1" unless ['true', 'false', true, false].include?(tlsv1) @no_tlsv1 = tlsv1 end + # disables TLSv1 and TLSv1.1. Overrides `#no_tlsv1=` + def no_tlsv1_1=(tlsv1_1) + raise ArgumentError, "Invalid value of no_tlsv1" unless ['true', 'false', true, false].include?(tlsv1_1) + @no_tlsv1_1 = tlsv1_1 + end + end VERIFY_NONE = 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/plugin/tmp_restart.rb new/lib/puma/plugin/tmp_restart.rb --- old/lib/puma/plugin/tmp_restart.rb 2019-07-11 19:49:53.000000000 +0200 +++ new/lib/puma/plugin/tmp_restart.rb 2019-08-08 21:28:03.000000000 +0200 @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'puma/plugin' Puma::Plugin.create do diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/rack/builder.rb new/lib/puma/rack/builder.rb --- old/lib/puma/rack/builder.rb 2019-07-11 19:49:53.000000000 +0200 +++ new/lib/puma/rack/builder.rb 2019-08-08 21:28:03.000000000 +0200 @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Puma end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/rack/urlmap.rb new/lib/puma/rack/urlmap.rb --- old/lib/puma/rack/urlmap.rb 2019-07-11 19:49:53.000000000 +0200 +++ new/lib/puma/rack/urlmap.rb 2019-08-08 21:28:03.000000000 +0200 @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Puma::Rack # Rack::URLMap takes a hash mapping urls or paths to apps, and # dispatches accordingly. Support for HTTP/1.1 host names exists if diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/rack_default.rb new/lib/puma/rack_default.rb --- old/lib/puma/rack_default.rb 2019-07-11 19:49:53.000000000 +0200 +++ new/lib/puma/rack_default.rb 2019-08-08 21:28:03.000000000 +0200 @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'rack/handler/puma' module Rack::Handler diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/reactor.rb new/lib/puma/reactor.rb --- old/lib/puma/reactor.rb 2019-07-11 19:49:53.000000000 +0200 +++ new/lib/puma/reactor.rb 2019-08-08 21:28:03.000000000 +0200 @@ -23,7 +23,7 @@ # A connection comes into a `Puma::Server` instance, it is then passed to a `Puma::Reactor` instance, # which stores it in an array and waits for any of the connections to be ready for reading. # - # The waiting/wake up is performed with nio4r, which will use the apropriate backend (libev, Java NIO or + # The waiting/wake up is performed with nio4r, which will use the appropriate backend (libev, Java NIO or # just plain IO#select). The call to `NIO::Selector#select` will "wake up" and # return the references to any objects that caused it to "wake". The reactor # then loops through each of these request objects, and sees if they're complete. If they diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/runner.rb new/lib/puma/runner.rb --- old/lib/puma/runner.rb 2019-07-11 19:49:53.000000000 +0200 +++ new/lib/puma/runner.rb 2019-08-08 21:28:03.000000000 +0200 @@ -14,6 +14,7 @@ @options = cli.options @app = nil @control = nil + @started_at = Time.now end def daemon? diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/server.rb new/lib/puma/server.rb --- old/lib/puma/server.rb 2019-07-11 19:49:53.000000000 +0200 +++ new/lib/puma/server.rb 2019-08-08 21:28:03.000000000 +0200 @@ -588,8 +588,11 @@ end def default_server_port(env) - return PORT_443 if env[HTTPS_KEY] == 'on' || env[HTTPS_KEY] == 'https' - env['HTTP_X_FORWARDED_PROTO'] == 'https' ? PORT_443 : PORT_80 + if ['on', HTTPS].include?(env[HTTPS_KEY]) || env[HTTP_X_FORWARDED_PROTO].to_s[0...5] == HTTPS || env[HTTP_X_FORWARDED_SCHEME] == HTTPS || env[HTTP_X_FORWARDED_SSL] == "on" + PORT_443 + else + PORT_80 + end end # Takes the request +req+, invokes the Rack application to construct @@ -627,23 +630,27 @@ head = env[REQUEST_METHOD] == HEAD env[RACK_INPUT] = body - env[RACK_URL_SCHEME] = env[HTTPS_KEY] ? HTTPS : HTTP + env[RACK_URL_SCHEME] = default_server_port(env) == PORT_443 ? HTTPS : HTTP if @early_hints env[EARLY_HINTS] = lambda { |headers| - fast_write client, "HTTP/1.1 103 Early Hints\r\n".freeze + begin + fast_write client, "HTTP/1.1 103 Early Hints\r\n".freeze - headers.each_pair do |k, vs| - if vs.respond_to?(:to_s) && !vs.to_s.empty? - vs.to_s.split(NEWLINE).each do |v| - fast_write client, "#{k}: #{v}\r\n" + headers.each_pair do |k, vs| + if vs.respond_to?(:to_s) && !vs.to_s.empty? + vs.to_s.split(NEWLINE).each do |v| + fast_write client, "#{k}: #{v}\r\n" + end + else + fast_write client, "#{k}: #{vs}\r\n" end - else - fast_write client, "#{k}: #{vs}\r\n" end - end - fast_write client, "\r\n".freeze + fast_write client, "\r\n".freeze + rescue ConnectionError + # noop, if we lost the socket we just won't send the early hints + end } end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/single.rb new/lib/puma/single.rb --- old/lib/puma/single.rb 2019-07-11 19:49:53.000000000 +0200 +++ new/lib/puma/single.rb 2019-08-08 21:28:03.000000000 +0200 @@ -18,7 +18,7 @@ r = @server.running || 0 t = @server.pool_capacity || 0 m = @server.max_threads || 0 - %Q!{ "backlog": #{b}, "running": #{r}, "pool_capacity": #{t}, "max_threads": #{m} }! + %Q!{ "started_at": "#{@started_at.utc.iso8601}", "backlog": #{b}, "running": #{r}, "pool_capacity": #{t}, "max_threads": #{m} }! end def restart diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma.rb new/lib/puma.rb --- old/lib/puma.rb 2019-07-11 19:49:53.000000000 +0200 +++ new/lib/puma.rb 2019-08-08 21:28:03.000000000 +0200 @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Standard libraries require 'socket' require 'tempfile' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rack/handler/puma.rb new/lib/rack/handler/puma.rb --- old/lib/rack/handler/puma.rb 2019-07-11 19:49:53.000000000 +0200 +++ new/lib/rack/handler/puma.rb 2019-08-08 21:28:03.000000000 +0200 @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'rack/handler' module Rack @@ -86,7 +88,7 @@ "Verbose" => "Don't report each request (default: false)" } end - private + def self.set_host_port_to_config(host, port, config) config.clear_binds! if host || port diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/metadata new/metadata --- old/metadata 2019-07-11 19:49:53.000000000 +0200 +++ new/metadata 2019-08-08 21:28:03.000000000 +0200 @@ -1,14 +1,14 @@ --- !ruby/object:Gem::Specification name: puma version: !ruby/object:Gem::Version - version: 4.0.1 + version: 4.1.0 platform: ruby authors: - Evan Phoenix autorequire: bindir: bin cert_chain: [] -date: 2019-07-11 00:00:00.000000000 Z +date: 2019-08-08 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: nio4r diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tools/jungle/init.d/puma new/tools/jungle/init.d/puma --- old/tools/jungle/init.d/puma 2019-07-11 19:49:53.000000000 +0200 +++ new/tools/jungle/init.d/puma 2019-08-08 21:28:03.000000000 +0200 @@ -398,7 +398,7 @@ ;; remove) if [ "$#" -lt 2 ]; then - echo "Please, specifiy the app's directory to remove." + echo "Please, specify the app's directory to remove." exit 1 else do_remove $2
