puma 2.1.1 → 7.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +3273 -0
  3. data/LICENSE +23 -20
  4. data/README.md +388 -100
  5. data/bin/puma-wild +25 -0
  6. data/bin/pumactl +1 -1
  7. data/docs/architecture.md +74 -0
  8. data/docs/compile_options.md +55 -0
  9. data/docs/deployment.md +137 -0
  10. data/docs/fork_worker.md +41 -0
  11. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  12. data/docs/images/puma-connection-flow.png +0 -0
  13. data/docs/images/puma-general-arch.png +0 -0
  14. data/docs/java_options.md +54 -0
  15. data/docs/jungle/README.md +9 -0
  16. data/docs/jungle/rc.d/README.md +74 -0
  17. data/docs/jungle/rc.d/puma +61 -0
  18. data/docs/jungle/rc.d/puma.conf +10 -0
  19. data/docs/kubernetes.md +73 -0
  20. data/docs/nginx.md +5 -5
  21. data/docs/plugins.md +42 -0
  22. data/docs/rails_dev_mode.md +28 -0
  23. data/docs/restart.md +65 -0
  24. data/docs/signals.md +98 -0
  25. data/docs/stats.md +148 -0
  26. data/docs/systemd.md +253 -0
  27. data/docs/testing_benchmarks_local_files.md +150 -0
  28. data/docs/testing_test_rackup_ci_files.md +36 -0
  29. data/ext/puma_http11/PumaHttp11Service.java +2 -2
  30. data/ext/puma_http11/extconf.rb +59 -2
  31. data/ext/puma_http11/http11_parser.c +319 -487
  32. data/ext/puma_http11/http11_parser.h +15 -13
  33. data/ext/puma_http11/http11_parser.java.rl +27 -43
  34. data/ext/puma_http11/http11_parser.rl +27 -24
  35. data/ext/puma_http11/http11_parser_common.rl +8 -8
  36. data/ext/puma_http11/mini_ssl.c +696 -38
  37. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  38. data/ext/puma_http11/org/jruby/puma/Http11.java +145 -113
  39. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +139 -172
  40. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +413 -193
  41. data/ext/puma_http11/puma_http11.c +183 -175
  42. data/lib/puma/app/status.rb +77 -29
  43. data/lib/puma/binder.rb +349 -119
  44. data/lib/puma/cli.rb +164 -769
  45. data/lib/puma/client.rb +591 -132
  46. data/lib/puma/cluster/worker.rb +183 -0
  47. data/lib/puma/cluster/worker_handle.rb +127 -0
  48. data/lib/puma/cluster.rb +634 -0
  49. data/lib/puma/cluster_accept_loop_delay.rb +91 -0
  50. data/lib/puma/commonlogger.rb +115 -0
  51. data/lib/puma/configuration.rb +370 -202
  52. data/lib/puma/const.rb +266 -115
  53. data/lib/puma/control_cli.rb +219 -120
  54. data/lib/puma/detect.rb +45 -2
  55. data/lib/puma/dsl.rb +1496 -0
  56. data/lib/puma/error_logger.rb +115 -0
  57. data/lib/puma/events.rb +44 -60
  58. data/lib/puma/io_buffer.rb +48 -5
  59. data/lib/puma/jruby_restart.rb +2 -51
  60. data/lib/puma/json_serialization.rb +96 -0
  61. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  62. data/lib/puma/launcher.rb +502 -0
  63. data/lib/puma/log_writer.rb +147 -0
  64. data/lib/puma/minissl/context_builder.rb +96 -0
  65. data/lib/puma/minissl.rb +355 -37
  66. data/lib/puma/null_io.rb +79 -12
  67. data/lib/puma/plugin/systemd.rb +90 -0
  68. data/lib/puma/plugin/tmp_restart.rb +36 -0
  69. data/lib/puma/plugin.rb +111 -0
  70. data/lib/puma/rack/builder.rb +297 -0
  71. data/lib/puma/rack/urlmap.rb +93 -0
  72. data/lib/puma/rack_default.rb +21 -4
  73. data/lib/puma/reactor.rb +102 -133
  74. data/lib/puma/request.rb +703 -0
  75. data/lib/puma/runner.rb +211 -0
  76. data/lib/puma/sd_notify.rb +146 -0
  77. data/lib/puma/server.rb +544 -436
  78. data/lib/puma/single.rb +72 -0
  79. data/lib/puma/state_file.rb +69 -0
  80. data/lib/puma/thread_pool.rb +282 -56
  81. data/lib/puma/util.rb +125 -0
  82. data/lib/puma.rb +80 -6
  83. data/lib/rack/handler/puma.rb +125 -47
  84. data/tools/Dockerfile +26 -0
  85. data/tools/trickletest.rb +44 -0
  86. metadata +77 -148
  87. data/COPYING +0 -55
  88. data/Gemfile +0 -10
  89. data/History.txt +0 -291
  90. data/Manifest.txt +0 -60
  91. data/Rakefile +0 -121
  92. data/TODO +0 -5
  93. data/docs/config.md +0 -0
  94. data/ext/puma_http11/ext_help.h +0 -15
  95. data/ext/puma_http11/io_buffer.c +0 -154
  96. data/lib/puma/accept_nonblock.rb +0 -23
  97. data/lib/puma/capistrano.rb +0 -34
  98. data/lib/puma/compat.rb +0 -11
  99. data/lib/puma/daemon_ext.rb +0 -20
  100. data/lib/puma/delegation.rb +0 -11
  101. data/lib/puma/java_io_buffer.rb +0 -45
  102. data/lib/puma/rack_patch.rb +0 -25
  103. data/puma.gemspec +0 -45
  104. data/test/test_app_status.rb +0 -88
  105. data/test/test_cli.rb +0 -171
  106. data/test/test_config.rb +0 -16
  107. data/test/test_http10.rb +0 -27
  108. data/test/test_http11.rb +0 -126
  109. data/test/test_integration.rb +0 -154
  110. data/test/test_iobuffer.rb +0 -38
  111. data/test/test_minissl.rb +0 -25
  112. data/test/test_null_io.rb +0 -31
  113. data/test/test_persistent.rb +0 -238
  114. data/test/test_puma_server.rb +0 -207
  115. data/test/test_rack_handler.rb +0 -10
  116. data/test/test_rack_server.rb +0 -141
  117. data/test/test_thread_pool.rb +0 -146
  118. data/test/test_unix_socket.rb +0 -39
  119. data/test/test_ws.rb +0 -89
  120. data/tools/jungle/init.d/README.md +0 -54
  121. data/tools/jungle/init.d/puma +0 -332
  122. data/tools/jungle/init.d/run-puma +0 -3
  123. data/tools/jungle/upstart/README.md +0 -61
  124. data/tools/jungle/upstart/puma-manager.conf +0 -31
  125. data/tools/jungle/upstart/puma.conf +0 -52
