diff --git a/lib/puma/cli.rb b/lib/puma/cli.rb index 7bc5ae1028..ff38d1ad54 100644 --- a/lib/puma/cli.rb +++ b/lib/puma/cli.rb @@ -21,6 +21,7 @@ class << self # 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 diff --git a/lib/puma/launcher.rb b/lib/puma/launcher.rb index 589a580a45..d63e3a7b5b 100644 --- a/lib/puma/launcher.rb +++ b/lib/puma/launcher.rb @@ -15,6 +15,7 @@ module Puma # 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, diff --git a/lib/puma/state_file.rb b/lib/puma/state_file.rb index 3915877e7d..dd6362880f 100644 --- a/lib/puma/state_file.rb +++ b/lib/puma/state_file.rb @@ -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 @@ def save(path, permission = nil) 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 --git a/test/test_cli.rb b/test/test_cli.rb index d78bae410d..5c3b2b1c80 100644 --- a/test/test_cli.rb +++ b/test/test_cli.rb @@ -4,6 +4,7 @@ require "puma/cli" require "json" +require "psych" class TestCLI < Minitest::Test include SSLHelper if ::Puma::HAS_SSL @@ -347,9 +348,21 @@ def test_tmp_control cli = Puma::CLI.new ["--state", @tmp_path, "--control-url", "auto"] cli.launcher.write_state - data = YAML.load File.read(@tmp_path) + opts = cli.launcher.instance_variable_get(:@options) - assert_equal Process.pid, data["pid"] + data = Psych.load_file @tmp_path + + Puma::StateFile::ALLOWED_FIELDS.each do |key| + val = + case key + when 'pid' then Process.pid + when 'running_from' then File.expand_path('.') # same as Launcher + else opts[key.to_sym] + end + assert_equal val, data[key] + end + + assert_equal (Puma::StateFile::ALLOWED_FIELDS & data.keys).sort, data.keys.sort url = data["control_url"] @@ -364,10 +377,9 @@ def test_state_file_callback_filtering "--state", @tmp_path ] cli.launcher.write_state - data = YAML.load_file(@tmp_path) + data = Psych.load_file @tmp_path - keys_not_stripped = data.keys & Puma::CLI::KEYS_NOT_TO_PERSIST_IN_STATE - assert_empty keys_not_stripped + assert_equal (Puma::StateFile::ALLOWED_FIELDS & data.keys).sort, data.keys.sort end def test_log_formatter_default_single @@ -401,7 +413,7 @@ def test_state cli = Puma::CLI.new ["--state", @tmp_path, "--control-url", url] cli.launcher.write_state - data = YAML.load File.read(@tmp_path) + data = Psych.load_file @tmp_path assert_equal Process.pid, data["pid"] assert_equal url, data["control_url"]