Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package rubygem-puma for openSUSE:Factory checked in at 2022-02-14 22:36:09 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rubygem-puma (Old) and /work/SRC/openSUSE:Factory/.rubygem-puma.new.1956 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-puma" Mon Feb 14 22:36:09 2022 rev:49 rq:954164 version:5.6.2 Changes: -------- --- /work/SRC/openSUSE:Factory/rubygem-puma/rubygem-puma.changes 2021-12-22 20:19:09.091877561 +0100 +++ /work/SRC/openSUSE:Factory/.rubygem-puma.new.1956/rubygem-puma.changes 2022-02-14 22:37:05.153556713 +0100 @@ -1,0 +2,11 @@ +Sat Feb 12 16:18:43 UTC 2022 - Marcus Rueckert <mrueck...@suse.de> + +- Update to version 5.6.2 + https://github.com/advisories/GHSA-rmj8-8hhh-gv5h + https://rubysec.com/advisories/CVE-2022-23634/ + + other changes: + https://github.com/puma/puma/releases/tag/v5.6.1 + https://github.com/puma/puma/releases/tag/v5.6.0 + +------------------------------------------------------------------- Old: ---- puma-5.5.2.gem New: ---- puma-5.6.2.gem ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rubygem-puma.spec ++++++ --- /var/tmp/diff_new_pack.qINhO6/_old 2022-02-14 22:37:05.585557839 +0100 +++ /var/tmp/diff_new_pack.qINhO6/_new 2022-02-14 22:37:05.589557849 +0100 @@ -1,7 +1,7 @@ # # spec file for package rubygem-puma # -# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -24,7 +24,7 @@ # Name: rubygem-puma -Version: 5.5.2 +Version: 5.6.2 Release: 0 %define mod_name puma %define mod_full_name %{mod_name}-%{version} ++++++ puma-5.5.2.gem -> puma-5.6.2.gem ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/History.md new/History.md --- old/History.md 2021-10-13 01:07:08.000000000 +0200 +++ new/History.md 1980-01-01 01:00:00.000000000 +0100 @@ -1,3 +1,36 @@ +## 5.6.2 / 2022-02-11 + +* Bugfix/Security + * Response body will always be `close`d. (GHSA-rmj8-8hhh-gv5h, related to [#2809]) + +## 5.6.1 / 2022-01-26 + +* Bugfixes + * Reverted a commit which appeared to be causing occasional blank header values ([#2809]) + +## 5.6.0 / 2022-01-25 + +* Features + * Support `localhost` integration in `ssl_bind` ([#2764], [#2708]) + * Allow backlog parameter to be set with ssl_bind DSL ([#2780]) + * Remove yaml (psych) requirement in StateFile ([#2784]) + * Allow culling of oldest workers, previously was only youngest ([#2773], [#2794]) + * Add worker_check_interval configuration option ([#2759]) + * Always send lowlevel_error response to client ([#2731], [#2341]) + * Support for cert_pem and key_pem with ssl_bind DSL ([#2728]) + +* Bugfixes + * Keep thread names under 15 characters, prevents breakage on some OSes ([#2733]) + * Fix two 'old-style-definition' compile warning ([#2807], [#2806]) + * Log environment correctly using option value ([#2799]) + * Fix warning from Ruby master (will be 3.2.0) ([#2785]) + * extconf.rb - fix openssl with old Windows builds ([#2757]) + * server.rb - rescue handling (`Errno::EBADF`) for `@notify.close` ([#2745]) + +* Refactor + * server.rb - refactor code using @options[:remote_address] ([#2742]) + * [jruby] a couple refactorings - avoid copy-ing bytes ([#2730]) + ## 5.5.2 / 2021-10-12 * Bugfixes @@ -5,6 +38,9 @@ ## 5.5.1 / 2021-10-12 +* Feature (added as mistake - we don't normally do this on bugfix releases, sorry!) + * Allow setting APP_ENV in preference to RACK_ENV or RAILS_ENV ([#2702]) + * Security * Do not allow LF as a line ending in a header (CVE-2021-41136) @@ -261,6 +297,11 @@ * Support parallel tests in verbose progress reporting ([#2223]) * Refactor error handling in server accept loop ([#2239]) +## 4.3.10 / 2021-10-12 + +* Bugfixes + * Allow UTF-8 in HTTP header values + ## 4.3.9 / 2021-10-12 * Security @@ -1799,6 +1840,27 @@ * Bugfixes * Your bugfix goes here <Most recent on the top, like GitHub> (#Github Number) +[#2809]:https://github.com/puma/puma/pull/2809 "PR by @dentarg, merged 2022-01-26" +[#2764]:https://github.com/puma/puma/pull/2764 "PR by @dentarg, merged 2022-01-18" +[#2708]:https://github.com/puma/puma/issues/2708 "Issue by @erikaxel, closed 2022-01-18" +[#2780]:https://github.com/puma/puma/pull/2780 "PR by @dalibor, merged 2022-01-01" +[#2784]:https://github.com/puma/puma/pull/2784 "PR by @MSP-Greg, merged 2022-01-01" +[#2773]:https://github.com/puma/puma/pull/2773 "PR by @ob-stripe, merged 2022-01-01" +[#2794]:https://github.com/puma/puma/pull/2794 "PR by @johnnyshields, merged 2022-01-10" +[#2759]:https://github.com/puma/puma/pull/2759 "PR by @ob-stripe, merged 2021-12-11" +[#2731]:https://github.com/puma/puma/pull/2731 "PR by @baelter, merged 2021-11-02" +[#2341]:https://github.com/puma/puma/issues/2341 "Issue by @cjlarose, closed 2021-11-02" +[#2728]:https://github.com/puma/puma/pull/2728 "PR by @dalibor, merged 2021-10-31" +[#2733]:https://github.com/puma/puma/pull/2733 "PR by @ob-stripe, merged 2021-12-12" +[#2807]:https://github.com/puma/puma/pull/2807 "PR by @MSP-Greg, merged 2022-01-25" +[#2806]:https://github.com/puma/puma/issues/2806 "Issue by @olleolleolle, closed 2022-01-25" +[#2799]:https://github.com/puma/puma/pull/2799 "PR by @ags, merged 2022-01-22" +[#2785]:https://github.com/puma/puma/pull/2785 "PR by @MSP-Greg, merged 2022-01-02" +[#2757]:https://github.com/puma/puma/pull/2757 "PR by @MSP-Greg, merged 2021-11-24" +[#2745]:https://github.com/puma/puma/pull/2745 "PR by @MSP-Greg, merged 2021-11-03" +[#2742]:https://github.com/puma/puma/pull/2742 "PR by @MSP-Greg, merged 2021-12-12" +[#2730]:https://github.com/puma/puma/pull/2730 "PR by @kares, merged 2021-11-01" +[#2702]:https://github.com/puma/puma/pull/2702 "PR by @jacobherrington, merged 2021-09-21" [#2610]:https://github.com/puma/puma/pull/2610 "PR by @ye-lin-aung, merged 2021-08-18" [#2257]:https://github.com/puma/puma/issues/2257 "Issue by @nateberkopec, closed 2021-08-18" [#2654]:https://github.com/puma/puma/pull/2654 "PR by @Roguelazer, merged 2021-09-07" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/README.md new/README.md --- old/README.md 2021-10-13 01:07:08.000000000 +0200 +++ new/README.md 1980-01-01 01:00:00.000000000 +0100 @@ -137,6 +137,11 @@ 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. +Constants loaded by your application (such as `Rails`) will not be available in `on_worker_boot`. +However, these constants _will_ be available if `preload_app!` is enabled, either explicitly in your `puma` config or automatically if +using 2 or more workers in cluster mode. +If `preload_app!` is not enabled and 1 worker is used, then `on_worker_boot` will fire, but your app will not be preloaded and constants will not be available. + `before_fork` specifies a block to be run before workers are forked: ```ruby Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/docs/architecture.md new/docs/architecture.md --- old/docs/architecture.md 2021-10-13 01:07:08.000000000 +0200 +++ new/docs/architecture.md 1980-01-01 01:00:00.000000000 +0100 @@ -31,10 +31,10 @@  * Upon startup, Puma listens on a TCP or UNIX socket. - * The backlog of this socket is configured (with a default of 1024). The - backlog determines the size of the queue for unaccepted connections. - Generally, you'll never hit the backlog cap in production. If the backlog is - full, the operating system refuses new connections. + * The backlog of this socket is configured with a default of 1024, but the + actual backlog value is capped by the `net.core.somaxconn` sysctl value. + The backlog determines the size of the queue for unaccepted connections. If + the backlog is full, the operating system is not accepting new connections. * This socket backlog is distinct from the `backlog` of work as reported by `Puma.stats` or the control server. The backlog that `Puma.stats` refers to represents the number of connections in the process' `todo` set waiting for diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/docs/signals.md new/docs/signals.md --- old/docs/signals.md 2021-10-13 01:07:08.000000000 +0200 +++ new/docs/signals.md 1980-01-01 01:00:00.000000000 +0100 @@ -42,6 +42,7 @@ - `INT ` equivalent of sending Ctrl-C to cluster. Puma will attempt to finish then exit. - `CHLD` - `URG ` refork workers in phases from worker 0 if `fork_workers` option is enabled. +- `INFO` print backtraces of all puma threads ## Callbacks order in case of different signals diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ext/puma_http11/extconf.rb new/ext/puma_http11/extconf.rb --- old/ext/puma_http11/extconf.rb 2021-10-13 01:07:08.000000000 +0200 +++ new/ext/puma_http11/extconf.rb 1980-01-01 01:00:00.000000000 +0100 @@ -11,7 +11,7 @@ unless ENV["DISABLE_SSL"] dir_config("openssl") - found_ssl = if pkg_config 'openssl' + found_ssl = if (!$mingw || RUBY_VERSION >= '2.4') && (t = pkg_config 'openssl') puts 'using OpenSSL pkgconfig (openssl.pc)' true elsif %w'crypto libeay32'.find {|crypto| have_library(crypto, 'BIO_read')} && @@ -33,11 +33,14 @@ have_func "SSL_CTX_set_min_proto_version(NULL, 0)", "openssl/ssl.h" have_func "X509_STORE_up_ref" - have_func("SSL_CTX_set_ecdh_auto(NULL, 0)", "openssl/ssl.h") + have_func "SSL_CTX_set_ecdh_auto(NULL, 0)" , "openssl/ssl.h" + + # below are yes for 3.0.0 & later, use for OpenSSL 3 detection + have_func "SSL_get1_peer_certificate" , "openssl/ssl.h" # Random.bytes available in Ruby 2.5 and later, Random::DEFAULT deprecated in 3.0 if Random.respond_to?(:bytes) - $defs.push("-DHAVE_RANDOM_BYTES") + $defs.push "-DHAVE_RANDOM_BYTES" puts "checking for Random.bytes... yes" else puts "checking for Random.bytes... no" @@ -48,11 +51,14 @@ if ENV["MAKE_WARNINGS_INTO_ERRORS"] # Make all warnings into errors # Except `implicit-fallthrough` since most failures comes from ragel state machine generated code - if respond_to? :append_cflags - append_cflags config_string 'WERRORFLAG' + if respond_to?(:append_cflags, true) # Ruby 2.5 and later + append_cflags(config_string('WERRORFLAG') || '-Werror') append_cflags '-Wno-implicit-fallthrough' else - $CFLAGS += ' ' << (config_string 'WERRORFLAG') << ' -Wno-implicit-fallthrough' + # flag may not exist on some platforms, -Werror may not be defined on some platforms, but + # works with all in current CI + $CFLAGS << " #{config_string('WERRORFLAG') || '-Werror'}" + $CFLAGS << ' -Wno-implicit-fallthrough' end end 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 2021-10-13 01:07:08.000000000 +0200 +++ new/ext/puma_http11/mini_ssl.c 1980-01-01 01:00:00.000000000 +0100 @@ -49,7 +49,8 @@ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; -DH *get_dh2048() { +#ifndef HAVE_SSL_GET1_PEER_CERTIFICATE +DH *get_dh2048(void) { /* `openssl dhparam -C 2048` * -----BEGIN DH PARAMETERS----- * MIIBCAKCAQEAjmh1uQHdTfxOyxEbKAV30fUfzqMDF/ChPzjfyzl2jcrqQMhrk76o @@ -119,6 +120,7 @@ return dh; } +#endif static void sslctx_free(void *ptr) { @@ -208,8 +210,13 @@ #endif int ssl_options; VALUE key, cert, ca, verify_mode, ssl_cipher_filter, no_tlsv1, no_tlsv1_1, - verification_flags, session_id_bytes; + verification_flags, session_id_bytes, cert_pem, key_pem; +#ifndef HAVE_SSL_GET1_PEER_CERTIFICATE DH *dh; +#endif + BIO *bio; + X509 *x509; + EVP_PKEY *pkey; #if OPENSSL_VERSION_NUMBER < 0x10002000L EC_KEY *ecdh; @@ -218,13 +225,15 @@ TypedData_Get_Struct(self, SSL_CTX, &sslctx_type, ctx); key = rb_funcall(mini_ssl_ctx, rb_intern_const("key"), 0); - StringValue(key); cert = rb_funcall(mini_ssl_ctx, rb_intern_const("cert"), 0); - StringValue(cert); ca = rb_funcall(mini_ssl_ctx, rb_intern_const("ca"), 0); + cert_pem = rb_funcall(mini_ssl_ctx, rb_intern_const("cert_pem"), 0); + + key_pem = rb_funcall(mini_ssl_ctx, rb_intern_const("key_pem"), 0); + verify_mode = rb_funcall(mini_ssl_ctx, rb_intern_const("verify_mode"), 0); ssl_cipher_filter = rb_funcall(mini_ssl_ctx, rb_intern_const("ssl_cipher_filter"), 0); @@ -233,8 +242,31 @@ no_tlsv1_1 = rb_funcall(mini_ssl_ctx, rb_intern_const("no_tlsv1_1"), 0); - SSL_CTX_use_certificate_chain_file(ctx, RSTRING_PTR(cert)); - SSL_CTX_use_PrivateKey_file(ctx, RSTRING_PTR(key), SSL_FILETYPE_PEM); + if (!NIL_P(cert)) { + StringValue(cert); + SSL_CTX_use_certificate_chain_file(ctx, RSTRING_PTR(cert)); + } + + if (!NIL_P(key)) { + StringValue(key); + SSL_CTX_use_PrivateKey_file(ctx, RSTRING_PTR(key), SSL_FILETYPE_PEM); + } + + if (!NIL_P(cert_pem)) { + bio = BIO_new(BIO_s_mem()); + BIO_puts(bio, RSTRING_PTR(cert_pem)); + x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); + + SSL_CTX_use_certificate(ctx, x509); + } + + if (!NIL_P(key_pem)) { + bio = BIO_new(BIO_s_mem()); + BIO_puts(bio, RSTRING_PTR(key_pem)); + pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + + SSL_CTX_use_PrivateKey(ctx, pkey); + } verification_flags = rb_funcall(mini_ssl_ctx, rb_intern_const("verification_flags"), 0); @@ -289,9 +321,6 @@ SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL@STRENGTH"); } - dh = get_dh2048(); - SSL_CTX_set_tmp_dh(ctx, dh); - #if OPENSSL_VERSION_NUMBER < 0x10002000L // Remove this case if OpenSSL 1.0.1 (now EOL) support is no // longer needed. @@ -325,6 +354,15 @@ SSL_MAX_SSL_SESSION_ID_LENGTH); // printf("\ninitialize end security_level %d\n", SSL_CTX_get_security_level(ctx)); + +#ifdef HAVE_SSL_GET1_PEER_CERTIFICATE + // https://www.openssl.org/docs/man3.0/man3/SSL_CTX_set_dh_auto.html + SSL_CTX_set_dh_auto(ctx, 1); +#else + dh = get_dh2048(); + SSL_CTX_set_tmp_dh(ctx, dh); +#endif + rb_obj_freeze(self); return self; } @@ -523,7 +561,11 @@ TypedData_Get_Struct(self, ms_conn, &engine_data_type, conn); +#ifdef HAVE_SSL_GET1_PEER_CERTIFICATE + cert = SSL_get1_peer_certificate(conn->ssl); +#else cert = SSL_get_peer_certificate(conn->ssl); +#endif if(!cert) { /* * See if there was a failed certificate associated with this client. @@ -580,7 +622,10 @@ ERR_load_crypto_strings(); mod = rb_define_module_under(puma, "MiniSSL"); + eng = rb_define_class_under(mod, "Engine", rb_cObject); + rb_undef_alloc_func(eng); + sslctx = rb_define_class_under(mod, "SSLContext", rb_cObject); rb_define_alloc_func(sslctx, sslctx_alloc); rb_define_method(sslctx, "initialize", sslctx_initialize, 1); 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 2021-10-13 01:07:08.000000000 +0200 +++ new/ext/puma_http11/org/jruby/puma/MiniSSL.java 1980-01-01 01:00:00.000000000 +0100 @@ -6,6 +6,7 @@ import org.jruby.RubyObject; import org.jruby.RubyString; import org.jruby.anno.JRubyMethod; +import org.jruby.exceptions.RaiseException; import org.jruby.javasupport.JavaEmbedUtils; import org.jruby.runtime.Block; import org.jruby.runtime.ObjectAllocator; @@ -80,11 +81,11 @@ /** * Writes bytes to the buffer after ensuring there's room */ - public void put(byte[] bytes) { - if (buffer.remaining() < bytes.length) { - resize(buffer.limit() + bytes.length); + private void put(byte[] bytes, final int offset, final int length) { + if (buffer.remaining() < length) { + resize(buffer.limit() + length); } - buffer.put(bytes); + buffer.put(bytes, offset, length); } /** @@ -115,7 +116,7 @@ buffer.get(bss); buffer.clear(); - return new ByteList(bss); + return new ByteList(bss, false); } @Override @@ -174,8 +175,6 @@ @JRubyMethod public IRubyObject initialize(ThreadContext threadContext, IRubyObject miniSSLContext) throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException { - KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); - KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType()); String keystoreFile = miniSSLContext.callMethod(threadContext, "keystore").convertToString().asJavaString(); KeyManagerFactory kmf = keyManagerFactoryMap.get(keystoreFile); @@ -230,14 +229,9 @@ @JRubyMethod public IRubyObject inject(IRubyObject arg) { - try { - byte[] bytes = arg.convertToString().getBytes(); - inboundNetData.put(bytes); - return this; - } catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException(e); - } + ByteList bytes = arg.convertToString().getByteList(); + inboundNetData.put(bytes.unsafeBytes(), bytes.getBegin(), bytes.getRealSize()); + return this; } private enum SSLOperation { @@ -297,7 +291,7 @@ } @JRubyMethod - public IRubyObject read() throws Exception { + public IRubyObject read() { try { inboundNetData.flip(); @@ -342,55 +336,46 @@ return getRuntime().getNil(); } - RubyString str = getRuntime().newString(""); - str.setValue(appDataByteList); - return str; - } catch (Exception e) { - throw getRuntime().newEOFError(e.getMessage()); + return RubyString.newString(getRuntime(), appDataByteList); + } catch (SSLException e) { + RaiseException re = getRuntime().newEOFError(e.getMessage()); + re.initCause(e); + throw re; } } @JRubyMethod public IRubyObject write(IRubyObject arg) { - try { - byte[] bls = arg.convertToString().getBytes(); - outboundAppData = new MiniSSLBuffer(bls); + byte[] bls = arg.convertToString().getBytes(); + outboundAppData = new MiniSSLBuffer(bls); - return getRuntime().newFixnum(bls.length); - } catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException(e); - } + return getRuntime().newFixnum(bls.length); } @JRubyMethod - public IRubyObject extract() throws SSLException { + public IRubyObject extract(ThreadContext context) { try { ByteList dataByteList = outboundNetData.asByteList(); if (dataByteList != null) { - RubyString str = getRuntime().newString(""); - str.setValue(dataByteList); - return str; + return RubyString.newString(context.runtime, dataByteList); } if (!outboundAppData.hasRemaining()) { - return getRuntime().getNil(); + return context.nil; } outboundNetData.clear(); doOp(SSLOperation.WRAP, outboundAppData, outboundNetData); dataByteList = outboundNetData.asByteList(); if (dataByteList == null) { - return getRuntime().getNil(); + return context.nil; } - RubyString str = getRuntime().newString(""); - str.setValue(dataByteList); - - return str; - } catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException(e); + return RubyString.newString(context.runtime, dataByteList); + } catch (SSLException e) { + RaiseException ex = context.runtime.newRuntimeError(e.toString()); + ex.initCause(e); + throw ex; } } @@ -398,7 +383,7 @@ public IRubyObject peercert() throws CertificateEncodingException { try { return JavaEmbedUtils.javaToRuby(getRuntime(), engine.getSession().getPeerCertificates()[0].getEncoded()); - } catch (SSLPeerUnverifiedException ex) { + } catch (SSLPeerUnverifiedException e) { return getRuntime().getNil(); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ext/puma_http11/puma_http11.c new/ext/puma_http11/puma_http11.c --- old/ext/puma_http11/puma_http11.c 2021-10-13 01:07:08.000000000 +0200 +++ new/ext/puma_http11/puma_http11.c 1980-01-01 01:00:00.000000000 +0100 @@ -451,7 +451,7 @@ void Init_mini_ssl(VALUE mod); #endif -void Init_puma_http11() +void Init_puma_http11(void) { VALUE mPuma = rb_define_module("Puma"); 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 2021-10-13 01:07:08.000000000 +0200 +++ new/lib/puma/binder.rb 1980-01-01 01:00:00.000000000 +0100 @@ -30,6 +30,7 @@ def initialize(events, conf = Configuration.new) @events = events + @conf = conf @listeners = [] @inherited_fds = {} @activated_sockets = {} @@ -167,9 +168,9 @@ params = Util.parse_query uri.query opt = params.key?('low_latency') && params['low_latency'] != 'false' - bak = params.fetch('backlog', 1024).to_i + backlog = params.fetch('backlog', 1024).to_i - io = add_tcp_listener uri.host, uri.port, opt, bak + io = add_tcp_listener uri.host, uri.port, opt, backlog @ios[ios_len..-1].each do |i| addr = loc_addr_str i @@ -232,9 +233,21 @@ # If key and certs are not defined and localhost gem is required. # localhost gem will be used for self signed # Load localhost authority if not loaded. - ctx = localhost_authority && localhost_authority_context if params.empty? + if params.values_at('cert', 'key').all? { |v| v.to_s.empty? } + ctx = localhost_authority && localhost_authority_context + end - ctx ||= MiniSSL::ContextBuilder.new(params, @events).context + ctx ||= + begin + # Extract cert_pem and key_pem from options[:store] if present + ['cert', 'key'].each do |v| + if params[v] && params[v].start_with?('store:') + index = Integer(params.delete(v).split('store:').last) + params["#{v}_pem"] = @conf.options[:store][index] + end + end + MiniSSL::ContextBuilder.new(params, @events).context + end if fd = @inherited_fds.delete(str) logger.log "* Inherited #{str}" @@ -244,7 +257,8 @@ logger.log "* Activated #{str}" else ios_len = @ios.length - io = add_ssl_listener uri.host, uri.port, ctx + backlog = params.fetch('backlog', 1024).to_i + io = add_ssl_listener uri.host, uri.port, ctx, optimize_for_latency = true, backlog @ios[ios_len..-1].each do |i| addr = loc_addr_str i diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/cli.rb new/lib/puma/cli.rb --- old/lib/puma/cli.rb 2021-10-13 01:07:08.000000000 +0200 +++ new/lib/puma/cli.rb 1980-01-01 01:00:00.000000000 +0100 @@ -11,16 +11,17 @@ module Puma class << self - # The CLI exports its Puma::Configuration object here to allow - # apps to pick it up. An app needs to use it conditionally though - # since it is not set if the app is launched via another - # mechanism than the CLI class. + # The CLI exports a Puma::Configuration instance here to allow + # apps to pick it up. An app must load this object conditionally + # because it is not set if the app is launched via any mechanism + # other than the CLI class. attr_accessor :cli_config end # Handles invoke a Puma::Server in a command line style. # class CLI + # @deprecated 6.0.0 KEYS_NOT_TO_PERSIST_IN_STATE = Launcher::KEYS_NOT_TO_PERSIST_IN_STATE # Create a new CLI object using +argv+ as the command line @@ -184,6 +185,10 @@ user_config.restart_command cmd end + o.on "-s", "--silent", "Do not log prompt messages other than errors" do + @events = Events.new NullIO.new, $stderr + end + o.on "-S", "--state PATH", "Where to store the state details" do |arg| user_config.state_path arg end 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 2021-10-13 01:07:08.000000000 +0200 +++ new/lib/puma/client.rb 1980-01-01 01:00:00.000000000 +0100 @@ -161,7 +161,7 @@ def close begin @io.close - rescue IOError + rescue IOError, Errno::EBADF Puma::Util.purge_interrupt_queue end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/cluster/worker.rb new/lib/puma/cluster/worker.rb --- old/lib/puma/cluster/worker.rb 2021-10-13 01:07:08.000000000 +0200 +++ new/lib/puma/cluster/worker.rb 1980-01-01 01:00:00.000000000 +0100 @@ -33,8 +33,8 @@ Signal.trap "SIGINT", "IGNORE" Signal.trap "SIGCHLD", "DEFAULT" - Thread.new do - Puma.set_thread_name "worker check pipe" + Thread.new do + Puma.set_thread_name "wrkr check" @check_pipe.wait_readable log "! Detected parent died, dying" exit! 1 @@ -76,7 +76,7 @@ end Thread.new do - Puma.set_thread_name "worker fork pipe" + Puma.set_thread_name "wrkr fork" while (idx = @fork_pipe.gets) idx = idx.to_i if idx == -1 # stop server @@ -114,7 +114,7 @@ while restart_server.pop server_thread = server.run stat_thread ||= Thread.new(@worker_write) do |io| - Puma.set_thread_name "stat payload" + Puma.set_thread_name "stat pld" base_payload = "p#{Process.pid}" while true @@ -130,7 +130,7 @@ Puma::Util.purge_interrupt_queue break end - sleep Const::WORKER_CHECK_INTERVAL + sleep @options[:worker_check_interval] end end server_thread.join diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/cluster/worker_handle.rb new/lib/puma/cluster/worker_handle.rb --- old/lib/puma/cluster/worker_handle.rb 2021-10-13 01:07:08.000000000 +0200 +++ new/lib/puma/cluster/worker_handle.rb 1980-01-01 01:00:00.000000000 +0100 @@ -40,6 +40,10 @@ @stage = :booted end + def term! + @term = true + end + def term? @term end 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 2021-10-13 01:07:08.000000000 +0200 +++ new/lib/puma/cluster.rb 1980-01-01 01:00:00.000000000 +0100 @@ -108,24 +108,42 @@ def cull_workers diff = @workers.size - @options[:workers] return if diff < 1 + debug "Culling #{diff} workers" - debug "Culling #{diff.inspect} workers" + workers = workers_to_cull(diff) + debug "Workers to cull: #{workers.inspect}" - workers_to_cull = @workers[-diff,diff] - debug "Workers to cull: #{workers_to_cull.inspect}" - - workers_to_cull.each do |worker| + workers.each do |worker| log "- Worker #{worker.index} (PID: #{worker.pid}) terminating" worker.term end end + def workers_to_cull(diff) + workers = @workers.sort_by(&:started_at) + + # In fork_worker mode, worker 0 acts as our master process. + # We should avoid culling it to preserve copy-on-write memory gains. + workers.reject! { |w| w.index == 0 } if @options[:fork_worker] + + workers[cull_start_index(diff), diff] + end + + def cull_start_index(diff) + case @options[:worker_culling_strategy] + when :oldest + 0 + else # :youngest + -diff + end + end + # @!attribute [r] next_worker_index def next_worker_index - all_positions = 0...@options[:workers] - occupied_positions = @workers.map { |w| w.index } - available_positions = all_positions.to_a - occupied_positions - available_positions.first + occupied_positions = @workers.map(&:index) + idx = 0 + idx += 1 until !occupied_positions.include?(idx) + idx end def all_workers_booted? @@ -135,7 +153,7 @@ def check_workers return if @next_check >= Time.now - @next_check = Time.now + Const::WORKER_CHECK_INTERVAL + @next_check = Time.now + @options[:worker_check_interval] timeout_workers wait_workers @@ -440,7 +458,7 @@ workers_not_booted -= 1 when "e" # external term, see worker method, Signal.trap "SIGTERM" - w.instance_variable_set :@term, true + w.term! when "t" w.term unless w.term? when "p" 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 2021-10-13 01:07:08.000000000 +0200 +++ new/lib/puma/configuration.rb 1980-01-01 01:00:00.000000000 +0100 @@ -11,6 +11,7 @@ DefaultTCPHost = "0.0.0.0" DefaultTCPPort = 9292 + DefaultWorkerCheckInterval = 5 DefaultWorkerTimeout = 60 DefaultWorkerShutdownTimeout = 30 end @@ -195,9 +196,11 @@ :workers => Integer(ENV['WEB_CONCURRENCY'] || 0), :silence_single_worker_warning => false, :mode => :http, + :worker_check_interval => DefaultWorkerCheckInterval, :worker_timeout => DefaultWorkerTimeout, :worker_boot_timeout => DefaultWorkerTimeout, :worker_shutdown_timeout => DefaultWorkerShutdownTimeout, + :worker_culling_strategy => :youngest, :remote_address => :socket, :tag => method(:infer_tag), :environment => -> { ENV['APP_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development' }, 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 2021-10-13 01:07:08.000000000 +0200 +++ new/lib/puma/const.rb 1980-01-01 01:00:00.000000000 +0100 @@ -100,8 +100,8 @@ # too taxing on performance. module Const - PUMA_VERSION = VERSION = "5.5.2".freeze - CODE_NAME = "Zawgyi".freeze + PUMA_VERSION = VERSION = "5.6.2".freeze + CODE_NAME = "Birdie's Version".freeze PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze @@ -235,9 +235,6 @@ EARLY_HINTS = "rack.early_hints".freeze - # Minimum interval to checks worker health - WORKER_CHECK_INTERVAL = 5 - # Illegal character in the key or value of response header DQUOTE = "\"".freeze HTTP_HEADER_DELIMITER = Regexp.escape("(),/:;<=>?@[]{}\\").freeze diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/detect.rb new/lib/puma/detect.rb --- old/lib/puma/detect.rb 2021-10-13 01:07:08.000000000 +0200 +++ new/lib/puma/detect.rb 1980-01-01 01:00:00.000000000 +0100 @@ -10,8 +10,10 @@ IS_JRUBY = Object.const_defined? :JRUBY_VERSION - IS_WINDOWS = !!(RUBY_PLATFORM =~ /mswin|ming|cygwin/ || - IS_JRUBY && RUBY_DESCRIPTION =~ /mswin/) + IS_OSX = RUBY_PLATFORM.include? 'darwin' + + IS_WINDOWS = !!(RUBY_PLATFORM =~ /mswin|ming|cygwin/) || + IS_JRUBY && RUBY_DESCRIPTION.include?('mswin') # @version 5.2.0 IS_MRI = (RUBY_ENGINE == 'ruby' || RUBY_ENGINE.nil?) @@ -20,6 +22,10 @@ IS_JRUBY end + def self.osx? + IS_OSX + end + def self.windows? IS_WINDOWS 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 2021-10-13 01:07:08.000000000 +0200 +++ new/lib/puma/dsl.rb 1980-01-01 01:00:00.000000000 +0100 @@ -48,6 +48,8 @@ ca_additions = "&ca=#{opts[:ca]}" if ['peer', 'force_peer'].include?(verify) + backlog_str = opts[:backlog] ? "&backlog=#{Integer(opts[:backlog])}" : '' + if defined?(JRUBY_VERSION) ssl_cipher_list = opts[:ssl_cipher_list] ? "&ssl_cipher_list=#{opts[:ssl_cipher_list]}" : nil @@ -55,7 +57,7 @@ keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}" "ssl://#{host}:#{port}?#{keystore_additions}#{ssl_cipher_list}" \ - "&verify_mode=#{verify}#{tls_str}#{ca_additions}" + "&verify_mode=#{verify}#{tls_str}#{ca_additions}#{backlog_str}" else ssl_cipher_filter = opts[:ssl_cipher_filter] ? "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" : nil @@ -64,7 +66,7 @@ "&verification_flags=#{Array(ary).join ','}" : nil "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}" \ - "#{ssl_cipher_filter}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}" + "#{ssl_cipher_filter}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}#{backlog_str}" end end @@ -191,7 +193,7 @@ end # Bind the server to +url+. "tcp://", "unix://" and "ssl://" are the only - # accepted protocols. Multiple urls can be bound to, calling `bind` does + # accepted protocols. Multiple urls can be bound to, calling +bind+ does # not overwrite previous bindings. # # The default is "tcp://0.0.0.0:9292". @@ -436,8 +438,15 @@ @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 this method. + # Instead of using +bind+ and manually constructing a URI like: + # + # bind 'ssl://127.0.0.1:9292?key=key_path&cert=cert_path' + # + # you can use the this method. + # + # When binding on localhost you don't need to specify +cert+ and +key+, + # Puma will assume you are using the +localhost+ gem and try to load the + # appropriate files. # # @example # ssl_bind '127.0.0.1', '9292', { @@ -447,14 +456,25 @@ # verify_mode: verify_mode, # default 'none' # verification_flags: flags, # optional, not supported by JRuby # } - # @example For JRuby, two keys are required: keystore & keystore_pass. + # + # @example Using self-signed certificate with the +localhost+ gem: + # ssl_bind '127.0.0.1', '9292' + # + # @example Alternatively, you can provide +cert_pem+ and +key_pem+: + # ssl_bind '127.0.0.1', '9292', { + # cert_pem: File.read(path_to_cert), + # key_pem: File.read(path_to_key), + # } + # + # @example For JRuby, two keys are required: +keystore+ & +keystore_pass+ # ssl_bind '127.0.0.1', '9292', { # keystore: path_to_keystore, # keystore_pass: password, # ssl_cipher_list: cipher_list, # optional # verify_mode: verify_mode # default 'none' # } - def ssl_bind(host, port, opts) + def ssl_bind(host, port, opts = {}) + add_pem_values_to_options_store(opts) bind self.class.ssl_bind_str(host, port, opts) end @@ -727,6 +747,19 @@ @options[:tag] = string.to_s end + # Change the default interval for checking workers. + # + # The default value is 5 seconds. + # + # @note Cluster mode only. + # @example + # worker_check_interval 5 + # @see Puma::Cluster#check_workers + # + def worker_check_interval(interval) + @options[:worker_check_interval] = Integer(interval) + end + # 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. @@ -741,7 +774,7 @@ # def worker_timeout(timeout) timeout = Integer(timeout) - min = Const::WORKER_CHECK_INTERVAL + min = @options.fetch(:worker_check_interval, Puma::ConfigDefault::DefaultWorkerCheckInterval) if timeout <= min raise "The minimum worker_timeout must be greater than the worker reporting interval (#{min})" @@ -773,6 +806,30 @@ @options[:worker_shutdown_timeout] = Integer(timeout) end + # Set the strategy for worker culling. + # + # There are two possible values: + # + # 1. **:youngest** - the youngest workers (i.e. the workers that were + # the most recently started) will be culled. + # 2. **:oldest** - the oldest workers (i.e. the workers that were started + # the longest time ago) will be culled. + # + # @note Cluster mode only. + # @example + # worker_culling_strategy :oldest + # @see Puma::Cluster#cull_workers + # + def worker_culling_strategy(strategy) + stategy = strategy.to_sym + + if ![:youngest, :oldest].include?(strategy) + raise "Invalid value for worker_culling_strategy - #{stategy}" + end + + @options[:worker_culling_strategy] = strategy + end + # When set to true (the default), workers accept all requests # and queue them before passing them to the handlers. # When set to false, each worker process accepts exactly as @@ -927,5 +984,25 @@ def mutate_stdout_and_stderr_to_sync_on_write(enabled=true) @options[:mutate_stdout_and_stderr_to_sync_on_write] = enabled end + + private + + # To avoid adding cert_pem and key_pem as URI params, we store them on the + # options[:store] from where Puma binder knows how to find and extract them. + def add_pem_values_to_options_store(opts) + return if defined?(JRUBY_VERSION) + + @options[:store] ||= [] + + # Store cert_pem and key_pem to options[:store] if present + [:cert, :key].each do |v| + opt_key = :"#{v}_pem" + if opts[opt_key] + index = @options[:store].length + @options[:store] << opts[opt_key] + opts[v] = "store:#{index}" + end + end + end end end 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 2021-10-13 01:07:08.000000000 +0200 +++ new/lib/puma/launcher.rb 1980-01-01 01:00:00.000000000 +0100 @@ -15,6 +15,7 @@ # It is responsible for either launching a cluster of Puma workers or a single # puma server. class Launcher + # @deprecated 6.0.0 KEYS_NOT_TO_PERSIST_IN_STATE = [ :logger, :lowlevel_error_handler, :before_worker_shutdown, :before_worker_boot, :before_worker_fork, @@ -73,7 +74,7 @@ generate_restart_data - if clustered? && !Process.respond_to?(:fork) + if clustered? && !Puma.forkable? unsupported "worker mode not supported on #{RUBY_ENGINE} on this platform" end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/minissl/context_builder.rb new/lib/puma/minissl/context_builder.rb --- old/lib/puma/minissl/context_builder.rb 2021-10-13 01:07:08.000000000 +0200 +++ new/lib/puma/minissl/context_builder.rb 1980-01-01 01:00:00.000000000 +0100 @@ -23,17 +23,19 @@ ctx.keystore_pass = params['keystore-pass'] ctx.ssl_cipher_list = params['ssl_cipher_list'] if params['ssl_cipher_list'] else - unless params['key'] - events.error "Please specify the SSL key via 'key='" + if params['key'].nil? && params['key_pem'].nil? + events.error "Please specify the SSL key via 'key=' or 'key_pem='" end - ctx.key = params['key'] + ctx.key = params['key'] if params['key'] + ctx.key_pem = params['key_pem'] if params['key_pem'] - unless params['cert'] - events.error "Please specify the SSL cert via 'cert='" + if params['cert'].nil? && params['cert_pem'].nil? + events.error "Please specify the SSL cert via 'cert=' or 'cert_pem='" end - ctx.cert = params['cert'] + ctx.cert = params['cert'] if params['cert'] + ctx.cert_pem = params['cert_pem'] if params['cert_pem'] if ['peer', 'force_peer'].include?(params['verify_mode']) unless params['ca'] 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 2021-10-13 01:07:08.000000000 +0200 +++ new/lib/puma/minissl.rb 1980-01-01 01:00:00.000000000 +0100 @@ -208,6 +208,10 @@ def initialize @no_tlsv1 = false @no_tlsv1_1 = false + @key = nil + @cert = nil + @key_pem = nil + @cert_pem = nil end if IS_JRUBY @@ -230,6 +234,8 @@ attr_reader :key attr_reader :cert attr_reader :ca + attr_reader :cert_pem + attr_reader :key_pem attr_accessor :ssl_cipher_filter attr_accessor :verification_flags @@ -248,9 +254,19 @@ @ca = ca end + def cert_pem=(cert_pem) + raise ArgumentError, "'cert_pem' is not a String" unless cert_pem.is_a? String + @cert_pem = cert_pem + end + + def key_pem=(key_pem) + raise ArgumentError, "'key_pem' is not a String" unless key_pem.is_a? String + @key_pem = key_pem + end + def check - raise "Key not configured" unless @key - raise "Cert not configured" unless @cert + raise "Key not configured" if @key.nil? && @key_pem.nil? + raise "Cert not configured" if @cert.nil? && @cert_pem.nil? end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/plugin.rb new/lib/puma/plugin.rb --- old/lib/puma/plugin.rb 2021-10-13 01:07:08.000000000 +0200 +++ new/lib/puma/plugin.rb 1980-01-01 01:00:00.000000000 +0100 @@ -64,7 +64,7 @@ def fire_background @background.each_with_index do |b, i| Thread.new do - Puma.set_thread_name "plugin background #{i}" + Puma.set_thread_name "plgn bg #{i}" b.call end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/request.rb new/lib/puma/request.rb --- old/lib/puma/request.rb 2021-10-13 01:07:08.000000000 +0200 +++ new/lib/puma/request.rb 1980-01-01 01:00:00.000000000 +0100 @@ -167,11 +167,16 @@ end ensure - uncork_socket io + begin + uncork_socket io - body.close - client.tempfile.unlink if client.tempfile - res_body.close if res_body.respond_to? :close + body.close + client.tempfile.unlink if client.tempfile + ensure + # Whatever happens, we MUST call `close` on the response body. + # Otherwise Rack::BodyProxy callbacks may not fire and lead to various state leaks + res_body.close if res_body.respond_to? :close + end after_reply.each { |o| o.call } end 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 2021-10-13 01:07:08.000000000 +0200 +++ new/lib/puma/runner.rb 1980-01-01 01:00:00.000000000 +0100 @@ -69,7 +69,7 @@ control.binder.parse [str], self, 'Starting control server' - control.run thread_name: 'control' + control.run thread_name: 'ctl' @control = control end @@ -94,12 +94,13 @@ def output_header(mode) min_t = @options[:min_threads] max_t = @options[:max_threads] + environment = @options[:environment] log "Puma starting in #{mode} mode..." log "* Puma version: #{Puma::Const::PUMA_VERSION} (#{ruby_engine}) (\"#{Puma::Const::CODE_NAME}\")" log "* Min threads: #{min_t}" log "* Max threads: #{max_t}" - log "* Environment: #{ENV['RACK_ENV']}" + log "* Environment: #{environment}" if mode == "cluster" log "* Master PID: #{Process.pid}" 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 2021-10-13 01:07:08.000000000 +0200 +++ new/lib/puma/server.rb 1980-01-01 01:00:00.000000000 +0100 @@ -220,7 +220,7 @@ # up in the background to handle requests. Otherwise requests # are handled synchronously. # - def run(background=true, thread_name: 'server') + def run(background=true, thread_name: 'srv') BasicSocket.do_not_reverse_lookup = true @events.fire :state, :booting @@ -315,16 +315,15 @@ queue_requests = @queue_requests drain = @options[:drain_on_shutdown] ? 0 : nil - remote_addr_value = nil - remote_addr_header = nil - - case @options[:remote_address] + addr_send_name, addr_value = case @options[:remote_address] when :value - remote_addr_value = @options[:remote_address_value] + [:peerip=, @options[:remote_address_value]] when :header - remote_addr_header = @options[:remote_address_header] + [:remote_addr_header=, @options[:remote_address_header]] when :proxy_protocol - remote_addr_proxy_protocol = @options[:remote_address_proxy_protocol] + [:expect_proxy_proto=, @options[:remote_address_proxy_protocol]] + else + [nil, nil] end while @status == :run || (drain && shutting_down?) @@ -344,16 +343,10 @@ next end drain += 1 if shutting_down? - client = Client.new io, @binder.env(sock) - client.listener = sock - if remote_addr_value - client.peerip = remote_addr_value - elsif remote_addr_header - client.remote_addr_header = remote_addr_header - elsif remote_addr_proxy_protocol - client.expect_proxy_proto = remote_addr_proxy_protocol - end - pool << client + pool << Client.new(io, @binder.env(sock)).tap { |c| + c.listener = sock + c.send(addr_send_name, addr_value) if addr_value + } end end rescue IOError, Errno::EBADF @@ -375,13 +368,14 @@ rescue Exception => e @events.unknown_error e, nil, "Exception handling servers" ensure - begin - @check.close unless @check.closed? - rescue Errno::EBADF, RuntimeError - # RuntimeError is Ruby 2.2 issue, can't modify frozen IOError - # Errno::EBADF is infrequently raised + # RuntimeError is Ruby 2.2 issue, can't modify frozen IOError + # Errno::EBADF is infrequently raised + [@check, @notify].each do |io| + begin + io.close unless io.closed? + rescue Errno::EBADF, RuntimeError + end end - @notify.close @notify = nil @check = nil end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/state_file.rb new/lib/puma/state_file.rb --- old/lib/puma/state_file.rb 2021-10-13 01:07:08.000000000 +0200 +++ new/lib/puma/state_file.rb 1980-01-01 01:00:00.000000000 +0100 @@ -1,15 +1,40 @@ # frozen_string_literal: true -require 'yaml' - module Puma + + # Puma::Launcher uses StateFile to write a yaml file for use with Puma::ControlCLI. + # + # In previous versions of Puma, YAML was used to read/write the state file. + # Since Puma is similar to Bundler/RubyGems in that it may load before one's app + # does, minimizing the dependencies that may be shared with the app is desired. + # + # At present, it only works with numeric and string values. It is still a valid + # yaml file, and the CI tests parse it with Psych. + # class StateFile + + ALLOWED_FIELDS = %w!control_url control_auth_token pid running_from! + + # @deprecated 6.0.0 + FIELDS = ALLOWED_FIELDS + def initialize @options = {} end def save(path, permission = nil) - contents =YAML.dump @options + contents = "---\n".dup + @options.each do |k,v| + next unless ALLOWED_FIELDS.include? k + case v + when Numeric + contents << "#{k}: #{v}\n" + when String + next if v.strip.empty? + contents << (k == 'running_from' || v.to_s.include?(' ') ? + "#{k}: \"#{v}\"\n" : "#{k}: #{v}\n") + end + end if permission File.write path, contents, mode: 'wb:UTF-8' else @@ -18,12 +43,21 @@ end def load(path) - @options = YAML.load File.read(path) + File.read(path).lines.each do |line| + next if line.start_with? '#' + k,v = line.split ':', 2 + next unless v && ALLOWED_FIELDS.include?(k) + v = v.strip + @options[k] = + case v + when /\A\d+\z/ then v.to_i + when /\A\d+\.\d+\z/ then v.to_f + else v.gsub(/\A"|"\z/, '') + end + end end - FIELDS = %w!control_url control_auth_token pid running_from! - - FIELDS.each do |f| + ALLOWED_FIELDS.each do |f| define_method f do @options[f] end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/thread_pool.rb new/lib/puma/thread_pool.rb --- old/lib/puma/thread_pool.rb 2021-10-13 01:07:08.000000000 +0200 +++ new/lib/puma/thread_pool.rb 1980-01-01 01:00:00.000000000 +0100 @@ -72,7 +72,7 @@ attr_accessor :out_of_band_hook # @version 5.0.0 def self.clean_thread_locals - Thread.current.keys.each do |key| # rubocop: disable Performance/HashEachMethods + Thread.current.keys.each do |key| # rubocop: disable Style/HashEachMethods Thread.current[key] = nil unless key == :__recursive_key__ end end @@ -102,7 +102,7 @@ @spawned += 1 th = Thread.new(@spawned) do |spawned| - Puma.set_thread_name '%s threadpool %03i' % [@name, spawned] + Puma.set_thread_name '%s tp %03i' % [@name, spawned] todo = @todo block = @block mutex = @mutex diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/metadata new/metadata --- old/metadata 2021-10-13 01:07:08.000000000 +0200 +++ new/metadata 1980-01-01 01:00:00.000000000 +0100 @@ -1,14 +1,14 @@ --- !ruby/object:Gem::Specification name: puma version: !ruby/object:Gem::Version - version: 5.5.2 + version: 5.6.2 platform: ruby authors: - Evan Phoenix autorequire: bindir: bin cert_chain: [] -date: 2021-10-12 00:00:00.000000000 Z +date: 1980-01-01 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: nio4r @@ -140,7 +140,7 @@ - !ruby/object:Gem::Version version: '0' requirements: [] -rubygems_version: 3.2.3 +rubygems_version: 3.2.26 signing_key: specification_version: 4 summary: Puma is a simple, fast, threaded, and highly parallel HTTP 1.1 server for diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tools/Dockerfile new/tools/Dockerfile --- old/tools/Dockerfile 2021-10-13 01:07:08.000000000 +0200 +++ new/tools/Dockerfile 1980-01-01 01:00:00.000000000 +0100 @@ -1,6 +1,6 @@ # Use this Dockerfile to create minimal reproductions of issues -FROM ruby:2.6 +FROM ruby:3.1 # throw errors if Gemfile has been modified since Gemfile.lock RUN bundle config --global frozen 1