@@ -1,57 +1,287 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'plugin'
4
+ require_relative 'const'
5
+ require_relative 'dsl'
6
+ require_relative 'events'
7
+
1
8
  module Puma
9
+ # A class used for storing "leveled" configuration options.
10
+ #
11
+ # In this class any "user" specified options take precedence over any
12
+ # "file" specified options, take precedence over any "default" options.
13
+ #
14
+ # User input is preferred over "defaults":
15
+ # user_options = { foo: "bar" }
16
+ # default_options = { foo: "zoo" }
17
+ # options = UserFileDefaultOptions.new(user_options, default_options)
18
+ # puts options[:foo]
19
+ # # => "bar"
20
+ #
21
+ # All values can be accessed via `all_of`
22
+ #
23
+ # puts options.all_of(:foo)
24
+ # # => ["bar", "zoo"]
25
+ #
26
+ # A "file" option can be set. This config will be preferred over "default" options
27
+ # but will defer to any available "user" specified options.
28
+ #
29
+ # user_options = { foo: "bar" }
30
+ # default_options = { rackup: "zoo.rb" }
31
+ # options = UserFileDefaultOptions.new(user_options, default_options)
32
+ # options.file_options[:rackup] = "sup.rb"
33
+ # puts options[:rackup]
34
+ # # => "sup.rb"
35
+ #
36
+ # The "default" options can be set via procs. These are resolved during runtime
37
+ # via calls to `finalize_values`
38
+ class UserFileDefaultOptions
39
+ def initialize(user_options, default_options)
40
+ @user_options = user_options
41
+ @file_options = {}
42
+ @default_options = default_options
43
+ end
44
+
45
+ attr_reader :user_options, :file_options, :default_options
46
+
47
+ def [](key)
48
+ fetch(key)
49
+ end
50
+
51
+ def []=(key, value)
52
+ user_options[key] = value
53
+ end
54
+
55
+ def fetch(key, default_value = nil)
56
+ return user_options[key] if user_options.key?(key)
57
+ return file_options[key] if file_options.key?(key)
58
+ return default_options[key] if default_options.key?(key)
59
+
60
+ default_value
61
+ end
62
+
63
+ def all_of(key)
64
+ user = user_options[key]
65
+ file = file_options[key]
66
+ default = default_options[key]
2
67
 
