Hello community, here is the log from the commit of package rubygem-puma for openSUSE:Factory checked in at 2018-07-18 22:54:42 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rubygem-puma (Old) and /work/SRC/openSUSE:Factory/.rubygem-puma.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-puma" Wed Jul 18 22:54:42 2018 rev:30 rq:622809 version:3.12.0 Changes: -------- --- /work/SRC/openSUSE:Factory/rubygem-puma/rubygem-puma.changes 2018-02-12 10:13:49.704503267 +0100 +++ /work/SRC/openSUSE:Factory/.rubygem-puma.new/rubygem-puma.changes 2018-07-18 22:55:43.466605825 +0200 @@ -1,0 +2,50 @@ +Fri Jul 13 17:15:17 UTC 2018 - [email protected] + +- updated to version 3.12.0 + see installed History.md + + ## 3.12.0 / 2018-07-13 + + * 5 features: + * You can now specify which SSL ciphers the server should support, default is unchanged (#1478) + * The setting for Puma's `max_threads` is now in `Puma.stats` (#1604) + * Pool capacity is now in `Puma.stats` (#1579) + * Installs restricted to Ruby 2.2+ (#1506) + * `--control` is now deprecated in favor of `--control-url` (#1487) + + * 2 bugfixes: + * Workers will no longer accept more web requests than they have capacity to process. This prevents an issue where one worker would accept lots of requests while starving other workers (#1563) + * In a test env puma now emits the stack on an exception (#1557) + +------------------------------------------------------------------- +Thu Apr 12 20:44:52 UTC 2018 - [email protected] + +- updated to version 3.11.4 + see installed History.md + + ## 3.11.4 / 2018-04-12 + + * 2 features: + * Manage puma as a service using rc.d (#1529) + * Server stats are now available from a top level method (#1532) + * 5 bugfixes: + * Fix parsing CLI options (#1482) + * Order of stderr and stdout is made before redirecting to a log file (#1511) + * Init.d fix of `ps -p` to check if pid exists (#1545) + * Early hits bugfix (#1550) + * Purge interrupt queue when closing socket fails (#1553) + +------------------------------------------------------------------- +Tue Mar 20 10:14:18 UTC 2018 - [email protected] + +- updated to version 3.11.3 + see installed History.md + + ## 3.11.3 / 2018-03-05 + + * 3 bugfixes: + * Add closed? to MiniSSL::Socket for use in reactor (#1510) + * Handle EOFError at the toplevel of the server threads (#1524) (#1507) + * Deal with zero sized bodies when using SSL (#1483) + +------------------------------------------------------------------- Old: ---- puma-3.11.2.gem New: ---- puma-3.12.0.gem ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rubygem-puma.spec ++++++ --- /var/tmp/diff_new_pack.kZDXe0/_old 2018-07-18 22:55:43.930604286 +0200 +++ /var/tmp/diff_new_pack.kZDXe0/_new 2018-07-18 22:55:43.934604273 +0200 @@ -24,7 +24,7 @@ # Name: rubygem-puma -Version: 3.11.2 +Version: 3.12.0 Release: 0 %define mod_name puma %define mod_full_name %{mod_name}-%{version} @@ -32,7 +32,7 @@ BuildRequires: openssl-devel # /MANUAL BuildRoot: %{_tmppath}/%{name}-%{version}-build -BuildRequires: %{rubydevel >= 1.9.3} +BuildRequires: %{rubydevel >= 2.2} BuildRequires: %{rubygem gem2rpm} BuildRequires: ruby-macros >= 5 BuildRequires: update-alternatives ++++++ puma-3.11.2.gem -> puma-3.12.0.gem ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/History.md new/History.md --- old/History.md 2018-01-19 20:23:10.000000000 +0100 +++ new/History.md 2018-07-13 18:08:52.000000000 +0200 @@ -1,3 +1,35 @@ +## 3.12.0 / 2018-07-13 + +* 5 features: + * You can now specify which SSL ciphers the server should support, default is unchanged (#1478) + * The setting for Puma's `max_threads` is now in `Puma.stats` (#1604) + * Pool capacity is now in `Puma.stats` (#1579) + * Installs restricted to Ruby 2.2+ (#1506) + * `--control` is now deprecated in favor of `--control-url` (#1487) + +* 2 bugfixes: + * Workers will no longer accept more web requests than they have capacity to process. This prevents an issue where one worker would accept lots of requests while starving other workers (#1563) + * In a test env puma now emits the stack on an exception (#1557) + +## 3.11.4 / 2018-04-12 + +* 2 features: + * Manage puma as a service using rc.d (#1529) + * Server stats are now available from a top level method (#1532) +* 5 bugfixes: + * Fix parsing CLI options (#1482) + * Order of stderr and stdout is made before redirecting to a log file (#1511) + * Init.d fix of `ps -p` to check if pid exists (#1545) + * Early hints bugfix (#1550) + * Purge interrupt queue when closing socket fails (#1553) + +## 3.11.3 / 2018-03-05 + +* 3 bugfixes: + * Add closed? to MiniSSL::Socket for use in reactor (#1510) + * Handle EOFError at the toplevel of the server threads (#1524) (#1507) + * Deal with zero sized bodies when using SSL (#1483) + ## 3.11.2 / 2018-01-19 * 1 bugfix: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/README.md new/README.md --- old/README.md 2018-01-19 20:23:10.000000000 +0100 +++ new/README.md 2018-07-13 18:08:52.000000000 +0200 @@ -157,17 +157,27 @@ ``` 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. +#####Ruby: +``` +$ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&ssl_cipher_filter=!aNULL:AES+SHA' +``` +#####JRuby: +``` +$ puma -b 'ssl://127.0.0.1:9292?keystore=path_to_keystore&keystore-pass=keystore_password&ssl_cipher_list=TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA' +``` +See https://www.openssl.org/docs/man1.0.2/apps/ciphers.html for cipher filter format and full list of cipher suites. ### Control/Status Server Puma has a built-in status/control app that can be used to query and control Puma itself. ``` -$ puma --control tcp://127.0.0.1:9293 --control-token foo +$ 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. @@ -175,7 +185,7 @@ You can also interact with the control server via `pumactl`. This command will restart Puma: ``` -$ pumactl -C 'tcp://127.0.0.1:9293' --control-token foo restart +$ pumactl --control-url 'tcp://127.0.0.1:9293' --control-token foo restart ``` To see a list of `pumactl` options, use `pumactl --help`. @@ -217,10 +227,10 @@ ## Known Bugs -For MRI versions 2.2.7, 2.2.8, 2.3.4 and 2.4.1, you may see ```stream closed in another thread (IOError)```. It may be caused by a [Ruby bug](https://bugs.ruby-lang.org/issues/13632). It can be fixed with the gem https://rubygems.org/gems/stopgap_13632: +For MRI versions 2.2.7, 2.2.8, 2.2.9, 2.2.10 2.3.4 and 2.4.1, you may see ```stream closed in another thread (IOError)```. It may be caused by a [Ruby bug](https://bugs.ruby-lang.org/issues/13632). It can be fixed with the gem https://rubygems.org/gems/stopgap_13632: ```ruby -if %w(2.2.7 2.2.8 2.3.4 2.4.1).include? RUBY_VERSION +if %w(2.2.7 2.2.8 2.2.9 2.2.10 2.3.4 2.4.1).include? RUBY_VERSION begin require 'stopgap_13632' rescue LoadError 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 2018-01-19 20:23:10.000000000 +0100 +++ new/docs/architecture.md 2018-07-13 18:08:52.000000000 +0200 @@ -33,4 +33,4 @@ The `queue_requests` option is `true` by default, enabling the separate thread used to buffer requests as described above. If set to `false`, this buffer will not be used for connections while waiting for the request to arrive. -In this mode, when a connection is accepted, it is added to the "todo" queue immediately, and a worker will syncronously do any waiting necessarry to read the HTTP request from the socket. \ No newline at end of file +In this mode, when a connection is accepted, it is added to the "todo" queue immediately, and a worker will synchronously do any waiting necessary to read the HTTP request from the socket. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/docs/restart.md new/docs/restart.md --- old/docs/restart.md 2018-01-19 20:23:10.000000000 +0100 +++ new/docs/restart.md 2018-07-13 18:08:52.000000000 +0200 @@ -20,7 +20,7 @@ But again beware, upgrading an application sometimes involves upgrading the database schema. With phased restart, there may be a moment during the deployment where processes belonging to the previous version and processes belonging to the new version both exist at the same time. Any database schema upgrades you perform must therefore be backwards-compatible with the old application version. -If you perform a lot of database migrations, you probably should not use phased restart and use a normal/hot restart instead (pumactl restart). That way, no code is shared while deploying (in that case, preload_app might help for quicker deployment, see below). +If you perform a lot of database migrations, you probably should not use phased restart and use a normal/hot restart instead (`pumactl restart`). That way, no code is shared while deploying (in that case, `preload_app!` might help for quicker deployment, see ["Clustered Mode" in the README](../README.md#clustered-mode)). ### Release Directory diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/docs/systemd.md new/docs/systemd.md --- old/docs/systemd.md 2018-01-19 20:23:10.000000000 +0100 +++ new/docs/systemd.md 2018-07-13 18:08:52.000000000 +0200 @@ -102,6 +102,16 @@ Note that the above configurations will work with Puma in either single process or cluster mode. +### Sockets and symlinks + +When using releases folders, you should set the socket path using the +shared folder path (ex. `/srv/projet/shared/tmp/puma.sock`), not the +release folder path (`/srv/projet/releases/1234/tmp/puma.sock`). + +Puma will detect the release path socket as different than the one provided by +systemd and attempt to bind it again, resulting in the exception + `There is already a server bound to:`. + ## Usage Without socket activation, use `systemctl` as root (e.g. via `sudo`) as 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 2018-01-19 20:23:10.000000000 +0100 +++ new/ext/puma_http11/mini_ssl.c 2018-07-13 18:08:52.000000000 +0200 @@ -161,6 +161,9 @@ ID sym_verify_mode = rb_intern("verify_mode"); VALUE verify_mode = rb_funcall(mini_ssl_ctx, sym_verify_mode, 0); + ID sym_ssl_cipher_filter = rb_intern("ssl_cipher_filter"); + VALUE ssl_cipher_filter = rb_funcall(mini_ssl_ctx, sym_ssl_cipher_filter, 0); + ctx = SSL_CTX_new(SSLv23_server_method()); conn->ctx = ctx; @@ -175,7 +178,13 @@ SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION); SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); - SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL@STRENGTH"); + if (!NIL_P(ssl_cipher_filter)) { + StringValue(ssl_cipher_filter); + SSL_CTX_set_cipher_list(ctx, RSTRING_PTR(ssl_cipher_filter)); + } + else { + SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL@STRENGTH"); + } DH *dh = get_dh1024(); SSL_CTX_set_tmp_dh(ctx, dh); 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 2018-01-19 20:23:10.000000000 +0100 +++ new/ext/puma_http11/org/jruby/puma/MiniSSL.java 2018-07-13 18:08:52.000000000 +0200 @@ -170,6 +170,12 @@ engine.setNeedClientAuth(true); } + IRubyObject sslCipherListObject = miniSSLContext.callMethod(threadContext, "ssl_cipher_list"); + if (!sslCipherListObject.isNil()) { + String[] sslCipherList = sslCipherListObject.convertToString().asJavaString().split(","); + engine.setEnabledCipherSuites(sslCipherList); + } + SSLSession session = engine.getSession(); inboundNetData = new MiniSSLBuffer(session.getPacketBufferSize()); outboundAppData = new MiniSSLBuffer(session.getApplicationBufferSize()); 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 2018-01-19 20:23:10.000000000 +0100 +++ new/lib/puma/binder.rb 2018-07-13 18:08:52.000000000 +0200 @@ -162,6 +162,7 @@ end 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='" @@ -182,6 +183,7 @@ end ctx.ca = params['ca'] if params['ca'] + ctx.ssl_cipher_filter = params['ssl_cipher_filter'] if params['ssl_cipher_filter'] end if params['verify_mode'] @@ -313,6 +315,7 @@ s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true) s.listen backlog + ssl = MiniSSL::Server.new s, ctx env = @proto_env.dup env[HTTPS_KEY] = HTTPS 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 2018-01-19 20:23:10.000000000 +0100 +++ new/lib/puma/cli.rb 2018-07-13 18:08:52.000000000 +0200 @@ -1,6 +1,7 @@ require 'optparse' require 'uri' +require 'puma' require 'puma/configuration' require 'puma/launcher' require 'puma/const' @@ -83,6 +84,14 @@ raise UnsupportedOption end + def configure_control_url(command_line_arg) + if command_line_arg + @control_url = command_line_arg + elsif Puma.jruby? + unsupported "No default url available on JRuby" + end + end + # Build the OptionParser object to handle the available options. # @@ -97,13 +106,13 @@ file_config.load arg end - o.on "--control URL", "The bind url to use for the control server", - "Use 'auto' to use temp unix server" do |arg| - if arg - @control_url = arg - elsif Puma.jruby? - unsupported "No default url available on JRuby" - end + o.on "--control-url URL", "The bind url to use for the control server. Use 'auto' to use temp unix server" do |arg| + configure_control_url(arg) + end + + # alias --control-url for backwards-compatibility + o.on "--control URL", "DEPRECATED alias for --control-url" do |arg| + configure_control_url(arg) end o.on "--control-token TOKEN", 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 2018-01-19 20:23:10.000000000 +0100 +++ new/lib/puma/client.rb 2018-07-13 18:08:52.000000000 +0200 @@ -21,6 +21,17 @@ class ConnectionError < RuntimeError; end + # An instance of this class represents a unique request from a client. + # For example a web request from a browser or from CURL. This + # + # An instance of `Puma::Client` can be used as if it were an IO object + # for example it is passed into `IO.select` inside of the `Puma::Reactor`. + # This is accomplished by the `to_io` method which gets called on any + # non-IO objects being used with the IO api such as `IO.select. + # + # Instances of this class are responsible for knowing if + # the header and body are fully buffered via the `try_to_finish` method. + # They can be used to "time out" a response via the `timeout_at` reader. class Client include Puma::Const extend Puma::Delegation 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 2018-01-19 20:23:10.000000000 +0100 +++ new/lib/puma/cluster.rb 2018-07-13 18:08:52.000000000 +0200 @@ -5,6 +5,17 @@ require 'time' module Puma + # This class is instantiated by the `Puma::Launcher` and used + # to boot and serve a Ruby application when puma "workers" are needed + # i.e. when using multi-processes. For example `$ puma -w 5` + # + # At the core of this class is running an instance of `Puma::Server` which + # gets created via the `start_server` method from the `Puma::Runner` class + # that this inherits from. + # + # An instance of this class will spawn the number of processes passed in + # via the `spawn_workers` method call. Each worker will have it's own + # instance of a `Puma::Server`. class Cluster < Runner WORKER_CHECK_INTERVAL = 5 @@ -281,7 +292,9 @@ begin b = server.backlog || 0 r = server.running || 0 - payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r} }\n! + t = server.pool_capacity || 0 + m = server.max_threads || 0 + payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads": #{m} }\n! io << payload rescue IOError Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue 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 2018-01-19 20:23:10.000000000 +0100 +++ new/lib/puma/const.rb 2018-07-13 18:08:52.000000000 +0200 @@ -98,8 +98,8 @@ # too taxing on performance. module Const - PUMA_VERSION = VERSION = "3.11.2".freeze - CODE_NAME = "Love Song".freeze + PUMA_VERSION = VERSION = "3.12.0".freeze + CODE_NAME = "Llamas in Pajamas".freeze PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze FAST_TRACK_KA_TIMEOUT = 0.2 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 2018-01-19 20:23:10.000000000 +0100 +++ new/lib/puma/control_cli.rb 2018-07-13 18:08:52.000000000 +0200 @@ -69,6 +69,7 @@ end opts.order!(argv) { |a| opts.terminate a } + opts.parse! @command = argv.shift @@ -204,7 +205,6 @@ Process.kill "SIGUSR1", @pid else - message "Puma is started" return end @@ -220,7 +220,7 @@ end def run - start if @command == "start" + return start if @command == "start" prepare_configuration 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 2018-01-19 20:23:10.000000000 +0100 +++ new/lib/puma/dsl.rb 2018-07-13 18:08:52.000000000 +0200 @@ -146,13 +146,13 @@ # them # def persistent_timeout(seconds) - @options[:persistent_timeout] = seconds + @options[:persistent_timeout] = Integer(seconds) end # Define how long the tcp socket stays open, if no data has been received # def first_data_timeout(seconds) - @options[:first_data_timeout] = seconds + @options[:first_data_timeout] = Integer(seconds) end # Work around leaky apps that leave garbage in Thread locals @@ -169,7 +169,7 @@ end # When shutting down, drain the accept socket of pending - # connections and proces them. This loops over the accept + # connections and process them. This loops over the accept # socket until there are no more read events and then stops # looking and waits for the requests to finish. def drain_on_shutdown(which=true) @@ -424,17 +424,17 @@ # that have not checked in within the given +timeout+. # This mitigates hung processes. Default value is 60 seconds. def worker_timeout(timeout) - @options[:worker_timeout] = timeout + @options[:worker_timeout] = Integer(timeout) end # *Cluster mode only* Set the timeout for workers to boot def worker_boot_timeout(timeout) - @options[:worker_boot_timeout] = timeout + @options[:worker_boot_timeout] = Integer(timeout) end # *Cluster mode only* Set the timeout for worker shutdown def worker_shutdown_timeout(timeout) - @options[:worker_shutdown_timeout] = timeout + @options[:worker_shutdown_timeout] = Integer(timeout) end # When set to true (the default), workers accept all requests 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 2018-01-19 20:23:10.000000000 +0100 +++ new/lib/puma/launcher.rb 2018-07-13 18:08:52.000000000 +0200 @@ -86,6 +86,7 @@ else @runner = Single.new(self, @events) end + Puma.stats_object = @runner @status = :run end 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 2018-01-19 20:23:10.000000000 +0100 +++ new/lib/puma/minissl.rb 2018-07-13 18:08:52.000000000 +0200 @@ -16,6 +16,10 @@ @socket end + def closed? + @socket.closed? + end + def readpartial(size) while true output = @engine.read @@ -77,6 +81,8 @@ end def write(data) + return 0 if data.empty? + need = data.bytesize while true @@ -118,7 +124,7 @@ def read_and_drop(timeout = 1) return :timeout unless IO.select([@socket], nil, nil, timeout) - read_nonblock(1024) + return :eof unless read_nonblock(1024) :drop rescue Errno::EAGAIN # do nothing @@ -135,7 +141,7 @@ # Don't let this socket hold this loop forever. # If it can't send more packets within 1s, then give up. while should_drop_bytes? - return if read_and_drop(1) == :timeout + return if [:timeout, :eof].include?(read_and_drop(1)) end rescue IOError, SystemCallError Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue @@ -174,6 +180,7 @@ # jruby-specific Context properties: java uses a keystore and password pair rather than a cert/key pair attr_reader :keystore attr_accessor :keystore_pass + attr_accessor :ssl_cipher_list def keystore=(keystore) raise ArgumentError, "No such keystore file '#{keystore}'" unless File.exist? keystore @@ -189,6 +196,7 @@ attr_reader :key attr_reader :cert attr_reader :ca + attr_accessor :ssl_cipher_filter def key=(key) raise ArgumentError, "No such key file '#{key}'" unless File.exist? key @@ -243,7 +251,7 @@ end def close - @socket.close + @socket.close unless @socket.closed? # closed? call is for Windows end end end 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 2018-01-19 20:23:10.000000000 +0100 +++ new/lib/puma/reactor.rb 2018-07-13 18:08:52.000000000 +0200 @@ -2,15 +2,54 @@ require 'puma/minissl' module Puma + # Internal Docs, Not a public interface. + # + # The Reactor object is responsible for ensuring that a request has been + # completely received before it starts to be processed. This may be known as read buffering. + # If read buffering is not done, and no other read buffering is performed (such as by an application server + # such as nginx) then the application would be subject to a slow client attack. + # + # Each Puma "worker" process has its own Reactor. For example if you start puma with `$ puma -w 5` then + # it will have 5 workers and each worker will have it's own reactor. + # + # For a graphical representation of how the reactor works see [architecture.md](https://github.com/puma/puma/blob/master/docs/architecture.md#connection-pipeline). + # + # ## Reactor Flow + # + # A request comes into a `Puma::Server` instance, it is then passed to a `Puma::Reactor` instance. + # The reactor stores the request in an array and calls `IO.select` on the array in a loop. + # + # When the request is written to by the client then the `IO.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 + # have a full header and body then the reactor passes the request to a thread pool. + # Once in a thread pool, a "worker thread" can run the the application's Ruby code against the request. + # + # If the request is not complete, then it stays in the array, and the next time any + # data is written to that socket reference, then the loop is woken up and it is checked for completeness again. + # + # A detailed example is given in the docs for `run_internal` which is where the bulk + # of this logic lives. class Reactor DefaultSleepFor = 5 + # Creates an instance of Puma::Reactor + # + # The `server` argument is an instance of `Puma::Server` + # this is used to write a response for "low level errors" + # when there is an exception inside of the reactor. + # + # The `app_pool` is an instance of `Puma::ThreadPool`. + # Once a request is fully formed (header and body are received) + # it will be passed to the `app_pool`. def initialize(server, app_pool) @server = server @events = server.events @app_pool = app_pool @mutex = Mutex.new + + # Read / Write pipes to wake up internal while loop @ready, @trigger = Puma::Util.pipe @input = [] @sleep_for = DefaultSleepFor @@ -21,6 +60,64 @@ private + + # Until a request is added via the `add` method this method will internally + # loop, waiting on the `sockets` array objects. The only object in this + # array at first is the `@ready` IO object, which is the read end of a pipe + # connected to `@trigger` object. When `@trigger` is written to, then the loop + # will break on `IO.select` and return an array. + # + # ## When a request is added: + # + # When the `add` method is called, an instance of `Puma::Client` is added to the `@input` array. + # Next the `@ready` pipe is "woken" by writing a string of `"*"` to `@trigger`. + # + # When that happens, the internal loop stops blocking at `IO.select` and returns a reference + # to whatever "woke" it up. On the very first loop, the only thing in `sockets` is `@ready`. + # When `@trigger` is written-to, the loop "wakes" and the `ready` + # variable returns an array of arrays that looks like `[[#<IO:fd 10>], [], []]` where the + # first IO object is the `@ready` object. This first array `[#<IO:fd 10>]` + # is saved as a `reads` variable. + # + # The `reads` variable is iterated through. In the case that the object + # is the same as the `@ready` input pipe, then we know that there was a `trigger` event. + # + # If there was a trigger event, then one byte of `@ready` is read into memory. In the case of the first request, + # the reactor sees that it's a `"*"` value and the reactor adds the contents of `@input` into the `sockets` array. + # The while then loop continues to iterate again, but now the `sockets` array contains a `Puma::Client` instance in addition + # to the `@ready` IO object. For example: `[#<IO:fd 10>, #<Puma::Client:0x3fdc1103bee8 @ready=false>]`. + # + # Since the `Puma::Client` in this example has data that has not been read yet, + # the `IO.select` is immediately able to "wake" and read from the `Puma::Client`. At this point the + # `ready` output looks like this: `[[#<Puma::Client:0x3fdc1103bee8 @ready=false>], [], []]`. + # + # Each element in the first entry is iterated over. The `Puma::Client` object is not + # the `@ready` pipe, so the reactor checks to see if it has the fully header and body with + # the `Puma::Client#try_to_finish` method. If the full request has been sent, + # then the request is passed off to the `@app_pool` thread pool so that a "worker thread" + # can pick up the request and begin to execute application logic. This is done + # via `@app_pool << c`. The `Puma::Client` is then removed from the `sockets` array. + # + # If the request body is not present then nothing will happen, and the loop will iterate + # again. When the client sends more data to the socket the `Puma::Client` object will + # wake up the `IO.select` and it can again be checked to see if it's ready to be + # passed to the thread pool. + # + # ## Time Out Case + # + # In addition to being woken via a write to one of the sockets the `IO.select` will + # periodically "time out" of the sleep. One of the functions of this is to check for + # any requests that have "timed out". At the end of the loop it's checked to see if + # the first element in the `@timeout` array has exceed it's allowed time. If so, + # the client object is removed from the timeout aray, a 408 response is written. + # Then it's connection is closed, and the object is removed from the `sockets` array + # that watches for new data. + # + # This behavior loops until all the objects that have timed out have been removed. + # + # Once all the timeouts have been processed, the next duration of the `IO.select` sleep + # will be set to be equal to the amount of time it will take for the next timeout to occur. + # This calculation happens in `calculate_sleep`. def run_internal sockets = @sockets @@ -163,6 +260,16 @@ end end + # The `calculate_sleep` sets the value that the `IO.select` will + # sleep for in the main reactor loop when no sockets are being written to. + # + # The values kept in `@timeouts` are sorted so that the first timeout + # comes first in the array. When there are no timeouts the default timeout is used. + # + # Otherwise a sleep value is set that is the same as the amount of time it + # would take for the first element to time out. + # + # If that value is in the past, then a sleep value of zero is used. def calculate_sleep if @timeouts.empty? @sleep_for = DefaultSleepFor @@ -177,6 +284,31 @@ end end + # This method adds a connection to the reactor + # + # Typically called by `Puma::Server` the value passed in + # is usually a `Puma::Client` object that responds like an IO + # object. + # + # The main body of the reactor loop is in `run_internal` and it + # will sleep on `IO.select`. When a new connection is added to the + # reactor it cannot be added directly to the `sockets` aray, because + # the `IO.select` will not be watching for it yet. + # + # Instead what needs to happen is that `IO.select` needs to be woken up, + # the contents of `@input` added to the `sockets` array, and then + # another call to `IO.select` needs to happen. Since the `Puma::Client` + # object can be read immediately, it does not block, but instead returns + # right away. + # + # This behavior is accomplished by writing to `@trigger` which wakes up + # the `IO.select` and then there is logic to detect the value of `*`, + # pull the contents from `@input` and add them to the sockets array. + # + # If the object passed in has a timeout value in `timeout_at` then + # it is added to a `@timeouts` array. This array is then re-arranged + # so that the first element to timeout will be at the front of the + # array. Then a value to sleep for is derived in the call to `calculate_sleep` def add(c) @mutex.synchronize do @input << c 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 2018-01-19 20:23:10.000000000 +0100 +++ new/lib/puma/runner.rb 2018-07-13 18:08:52.000000000 +0200 @@ -2,6 +2,9 @@ require 'puma/const' module Puma + # Generic class that is used by `Puma::Cluster` and `Puma::Single` to + # serve requests. This class spawns a new instance of `Puma::Server` via + # a call to `start_server`. class Runner def initialize(cli, events) @launcher = cli @@ -19,6 +22,10 @@ @options[:environment] == "development" end + def test? + @options[:environment] == "test" + end + def log(str) @events.log str end @@ -165,7 +172,7 @@ server.early_hints = true end - unless development? + unless development? || test? server.leak_stack_on_error = false end 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 2018-01-19 20:23:10.000000000 +0100 +++ new/lib/puma/server.rb 2018-07-13 18:08:52.000000000 +0200 @@ -23,6 +23,15 @@ module Puma # The HTTP Server itself. Serves out a single Rack app. + # + # This class is used by the `Puma::Single` and `Puma::Cluster` classes + # to generate one or more `Puma::Server` instances capable of handling requests. + # Each Puma process will contain one `Puma::Server` instacne. + # + # The `Puma::Server` instance pulls requests from the socket, adds them to a + # `Puma::Reactor` where they get eventually passed to a `Puma::ThreadPool`. + # + # Each `Puma::Server` will have one reactor and one thread pool. class Server include Puma::Const @@ -159,6 +168,18 @@ @thread_pool and @thread_pool.spawned end + + # This number represents the number of requests that + # the server is capable of taking right now. + # + # For example if the number is 5 then it means + # there are 5 threads sitting idle ready to take + # a request. If one request comes in, then the + # value would be 4 until it finishes processing. + def pool_capacity + @thread_pool and @thread_pool.pool_capacity + end + # Lopez Mode == raw tcp apps def run_lopez_mode(background=true) @@ -220,7 +241,11 @@ # nothing rescue Errno::ECONNABORTED # client closed the socket even before accept - io.close rescue nil + begin + io.close + rescue + Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue + end end end end @@ -237,7 +262,12 @@ STDERR.puts "Exception handling servers: #{e.message} (#{e.class})" STDERR.puts e.backtrace ensure - @check.close + begin + @check.close + rescue + Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue + end + @notify.close if @status != :restart and @own_binder @@ -295,7 +325,7 @@ client.close @events.parse_error self, client.env, e - rescue ConnectionError + rescue ConnectionError, EOFError client.close else if process_now @@ -372,7 +402,11 @@ # nothing rescue Errno::ECONNABORTED # client closed the socket even before accept - io.close rescue nil + begin + io.close + rescue + Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue + end end end end @@ -606,7 +640,7 @@ fast_write client, "#{k}: #{v}\r\n" end else - fast_write client, "#{k}: #{v}\r\n" + fast_write client, "#{k}: #{vs}\r\n" end end @@ -755,8 +789,8 @@ begin res_body.each do |part| + next if part.bytesize.zero? if chunked - next if part.bytesize.zero? fast_write client, part.bytesize.to_s(16) fast_write client, line_ending fast_write client, part 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 2018-01-19 20:23:10.000000000 +0100 +++ new/lib/puma/single.rb 2018-07-13 18:08:52.000000000 +0200 @@ -3,11 +3,20 @@ require 'puma/plugin' module Puma + # This class is instantiated by the `Puma::Launcher` and used + # to boot and serve a Ruby application when no puma "workers" are needed + # i.e. only using "threaded" mode. For example `$ puma -t 1:5` + # + # At the core of this class is running an instance of `Puma::Server` which + # gets created via the `start_server` method from the `Puma::Runner` class + # that this inherits from. class Single < Runner def stats b = @server.backlog || 0 r = @server.running || 0 - %Q!{ "backlog": #{b}, "running": #{r} }! + t = @server.pool_capacity || 0 + m = @server.max_threads || 0 + %Q!{ "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/thread_pool.rb new/lib/puma/thread_pool.rb --- old/lib/puma/thread_pool.rb 2018-01-19 20:23:10.000000000 +0100 +++ new/lib/puma/thread_pool.rb 2018-07-13 18:08:52.000000000 +0200 @@ -1,8 +1,17 @@ require 'thread' module Puma - # A simple thread pool management object. + # Internal Docs for A simple thread pool management object. # + # Each Puma "worker" has a thread pool to process requests. + # + # First a connection to a client is made in `Puma::Server`. It is wrapped in a + # `Puma::Client` instance and then passed to the `Puma::Reactor` to ensure + # the whole request is buffered into memory. Once the request is ready, it is passed into + # a thread pool via the `Puma::ThreadPool#<<` operator where it is stored in a `@todo` array. + # + # Each thread in the pool has an internal loop where it pulls a request from the `@todo` array + # and proceses it. class ThreadPool class ForceShutdown < RuntimeError end @@ -49,7 +58,7 @@ @clean_thread_locals = false end - attr_reader :spawned, :trim_requested + attr_reader :spawned, :trim_requested, :waiting attr_accessor :clean_thread_locals def self.clean_thread_locals @@ -64,6 +73,10 @@ @mutex.synchronize { @todo.size } end + def pool_capacity + waiting + (@max - spawned) + end + # :nodoc: # # Must be called with @mutex held! @@ -153,16 +166,42 @@ end end + # This method is used by `Puma::Server` to let the server know when + # the thread pool can pull more requests from the socket and + # pass to the reactor. + # + # The general idea is that the thread pool can only work on a fixed + # number of requests at the same time. If it is already processing that + # number of requests then it is at capacity. If another Puma process has + # spare capacity, then the request can be left on the socket so the other + # worker can pick it up and process it. + # + # For example: if there are 5 threads, but only 4 working on + # requests, this method will not wait and the `Puma::Server` + # can pull a request right away. + # + # If there are 5 threads and all 5 of them are busy, then it will + # pause here, and wait until the `not_full` condition variable is + # signaled, usually this indicates that a request has been processed. + # + # It's important to note that even though the server might accept another + # request, it might not be added to the `@todo` array right away. + # For example if a slow client has only sent a header, but not a body + # then the `@todo` array would stay the same size as the reactor works + # to try to buffer the request. In tha scenario the next call to this + # method would not block and another request would be added into the reactor + # by the server. This would continue until a fully bufferend request + # makes it through the reactor and can then be processed by the thread pool. def wait_until_not_full @mutex.synchronize do while true return if @shutdown - return if @waiting > 0 # If we can still spin up new threads and there - # is work queued, then accept more work until we would + # is work queued that cannot be handled by waiting + # threads, then accept more work until we would # spin up the max number of threads. - return if @todo.size < @max - @spawned + return if @todo.size - @waiting < @max - @spawned @not_full.wait @mutex end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma.rb new/lib/puma.rb --- old/lib/puma.rb 2018-01-19 20:23:10.000000000 +0100 +++ new/lib/puma.rb 2018-07-13 18:08:52.000000000 +0200 @@ -12,4 +12,12 @@ autoload :Const, 'puma/const' autoload :Server, 'puma/server' autoload :Launcher, 'puma/launcher' + + def self.stats_object=(val) + @get_stats = val + end + + def self.stats + @get_stats.stats + end end 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 2018-01-19 20:23:10.000000000 +0100 +++ new/lib/rack/handler/puma.rb 2018-07-13 18:08:52.000000000 +0200 @@ -9,6 +9,7 @@ } def self.config(app, options = {}) + require 'puma' require 'puma/configuration' require 'puma/events' require 'puma/launcher' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/metadata new/metadata --- old/metadata 2018-01-19 20:23:10.000000000 +0100 +++ new/metadata 2018-07-13 18:08:52.000000000 +0200 @@ -1,14 +1,14 @@ --- !ruby/object:Gem::Specification name: puma version: !ruby/object:Gem::Version - version: 3.11.2 + version: 3.12.0 platform: ruby authors: - Evan Phoenix autorequire: bindir: bin cert_chain: [] -date: 2018-01-19 00:00:00.000000000 Z +date: 2018-07-13 00:00:00.000000000 Z dependencies: [] description: Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server for Ruby/Rack applications. Puma is intended for use in both development and production @@ -96,6 +96,9 @@ - tools/jungle/init.d/README.md - tools/jungle/init.d/puma - tools/jungle/init.d/run-puma +- tools/jungle/rc.d/README.md +- tools/jungle/rc.d/puma +- tools/jungle/rc.d/puma.conf - tools/jungle/upstart/README.md - tools/jungle/upstart/puma-manager.conf - tools/jungle/upstart/puma.conf @@ -113,7 +116,7 @@ requirements: - - ">=" - !ruby/object:Gem::Version - version: 1.9.3 + version: '2.2' required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ">=" @@ -121,7 +124,7 @@ version: '0' requirements: [] rubyforge_project: -rubygems_version: 2.6.8 +rubygems_version: 2.7.6 signing_key: specification_version: 4 summary: Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server for diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tools/jungle/README.md new/tools/jungle/README.md --- old/tools/jungle/README.md 2018-01-19 20:23:10.000000000 +0100 +++ new/tools/jungle/README.md 2018-07-13 18:08:52.000000000 +0200 @@ -1,9 +1,5 @@ # Puma as a service -## Init.d - -See `/tools/jungle/init.d` for tools to use with init.d and start-stop-daemon. - ## Upstart See `/tools/jungle/upstart` for Ubuntu's upstart scripts. @@ -11,3 +7,13 @@ ## Systemd See [/docs/systemd](https://github.com/puma/puma/blob/master/docs/systemd.md). + +## Init.d + +Deprecatation Warning : `init.d` was replaced by `systemd` since Debian 8 and Ubuntu 16.04, you should look into [/docs/systemd](https://github.com/puma/puma/blob/master/docs/systemd.md) unless you are on an older OS. + +See `/tools/jungle/init.d` for tools to use with init.d and start-stop-daemon. + +## rc.d + +See `/tools/jungle/rc.d` for FreeBSD's rc.d scripts diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tools/jungle/init.d/README.md new/tools/jungle/init.d/README.md --- old/tools/jungle/init.d/README.md 2018-01-19 20:23:10.000000000 +0100 +++ new/tools/jungle/init.d/README.md 2018-07-13 18:08:52.000000000 +0200 @@ -1,5 +1,7 @@ # Puma daemon service +Deprecatation Warning : `init.d` was replaced by `systemd` since Debian 8 and Ubuntu 16.04, you should look into [/docs/systemd](https://github.com/puma/puma/blob/master/docs/systemd.md) unless you are on an older OS. + Init script to manage multiple Puma servers on the same box using start-stop-daemon. ## Installation 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 2018-01-19 20:23:10.000000000 +0100 +++ new/tools/jungle/init.d/puma 2018-07-13 18:08:52.000000000 +0200 @@ -48,7 +48,7 @@ if [ -e $PIDFILE ]; then PID=`cat $PIDFILE` # If the puma isn't running, run it, otherwise restart it. - if [ "`ps -A -o pid= | grep -c $PID`" -eq 0 ]; then + if ps -p $PID > /dev/null; then do_start_one_do $1 else do_restart_one $1 @@ -105,7 +105,7 @@ STATEFILE=$1/tmp/puma/state if [ -e $PIDFILE ]; then PID=`cat $PIDFILE` - if [ "`ps -A -o pid= | grep -c $PID`" -eq 0 ]; then + if ps -p $PID > /dev/null; then log_daemon_msg "---> Puma $1 isn't running." else log_daemon_msg "---> About to kill PID `cat $PIDFILE`" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tools/jungle/init.d/run-puma new/tools/jungle/init.d/run-puma --- old/tools/jungle/init.d/run-puma 2018-01-19 20:23:10.000000000 +0100 +++ new/tools/jungle/init.d/run-puma 2018-07-13 18:08:52.000000000 +0200 @@ -15,4 +15,4 @@ fi app=$1; config=$2; log=$3; -cd $app && exec bundle exec puma -C $config 2>&1 >> $log +cd $app && exec bundle exec puma -C $config >> $log 2>&1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tools/jungle/rc.d/README.md new/tools/jungle/rc.d/README.md --- old/tools/jungle/rc.d/README.md 1970-01-01 01:00:00.000000000 +0100 +++ new/tools/jungle/rc.d/README.md 2018-07-13 18:08:52.000000000 +0200 @@ -0,0 +1,74 @@ +# Puma as a service using rc.d + +Manage multilpe Puma servers as services on one box using FreeBSD's rc.d service. + +## Dependencies + +* `jq` - a command-line json parser is needed to parse the json in the config file + +## Installation + + # Copy the puma script to the rc.d directory (make sure everyone has read/execute perms) + sudo cp puma /usr/local/etc/rc.d/ + + # Create an empty configuration file + sudo touch /usr/local/etc/puma.conf + + # Enable the puma service + sudo echo 'puma_enable="YES"' >> /etc/rc.conf + +## Managing the jungle + +Puma apps are referenced in /usr/local/etc/puma.conf by default. + +Start the jungle running: + +`service puma start` + +This script will run at boot time. + + +You can also stop the jungle (stops ALL puma instances) by running: + +`service puma stop` + + +To restart the jungle: + +`service puma restart` + +## Conventions + +* The script expects: + * a config file to exist under `config/puma.rb` in your app. E.g.: `/home/apps/my-app/config/puma.rb`. + +You can always change those defaults by editing the scripts. + +## Here's what a minimal app's config file should have + +``` +{ + "servers" : [ + { + "dir": "/path/to/rails/project", + "user": "deploy-user", + "ruby_version": "ruby.version", + "ruby_env": "rbenv" + } + ] +} +``` + +## Before starting... + +You need to customise `puma.conf` to: + +* Set the right user your app should be running on unless you want root to execute it! +* Set the directory of the app +* Set the ruby version to execute +* Set the ruby environment (currently set to rbenv, since that is the only ruby environment currently supported) +* Add additional server instances following the scheme in the example + +## Notes: + +Only rbenv is currently supported. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tools/jungle/rc.d/puma new/tools/jungle/rc.d/puma --- old/tools/jungle/rc.d/puma 1970-01-01 01:00:00.000000000 +0100 +++ new/tools/jungle/rc.d/puma 2018-07-13 18:08:52.000000000 +0200 @@ -0,0 +1,61 @@ +#!/bin/sh +# + +# PROVIDE: puma + +. /etc/rc.subr + +name="puma" +start_cmd="puma_start" +stop_cmd="puma_stop" +restart_cmd="puma_restart" +rcvar=puma_enable +required_files=/usr/local/etc/puma.conf + +puma_start() +{ + server_count=$(/usr/local/bin/jq ".servers[] .ruby_env" /usr/local/etc/puma.conf | wc -l) + i=0 + while [ "$i" -lt "$server_count" ]; do + rb_env=$(/usr/local/bin/jq -r ".servers[$i].ruby_env" /usr/local/etc/puma.conf) + dir=$(/usr/local/bin/jq -r ".servers[$i].dir" /usr/local/etc/puma.conf) + user=$(/usr/local/bin/jq -r ".servers[$i].user" /usr/local/etc/puma.conf) + rb_ver=$(/usr/local/bin/jq -r ".servers[$i].ruby_version" /usr/local/etc/puma.conf) + case $rb_env in + "rbenv") + su - $user -c "cd $dir && rbenv shell $rb_ver && bundle exec puma -C $dir/config/puma.rb -d" + ;; + *) + ;; + esac + i=$(( i + 1 )) + done +} + +puma_stop() +{ + pkill ruby +} + +puma_restart() +{ + server_count=$(/usr/local/bin/jq ".servers[] .ruby_env" /usr/local/etc/puma.conf | wc -l) + i=0 + while [ "$i" -lt "$server_count" ]; do + rb_env=$(/usr/local/bin/jq -r ".servers[$i].ruby_env" /usr/local/etc/puma.conf) + dir=$(/usr/local/bin/jq -r ".servers[$i].dir" /usr/local/etc/puma.conf) + user=$(/usr/local/bin/jq -r ".servers[$i].user" /usr/local/etc/puma.conf) + rb_ver=$(/usr/local/bin/jq -r ".servers[$i].ruby_version" /usr/local/etc/puma.conf) + case $rb_env in + "rbenv") + su - $user -c "cd $dir && pkill ruby && rbenv shell $ruby_version && bundle exec puma -C $dir/config/puma.rb -d" + ;; + *) + ;; + esac + i=$(( i + 1 )) + done +} + +load_rc_config $name +run_rc_command "$1" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tools/jungle/rc.d/puma.conf new/tools/jungle/rc.d/puma.conf --- old/tools/jungle/rc.d/puma.conf 1970-01-01 01:00:00.000000000 +0100 +++ new/tools/jungle/rc.d/puma.conf 2018-07-13 18:08:52.000000000 +0200 @@ -0,0 +1,10 @@ +{ + "servers" : [ + { + "dir": "/path/to/rails/project", + "user": "deploy-user", + "ruby_version": "ruby.version", + "ruby_env": "rbenv" + } + ] +}
