# frozen_string_literal: truerequire'puma/rack/builder'require'puma/plugin'require'puma/const'modulePumamoduleConfigDefaultDefaultRackup="config.ru"DefaultTCPHost="0.0.0.0"DefaultTCPPort=9292DefaultWorkerTimeout=60DefaultWorkerShutdownTimeout=30end# A class used for storing "leveled" configuration options.## In this class any "user" specified options take precedence over any# "file" specified options, take precedence over any "default" options.## User input is preferred over "defaults":# user_options = { foo: "bar" }# default_options = { foo: "zoo" }# options = UserFileDefaultOptions.new(user_options, default_options)# puts options[:foo]# # => "bar"## All values can be accessed via `all_of`## puts options.all_of(:foo)# # => ["bar", "zoo"]## A "file" option can be set. This config will be preferred over "default" options# but will defer to any available "user" specified options.## user_options = { foo: "bar" }# default_options = { rackup: "zoo.rb" }# options = UserFileDefaultOptions.new(user_options, default_options)# options.file_options[:rackup] = "sup.rb"# puts options[:rackup]# # => "sup.rb"## The "default" options can be set via procs. These are resolved during runtime# via calls to `finalize_values`classUserFileDefaultOptionsdefinitialize(user_options,default_options)@user_options=user_options@file_options={}@default_options=default_optionsendattr_reader:user_options,:file_options,:default_optionsdef[](key)returnuser_options[key]ifuser_options.key?(key)returnfile_options[key]iffile_options.key?(key)returndefault_options[key]ifdefault_options.key?(key)enddef[]=(key,value)user_options[key]=valueenddeffetch(key,default_value=nil)self[key]||default_valueenddefall_of(key)user=user_options[key]file=file_options[key]default=default_options[key]user=[user]unlessuser.is_a?(Array)file=[file]unlessfile.is_a?(Array)default=[default]unlessdefault.is_a?(Array)user.compact!file.compact!default.compact!user+file+defaultenddeffinalize_values@default_options.eachdo|k,v|ifv.respond_to?:call@default_options[k]=v.callendendendend# The main configuration class of Puma.## It can be initialized with a set of "user" options and "default" options.# Defaults will be merged with `Configuration.puma_default_options`.## This class works together with 2 main other classes the `UserFileDefaultOptions`# which stores configuration options in order so the precedence is that user# set configuration wins over "file" based configuration wins over "default"# configuration. These configurations are set via the `DSL` class. This# class powers the Puma config file syntax and does double duty as a configuration# DSL used by the `Puma::CLI` and Puma rack handler.## It also handles loading plugins.## > Note: `:port` and `:host` are not valid keys. By they time they make it to the# configuration options they are expected to be incorporated into a `:binds` key.# Under the hood the DSL maps `port` and `host` calls to `:binds`## config = Configuration.new({}) do |user_config, file_config, default_config|# user_config.port 3003# end# config.load# puts config.options[:port]# # => 3003## It is expected that `load` is called on the configuration instance after setting# config. This method expands any values in `config_file` and puts them into the# correct configuration option hash.## Once all configuration is complete it is expected that `clamp` will be called# on the instance. This will expand any procs stored under "default" values. This# is done because an environment variable may have been modified while loading# configuration files.classConfigurationincludeConfigDefaultdefinitialize(user_options={},default_options={},&block)default_options=self.puma_default_options.merge(default_options)@options=UserFileDefaultOptions.new(user_options,default_options)@plugins=PluginLoader.new@user_dsl=DSL.new(@options.user_options,self)@file_dsl=DSL.new(@options.file_options,self)@default_dsl=DSL.new(@options.default_options,self)ifblockconfigure(&block)endendattr_reader:options,:pluginsdefconfigureyield@user_dsl,@file_dsl,@default_dslensure@user_dsl._offer_plugins@file_dsl._offer_plugins@default_dsl._offer_pluginsenddefinitialize_copy(other)@conf=nil@cli_options=nil@options=@options.dupenddefflattendup.flatten!enddefflatten!@options=@options.flattenselfenddefpuma_default_options{:min_threads=>0,:max_threads=>16,:log_requests=>false,:debug=>false,:binds=>["tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"],:workers=>0,:daemon=>false,:mode=>:http,:worker_timeout=>DefaultWorkerTimeout,:worker_boot_timeout=>DefaultWorkerTimeout,:worker_shutdown_timeout=>DefaultWorkerShutdownTimeout,:remote_address=>:socket,:tag=>method(:infer_tag),:environment=>->{ENV['RACK_ENV']||"development"},:rackup=>DefaultRackup,:logger=>STDOUT,:persistent_timeout=>Const::PERSISTENT_TIMEOUT,:first_data_timeout=>Const::FIRST_DATA_TIMEOUT,:raise_exception_on_sigterm=>true}enddefloadconfig_files.each{|config_file|@file_dsl._load_from(config_file)}@optionsenddefconfig_filesfiles=@options.all_of(:config_files)return[]iffiles==['-']returnfilesiffiles.any?first_default_file=%W(config/puma/#{environment_str}.rb config/puma.rb).finddo|f|File.exist?(f)end[first_default_file]end# Call once all configuration (included from rackup files)# is loaded to flesh out any defaultsdefclamp@options.finalize_valuesend# Injects the Configuration object into the envclassConfigMiddlewaredefinitialize(config,app)@config=config@app=appenddefcall(env)env[Const::PUMA_CONFIG]=@config@app.call(env)endend# Indicate if there is a properly configured app#defapp_configured?@options[:app]||File.exist?(rackup)enddefrackup@options[:rackup]end# Load the specified rackup file, pull options from# the rackup file, and set @app.#defappfound=options[:app]||load_rackupif@options[:mode]==:tcprequire'puma/tcp_logger'logger=@options[:logger]quiet=!@options[:log_requests]returnTCPLogger.new(logger,found,quiet)endif@options[:log_requests]require'puma/commonlogger'logger=@options[:logger]found=CommonLogger.new(found,logger)endConfigMiddleware.new(self,found)end# Return which environment we're running indefenvironment@options[:environment]enddefenvironment_strenvironment.respond_to?(:call)?environment.call:environmentenddefload_plugin(name)@plugins.createnameenddefrun_hooks(key,arg)@options.all_of(key).each{|b|b.callarg}enddefself.temp_pathrequire'tmpdir't=(Time.now.to_f*1000).to_i"#{Dir.tmpdir}/puma-status-#{t}-#{$$}"endprivatedefinfer_tagFile.basename(Dir.getwd)end# Load and use the normal Rack builder if we can, otherwise# fallback to our minimal version.defrack_builder# Load bundler now if we can so that we can pickup rack from# a GemfileifENV.key?'PUMA_BUNDLER_PRUNED'beginrequire'bundler/setup'rescueLoadErrorendendbeginrequire'rack'require'rack/builder'rescueLoadError# ok, use builtin versionreturnPuma::Rack::Builderelsereturn::Rack::Builderendenddefload_rackupraise"Missing rackup file '#{rackup}'"unlessFile.exist?(rackup)rack_app,rack_options=rack_builder.parse_file(rackup)@options.file_options.merge!(rack_options)config_ru_binds=[]rack_options.eachdo|k,v|config_ru_binds<<vifk.to_s.start_with?("bind")end@options.file_options[:binds]=config_ru_bindsunlessconfig_ru_binds.empty?rack_appenddefself.random_tokenbeginrequire'openssl'rescueLoadErrorendcount=16bytes=nilifdefined?OpenSSL::Randombytes=OpenSSL::Random.random_bytes(count)elsifFile.exist?("/dev/urandom")File.open('/dev/urandom'){|f|bytes=f.read(count)}endifbytestoken="".dupbytes.each_byte{|b|token<<b.to_s(16)}elsetoken=(0..count).to_a.map{rand(255).to_s(16)}.joinendreturntokenendendendrequire'puma/dsl'