3
- # The CLI exports it's Configuration object here to allow
4
- # apps to pick it up. An app needs to use it conditionally though
5
- # since it is not set if the app is launched via another
6
- # mechanism than the CLI class.
68
+ user = [user] unless user.is_a?(Array)
69
+ file = [file] unless file.is_a?(Array)
70
+ default = [default] unless default.is_a?(Array)
7
71
 
8
- class << self
9
- attr_accessor :cli_config
72
+ user.compact!
73
+ file.compact!
74
+ default.compact!
75
+
76
+ user + file + default
77
+ end
78
+
79
+ def finalize_values
80
+ @default_options.each do |k,v|
81
+ if v.respond_to? :call
82
+ @default_options[k] = v.call
83
+ end
84
+ end
85
+ end
86
+
87
+ def final_options
88
+ default_options
89
+ .merge(file_options)
90
+ .merge(user_options)
91
+ end
10
92
  end
11
93
 
94
+ # The main configuration class of Puma.
95
+ #
96
+ # It can be initialized with a set of "user" options and "default" options.
97
+ # Defaults will be merged with `Configuration.puma_default_options`.
98
+ #
99
+ # This class works together with 2 main other classes the `UserFileDefaultOptions`
100
+ # which stores configuration options in order so the precedence is that user
101
+ # set configuration wins over "file" based configuration wins over "default"
102
+ # configuration. These configurations are set via the `DSL` class. This
103
+ # class powers the Puma config file syntax and does double duty as a configuration
104
+ # DSL used by the `Puma::CLI` and Puma rack handler.
105
+ #
106
+ # It also handles loading plugins.
107
+ #
108
+ # [Note:]
109
+ # `:port` and `:host` are not valid keys. By the time they make it to the
110
+ # configuration options they are expected to be incorporated into a `:binds` key.
111
+ # Under the hood the DSL maps `port` and `host` calls to `:binds`
112
+ #
113
+ # config = Configuration.new({}) do |user_config, file_config, default_config|
114
+ # user_config.port 3003
115
+ # end
116
+ # config.clamp
117
+ # puts config.options[:port]
118
+ # # => 3003
119
+ #
120
+ # It is expected that `load` is called on the configuration instance after setting
121
+ # config. This method expands any values in `config_file` and puts them into the
122
+ # correct configuration option hash.
123
+ #
124
+ # Once all configuration is complete it is expected that `clamp` will be called
125
+ # on the instance. This will expand any procs stored under "default" values. This
126
+ # is done because an environment variable may have been modified while loading
127
+ # configuration files.
12
128
  class Configuration
13
- DefaultRackup = "config.ru"
129
+ class NotLoadedError < StandardError; end
130
+ class NotClampedError < StandardError; end
131
+
132
+ DEFAULTS = {
133
+ auto_trim_time: 30,
134
+ binds: ['tcp://0.0.0.0:9292'.freeze],
135
+ fiber_per_request: !!ENV.fetch("PUMA_FIBER_PER_REQUEST", false),
136
+ debug: false,
137
+ enable_keep_alives: true,
138
+ early_hints: nil,
139
+ environment: 'development'.freeze,
140
+ # Number of seconds to wait until we get the first data for the request.
141
+ first_data_timeout: 30,
142
+ # Number of seconds to wait until the next request before shutting down.
143
+ idle_timeout: nil,
144
+ io_selector_backend: :auto,
145
+ log_requests: false,
146
+ logger: STDOUT,
147
+ # Limits how many requests a keep alive connection can make.
148
+ # The connection will be closed after it reaches `max_keep_alive`
149
+ # requests.
150
+ max_keep_alive: 999,
151
+ max_threads: Puma.mri? ? 5 : 16,
152
+ min_threads: 0,
153
+ mode: :http,
154
+ mutate_stdout_and_stderr_to_sync_on_write: true,
155
+ out_of_band: [],
156
+ # Number of seconds for another request within a persistent session.
157
+ persistent_timeout: 65, # PUMA_PERSISTENT_TIMEOUT
158
+ prune_bundler: false,
159
+ queue_requests: true,
160
+ rackup: 'config.ru'.freeze,
161
+ raise_exception_on_sigterm: true,
162
+ reaping_time: 1,
163
+ remote_address: :socket,
164
+ silence_single_worker_warning: false,
165
+ silence_fork_callback_warning: false,
166
+ tag: File.basename(Dir.getwd),
167
+ tcp_host: '0.0.0.0'.freeze,
168
+ tcp_port: 9292,
169
+ wait_for_less_busy_worker: 0.005,
170
+ worker_boot_timeout: 60,
171
+ worker_check_interval: 5,
172
+ worker_culling_strategy: :youngest,
173
+ worker_shutdown_timeout: 30,
174
+ worker_timeout: 60,
175
+ workers: 0,
176
+ http_content_length_limit: nil
177
+ }
178
+
179
+ def initialize(user_options={}, default_options = {}, env = ENV, &block)
180
+ default_options = self.puma_default_options(env).merge(default_options)
181
+
182
+ @_options = UserFileDefaultOptions.new(user_options, default_options)
183
+ @plugins = PluginLoader.new
184
+ @events = @_options[:events] || Events.new
185
+ @hooks = {}
186
+ @user_dsl = DSL.new(@_options.user_options, self)
187
+ @file_dsl = DSL.new(@_options.file_options, self)
188
+ @default_dsl = DSL.new(@_options.default_options, self)
189
+
190
+ @puma_bundler_pruned = env.key? 'PUMA_BUNDLER_PRUNED'
191
+
192
+ if block
193
+ configure(&block)
194
+ end
195
+
196
+ @loaded = false
197
+ @clamped = false
198
+ end
199
+
200
+ attr_reader :plugins, :events, :hooks, :_options
14
201
 
15
- DefaultTCPHost = "0.0.0.0"
16
- DefaultTCPPort = 9292
202
+ def options
203
+ raise NotClampedError, "ensure clamp is called before accessing options" unless @clamped
17
204
 
18
- def initialize(options)
19
- @options = options
20
- @options[:binds] ||= []
21
- @options[:on_restart] ||= []
205
+ @_options
22
206
  end
23
207
 
24
- attr_reader :options
208
+ def configure
209
+ yield @user_dsl, @file_dsl, @default_dsl
210
+ ensure
211
+ @user_dsl._offer_plugins
212
+ @file_dsl._offer_plugins
213
+ @default_dsl._offer_plugins
214
+ end
25
215
 
26
216
  def initialize_copy(other)
27
- @options = @options.dup
217
+ @conf = nil
218
+ @cli_options = nil
219
+ @_options = @_options.dup
220
+ end
221
+
222
+ def flatten
223
+ dup.flatten!
224
+ end
225
+
226
+ def flatten!
227
+ @_options = @_options.flatten
228
+ self
229
+ end
230
+
231
+ def puma_default_options(env = ENV)
232
+ defaults = DEFAULTS.dup
233
+ puma_options_from_env(env).each { |k,v| defaults[k] = v if v }
234
+ defaults
235
+ end
236
+
237
+ def puma_options_from_env(env = ENV)
238
+ min = env['PUMA_MIN_THREADS'] || env['MIN_THREADS']
239
+ max = env['PUMA_MAX_THREADS'] || env['MAX_THREADS']
240
+ persistent_timeout = env['PUMA_PERSISTENT_TIMEOUT']
241
+ workers_env = env['WEB_CONCURRENCY']
242
+ workers = workers_env && workers_env.strip != "" ? parse_workers(workers_env.strip) : nil
243
+
244
+ {
245
+ min_threads: min && min != "" && Integer(min),
246
+ max_threads: max && max != "" && Integer(max),
247
+ persistent_timeout: persistent_timeout && persistent_timeout != "" && Integer(persistent_timeout),
248
+ workers: workers,
249
+ environment: env['APP_ENV'] || env['RACK_ENV'] || env['RAILS_ENV'],
250
+ }
28
251
  end
29
252
 
30
253
  def load
31
- if path = @options[:config_file]
32
- DSL.new(@options)._load_from path
33
- end
254
+ @loaded = true
255
+ config_files.each { |config_file| @file_dsl._load_from(config_file) }
256
+ @_options
257
+ end
34
258
 
35
- # Rakeup default option support
36
- if host = @options[:Host]
37
- port = @options[:Port] || DefaultTCPPort
259
+ def config_files
260
+ raise NotLoadedError, "ensure load is called before accessing config_files" unless @loaded
38
261
 
39
- @options[:binds] << "tcp://#{host}:#{port}"
40
- end
262
+ files = @_options.all_of(:config_files)
41
263
 
42
- if @options[:binds].empty?
43
- @options[:binds] << "tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"
44
- end
264
+ return [] if files == ['-']
265
+ return files if files.any?
45
266
 
46
- if @options[:control_url] == "auto"
47
- path = Configuration.temp_path
48
- @options[:control_url] = "unix://#{path}"
49
- @options[:control_url_temp] = path
267
+ first_default_file = %W(config/puma/#{@_options[:environment]}.rb config/puma.rb).find do |f|
268
+ File.exist?(f)
50
269
  end
51
270
 
52
- unless @options[:control_auth_token]
53
- setup_random_token
54
- end
271
+ [first_default_file]
272
+ end
273
+
274
+ # Call once all configuration (included from rackup files)
275
+ # is loaded to finalize defaults and lock in the configuration.
276
+ #
277
+ # This also calls load if it hasn't been called yet.
278
+ def clamp
279
+ load unless @loaded
280
+ set_conditional_default_options
281
+ @_options.finalize_values
282
+ @clamped = true
283
+ warn_hooks
284
+ options
55
285
  end
56
286
 
57
287
  # Injects the Configuration object into the env
@@ -70,68 +300,61 @@ module Puma
70
300
  # Indicate if there is a properly configured app
71
301
  #
72
302
  def app_configured?
73
- @options[:app] || File.exists?(rackup)
303
+ options[:app] || File.exist?(rackup)
74
304
  end
75
305
 
76
306
  def rackup
77
- @options[:rackup] || DefaultRackup
307
+ options[:rackup]
78
308
  end
79
309
 
80
- # Load the specified rackup file, pull an options from
310
+ # Load the specified rackup file, pull options from
81
311
  # the rackup file, and set @app.
82
312
  #
83
313
  def app
84
- app = @options[:app]
85
-
86
- unless app
87
- unless File.exists?(rackup)
88
- raise "Missing rackup file '#{rackup}'"
89
- end
90
-
91
- app, options = Rack::Builder.parse_file rackup
92
- @options.merge! options
93
-
94
- options.each do |key,val|
95
- if key.to_s[0,4] == "bind"
96
- @options[:binds] << val
97
- end
98
- end
99
- end
314
+ found = options[:app] || load_rackup
100
315
 
101
- if !@options[:quiet] and @options[:environment] == "development"
102
- logger = @options[:logger] || STDOUT
103
- app = Rack::CommonLogger.new(app, logger)
316
+ if options[:log_requests]
317
+ require_relative 'commonlogger'
318
+ logger = options[:custom_logger] ? options[:custom_logger] : options[:logger]
319
+ found = CommonLogger.new(found, logger)
104
320
  end
105
321
 
106
- return ConfigMiddleware.new(self, app)
322
+ ConfigMiddleware.new(self, found)
107
323
  end
108
324
 
109
- def setup_random_token
110
- begin
111
- require 'openssl'
112
- rescue LoadError
113
- end
114
-
115
- count = 16
325
+ # Return which environment we're running in
326
+ def environment
327
+ options[:environment]
328
+ end
116
329
 
117
- bytes = nil
330
+ def load_plugin(name)
331
+ @plugins.create name
332
+ end
118
333
 
119
- if defined? OpenSSL::Random
120
- bytes = OpenSSL::Random.random_bytes(count)
121
- elsif File.exists?("/dev/urandom")
122
- File.open("/dev/urandom") do |f|
123
- bytes = f.read(count)
334
+ # @param key [:Symbol] hook to run
335
+ # @param arg [Launcher, Int] `:before_restart` passes Launcher
336
+ #
337
+ def run_hooks(key, arg, log_writer, hook_data = nil)
338
+ log_writer.debug "Running #{key} hooks"
339
+
340
+ options.all_of(key).each do |hook_options|
341
+ begin
342
+ block = hook_options[:block]
343
+ if id = hook_options[:id]
344
+ hook_data[id] ||= Hash.new
345
+ block.call arg, hook_data[id]
346
+ else
347
+ block.call arg
348
+ end
349
+ rescue => e
350
+ log_writer.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
351
+ log_writer.debug e.backtrace.join("\n")
124
352
  end
125
353
  end
354
+ end
126
355
 
127
- if bytes
128
- token = ""
129
- bytes.each_byte { |b| token << b.to_s(16) }
130
- else
131
- token = (0..count).to_a.map { rand(255).to_s(16) }.join
132
- end
133
-
134
- @options[:control_auth_token] = token
356
+ def final_options
357
+ options.final_options
135
358
  end
136
359
 
137
360
  def self.temp_path
@@ -141,151 +364,96 @@ module Puma
141
364
  "#{Dir.tmpdir}/puma-status-#{t}-#{$$}"
142
365
  end
143
366
 
144
- # The methods that are available for use inside the config file.
145
- #
146
- class DSL
147
- def initialize(options)
148
- @options = options
149
- end
367
+ def self.random_token
368
+ require 'securerandom' unless defined?(SecureRandom)
150
369
 
151
- def _load_from(path)
152
- instance_eval File.read(path), path, 1
153
- end
370
+ SecureRandom.hex(16)
371
+ end
154
372
 
155
- # Use +obj+ or +block+ as the Rack app. This allows a config file to
156
- # be the app itself.
157
- #
158
- def app(obj=nil, &block)
159
- obj ||= block
373
+ private
160
374
 
161
- raise "Provide either a #call'able or a block" unless obj
375
+ def require_processor_counter
376
+ require 'concurrent/utility/processor_counter'
377
+ rescue LoadError
378
+ warn <<~MESSAGE
379
+ WEB_CONCURRENCY=auto or workers(:auto) requires the "concurrent-ruby" gem to be installed.
380
+ Please add "concurrent-ruby" to your Gemfile.
381
+ MESSAGE
382
+ raise
383
+ end
162
384
 
163
- @options[:app] = obj
385
+ def parse_workers(value)
386
+ if value == :auto || value == 'auto'
387
+ require_processor_counter
388
+ Integer(::Concurrent.available_processor_count)
389
+ else
390
+ Integer(value)
164
391
  end
392
+ rescue ArgumentError, TypeError
393
+ raise ArgumentError, "workers must be an Integer or :auto"
394
+ end
165
395
 
166
- # Start the Puma control rack app on +url+. This app can be communicated
167
- # with to control the main server.
168
- #
169
- def activate_control_app(url="auto", opts=nil)
170
- @options[:control_url] = url
171
-
172
- if opts
173
- if tok = opts[:auth_token]
174
- @options[:control_auth_token] = tok
175
- end
176
-
177
- if opts[:no_token]
178
- @options[:control_auth_token] = :none
179
- end
396
+ # Load and use the normal Rack builder if we can, otherwise
397
+ # fallback to our minimal version.
398
+ def rack_builder
399
+ # Load bundler now if we can so that we can pickup rack from
400
+ # a Gemfile
401
+ if @puma_bundler_pruned
402
+ begin
403
+ require 'bundler/setup'
404
+ rescue LoadError
180
405
  end
181
406
  end
182
407
 
183
- # Bind the server to +url+. tcp:// and unix:// are the only accepted
184
- # protocols.
185
- #
186
- def bind(url)
187
- @options[:binds] << url
188
- end
189
-
190
- # Daemonize the server into the background. Highly suggest that
191
- # this be combined with +pidfile+ and +stdout_redirect+.
192
- def daemonize(which=true)
193
- @options[:daemon] = which
408
+ begin
409
+ require 'rack'
410
+ require 'rack/builder'
411
+ ::Rack::Builder
412
+ rescue LoadError
413
+ require_relative 'rack/builder'
414
+ Puma::Rack::Builder
194
415
  end
416
+ end
195
417
 
196
- # Set the environment in which the Rack's app will run.
197
- def environment(environment)
198
- @options[:environment] = environment
199
- end
418
+ def load_rackup
419
+ raise "Missing rackup file '#{rackup}'" unless File.exist?(rackup)
200
420
 
201
- # Code to run before doing a restart. This code should
202
- # close logfiles, database connections, etc.
203
- #
204
- # This can be called multiple times to add code each time.
205
- #
206
- def on_restart(&blk)
207
- @options[:on_restart] << blk
208
- end
421
+ rack_app, rack_options = rack_builder.parse_file(rackup)
422
+ rack_options = rack_options || {}
209
423
 
210
- # Command to use to restart puma. This should be just how to
211
- # load puma itself (ie. 'ruby -Ilib bin/puma'), not the arguments
212
- # to puma, as those are the same as the original process.
213
- #
214
- def restart_command(cmd)
215
- @options[:request_cmd] = cmd
216
- end
424
+ options.file_options.merge!(rack_options)
217
425
 
218
- # Store the pid of the server in the file at +path+.
219
- def pidfile(path)
220
- @options[:pidfile] = path
426
+ config_ru_binds = []
427
+ rack_options.each do |k, v|
428
+ config_ru_binds << v if k.to_s.start_with?("bind")
221
429
  end
222
430
 
223
- # Disable request logging.
224
- #
225
- def quiet
226
- @options[:quiet] = true
227
- end
431
+ options.file_options[:binds] = config_ru_binds unless config_ru_binds.empty?
228
432
 
229
- # Load +path+ as a rackup file.
230
- #
231
- def rackup(path)
232
- @options[:rackup] = path.to_s
233
- end
433
+ rack_app
434
+ end
234
435
 
235
- # Redirect STDOUT and STDERR to files specified.
236
- def stdout_redirect(stdout=nil, stderr=nil, append=false)
237
- @options[:redirect_stdout] = stdout
238
- @options[:redirect_stderr] = stderr
239
- @options[:redirect_append] = append
240
- end
436
+ def set_conditional_default_options
437
+ @_options.default_options[:preload_app] = !@_options[:prune_bundler] &&
438
+ (@_options[:workers] > 1) && Puma.forkable?
439
+ end
241
440
 
242
- # Configure +min+ to be the minimum number of threads to use to answer
243
- # requests and +max+ the maximum.
244
- #
245
- def threads(min, max)
246
- if min > max
247
- raise "The minimum number of threads must be less than the max"
441
+ def warn_hooks
442
+ return if options[:workers] > 0
443
+ return if options[:silence_fork_callback_warning]
444
+
445
+ log_writer = LogWriter.stdio
446
+ @hooks.each_key do |hook|
447
+ options.all_of(hook).each do |hook_options|
448
+ next unless hook_options[:cluster_only]
449
+
450
+ log_writer.log(<<~MSG.tr("\n", " "))
451
+ Warning: The code in the `#{hook}` block will not execute
452
+ in the current Puma configuration. The `#{hook}` block only
453
+ executes in Puma's cluster mode. To fix this, either remove the
454
+ `#{hook}` call or increase Puma's worker count above zero.
455
+ MSG
248
456
  end
249
-
250
- @options[:min_threads] = min
251
- @options[:max_threads] = max
252
- end
253
-
254
- def ssl_bind(host, port, opts)
255
- o = [
256
- "cert=#{opts[:cert]}",
257
- "key=#{opts[:key]}"
258
- ]
259
-
260
- @options[:binds] << "ssl://#{host}:#{port}?#{o.join('&')}"
261
- end
262
-
263
- # Use +path+ as the file to store the server info state. This is
264
- # used by pumactl to query and control the server.
265
- #
266
- def state_path(path)
267
- @options[:state] = path.to_s
268
- end
269
-
270
- # *Cluster mode only* How many worker processes to run.
271
- #
272
- def workers(count)
273
- @options[:workers] = count.to_i
274
- end
275
-
276
- # *Cluster mode only* Code to run when a worker boots to setup
277
- # the process before booting the app.
278
- #
279
- # This can be called multiple times to add hooks.
280
- #
281
- def on_worker_boot(&block)
282
- @options[:worker_boot] << block
283
- end
284
-
285
- # The directory to operate out of.
286
- def directory(dir)
287
- @options[:directory] = dir.to_s
288
- @options[:worker_directory] = dir.to_s
289
457
  end
290
458
  end
291
459
  end