jun-puma 1.0.0-java

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 (87) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +2897 -0
  3. data/LICENSE +29 -0
  4. data/README.md +475 -0
  5. data/bin/puma +10 -0
  6. data/bin/puma-wild +25 -0
  7. data/bin/pumactl +12 -0
  8. data/docs/architecture.md +74 -0
  9. data/docs/compile_options.md +55 -0
  10. data/docs/deployment.md +102 -0
  11. data/docs/fork_worker.md +35 -0
  12. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  13. data/docs/images/puma-connection-flow.png +0 -0
  14. data/docs/images/puma-general-arch.png +0 -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 +78 -0
  20. data/docs/nginx.md +80 -0
  21. data/docs/plugins.md +38 -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 +142 -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 +17 -0
  30. data/ext/puma_http11/ext_help.h +15 -0
  31. data/ext/puma_http11/extconf.rb +80 -0
  32. data/ext/puma_http11/http11_parser.c +1057 -0
  33. data/ext/puma_http11/http11_parser.h +65 -0
  34. data/ext/puma_http11/http11_parser.java.rl +145 -0
  35. data/ext/puma_http11/http11_parser.rl +149 -0
  36. data/ext/puma_http11/http11_parser_common.rl +54 -0
  37. data/ext/puma_http11/mini_ssl.c +842 -0
  38. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  39. data/ext/puma_http11/org/jruby/puma/Http11.java +228 -0
  40. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +455 -0
  41. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +509 -0
  42. data/ext/puma_http11/puma_http11.c +495 -0
  43. data/lib/puma/app/status.rb +96 -0
  44. data/lib/puma/binder.rb +502 -0
  45. data/lib/puma/cli.rb +247 -0
  46. data/lib/puma/client.rb +682 -0
  47. data/lib/puma/cluster/worker.rb +180 -0
  48. data/lib/puma/cluster/worker_handle.rb +96 -0
  49. data/lib/puma/cluster.rb +616 -0
  50. data/lib/puma/commonlogger.rb +115 -0
  51. data/lib/puma/configuration.rb +390 -0
  52. data/lib/puma/const.rb +307 -0
  53. data/lib/puma/control_cli.rb +316 -0
  54. data/lib/puma/detect.rb +45 -0
  55. data/lib/puma/dsl.rb +1425 -0
  56. data/lib/puma/error_logger.rb +113 -0
  57. data/lib/puma/events.rb +57 -0
  58. data/lib/puma/io_buffer.rb +46 -0
  59. data/lib/puma/jruby_restart.rb +11 -0
  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 +488 -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 +459 -0
  66. data/lib/puma/null_io.rb +84 -0
  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/puma_http11.jar +0 -0
  71. data/lib/puma/rack/builder.rb +297 -0
  72. data/lib/puma/rack/urlmap.rb +93 -0
  73. data/lib/puma/rack_default.rb +24 -0
  74. data/lib/puma/reactor.rb +125 -0
  75. data/lib/puma/request.rb +688 -0
  76. data/lib/puma/runner.rb +213 -0
  77. data/lib/puma/sd_notify.rb +149 -0
  78. data/lib/puma/server.rb +680 -0
  79. data/lib/puma/single.rb +69 -0
  80. data/lib/puma/state_file.rb +68 -0
  81. data/lib/puma/thread_pool.rb +434 -0
  82. data/lib/puma/util.rb +141 -0
  83. data/lib/puma.rb +78 -0
  84. data/lib/rack/handler/puma.rb +144 -0
  85. data/tools/Dockerfile +16 -0
  86. data/tools/trickletest.rb +44 -0
  87. metadata +153 -0
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Puma
4
+ # Rack::CommonLogger forwards every request to the given +app+, and
5
+ # logs a line in the
6
+ # {Apache common log format}[https://httpd.apache.org/docs/2.4/logs.html#common]
7
+ # to the +logger+.
8
+ #
9
+ # If +logger+ is nil, CommonLogger will fall back +rack.errors+, which is
10
+ # an instance of Rack::NullLogger.
11
+ #
12
+ # +logger+ can be any class, including the standard library Logger, and is
13
+ # expected to have either +write+ or +<<+ method, which accepts the CommonLogger::FORMAT.
14
+ # According to the SPEC, the error stream must also respond to +puts+
15
+ # (which takes a single argument that responds to +to_s+), and +flush+
16
+ # (which is called without arguments in order to make the error appear for
17
+ # sure)
18
+ class CommonLogger
19
+ # Common Log Format: https://httpd.apache.org/docs/2.4/logs.html#common
20
+ #
21
+ # lilith.local - - [07/Aug/2006 23:58:02 -0400] "GET / HTTP/1.1" 500 -
22
+ #
23
+ # %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
24
+ FORMAT = %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n}
25
+
26
+ HIJACK_FORMAT = %{%s - %s [%s] "%s %s%s %s" HIJACKED -1 %0.4f\n}
27
+
28
+ LOG_TIME_FORMAT = '%d/%b/%Y:%H:%M:%S %z'
29
+
30
+ CONTENT_LENGTH = 'Content-Length' # should be lower case from app,
31
+ # Util::HeaderHash allows mixed
32
+ HTTP_VERSION = Const::HTTP_VERSION
33
+ HTTP_X_FORWARDED_FOR = Const::HTTP_X_FORWARDED_FOR
34
+ PATH_INFO = Const::PATH_INFO
35
+ QUERY_STRING = Const::QUERY_STRING
36
+ REMOTE_ADDR = Const::REMOTE_ADDR
37
+ REMOTE_USER = 'REMOTE_USER'
38
+ REQUEST_METHOD = Const::REQUEST_METHOD
39
+
40
+ def initialize(app, logger=nil)
41
+ @app = app
42
+ @logger = logger
43
+ end
44
+
45
+ def call(env)
46
+ began_at = Time.now
47
+ status, header, body = @app.call(env)
48
+ header = Util::HeaderHash.new(header)
49
+
50
+ # If we've been hijacked, then output a special line
51
+ if env['rack.hijack_io']
52
+ log_hijacking(env, 'HIJACK', header, began_at)
53
+ else
54
+ ary = env['rack.after_reply']
55
+ ary << lambda { log(env, status, header, began_at) }
56
+ end
57
+
58
+ [status, header, body]
59
+ end
60
+
61
+ private
62
+
63
+ def log_hijacking(env, status, header, began_at)
64
+ now = Time.now
65
+
66
+ msg = HIJACK_FORMAT % [
67
+ env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR] || "-",
68
+ env[REMOTE_USER] || "-",
69
+ now.strftime(LOG_TIME_FORMAT),
70
+ env[REQUEST_METHOD],
71
+ env[PATH_INFO],
72
+ env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
73
+ env[HTTP_VERSION],
74
+ now - began_at ]
75
+
76
+ write(msg)
77
+ end
78
+
79
+ def log(env, status, header, began_at)
80
+ now = Time.now
81
+ length = extract_content_length(header)
82
+
83
+ msg = FORMAT % [
84
+ env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR] || "-",
85
+ env[REMOTE_USER] || "-",
86
+ now.strftime(LOG_TIME_FORMAT),
87
+ env[REQUEST_METHOD],
88
+ env[PATH_INFO],
89
+ env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
90
+ env[HTTP_VERSION],
91
+ status.to_s[0..3],
92
+ length,
93
+ now - began_at ]
94
+
95
+ write(msg)
96
+ end
97
+
98
+ def write(msg)
99
+ logger = @logger || env['rack.errors']
100
+
101
+ # Standard library logger doesn't support write but it supports << which actually
102
+ # calls to write on the log device without formatting
103
+ if logger.respond_to?(:write)
104
+ logger.write(msg)
105
+ else
106
+ logger << msg
107
+ end
108
+ end
109
+
110
+ def extract_content_length(headers)
111
+ value = headers[CONTENT_LENGTH] or return '-'
112
+ value.to_s == '0' ? '-' : value
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,390 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'plugin'
4
+ require_relative 'const'
5
+ require_relative 'dsl'
6
+
7
+ module Puma
8
+ # A class used for storing "leveled" configuration options.
9
+ #
10
+ # In this class any "user" specified options take precedence over any
11
+ # "file" specified options, take precedence over any "default" options.
12
+ #
13
+ # User input is preferred over "defaults":
14
+ # user_options = { foo: "bar" }
15
+ # default_options = { foo: "zoo" }
16
+ # options = UserFileDefaultOptions.new(user_options, default_options)
17
+ # puts options[:foo]
18
+ # # => "bar"
19
+ #
20
+ # All values can be accessed via `all_of`
21
+ #
22
+ # puts options.all_of(:foo)
23
+ # # => ["bar", "zoo"]
24
+ #
25
+ # A "file" option can be set. This config will be preferred over "default" options
26
+ # but will defer to any available "user" specified options.
27
+ #
28
+ # user_options = { foo: "bar" }
29
+ # default_options = { rackup: "zoo.rb" }
30
+ # options = UserFileDefaultOptions.new(user_options, default_options)
31
+ # options.file_options[:rackup] = "sup.rb"
32
+ # puts options[:rackup]
33
+ # # => "sup.rb"
34
+ #
35
+ # The "default" options can be set via procs. These are resolved during runtime
36
+ # via calls to `finalize_values`
37
+ class UserFileDefaultOptions
38
+ def initialize(user_options, default_options)
39
+ @user_options = user_options
40
+ @file_options = {}
41
+ @default_options = default_options
42
+ end
43
+
44
+ attr_reader :user_options, :file_options, :default_options
45
+
46
+ def [](key)
47
+ fetch(key)
48
+ end
49
+
50
+ def []=(key, value)
51
+ user_options[key] = value
52
+ end
53
+
54
+ def fetch(key, default_value = nil)
55
+ return user_options[key] if user_options.key?(key)
56
+ return file_options[key] if file_options.key?(key)
57
+ return default_options[key] if default_options.key?(key)
58
+
59
+ default_value
60
+ end
61
+
62
+ def all_of(key)
63
+ user = user_options[key]
64
+ file = file_options[key]
65
+ default = default_options[key]
66
+
67
+ user = [user] unless user.is_a?(Array)
68
+ file = [file] unless file.is_a?(Array)
69
+ default = [default] unless default.is_a?(Array)
70
+
71
+ user.compact!
72
+ file.compact!
73
+ default.compact!
74
+
75
+ user + file + default
76
+ end
77
+
78
+ def finalize_values
79
+ @default_options.each do |k,v|
80
+ if v.respond_to? :call
81
+ @default_options[k] = v.call
82
+ end
83
+ end
84
+ end
85
+
86
+ def final_options
87
+ default_options
88
+ .merge(file_options)
89
+ .merge(user_options)
90
+ end
91
+ end
92
+
93
+ # The main configuration class of Puma.
94
+ #
95
+ # It can be initialized with a set of "user" options and "default" options.
96
+ # Defaults will be merged with `Configuration.puma_default_options`.
97
+ #
98
+ # This class works together with 2 main other classes the `UserFileDefaultOptions`
99
+ # which stores configuration options in order so the precedence is that user
100
+ # set configuration wins over "file" based configuration wins over "default"
101
+ # configuration. These configurations are set via the `DSL` class. This
102
+ # class powers the Puma config file syntax and does double duty as a configuration
103
+ # DSL used by the `Puma::CLI` and Puma rack handler.
104
+ #
105
+ # It also handles loading plugins.
106
+ #
107
+ # [Note:]
108
+ # `:port` and `:host` are not valid keys. By the time they make it to the
109
+ # configuration options they are expected to be incorporated into a `:binds` key.
110
+ # Under the hood the DSL maps `port` and `host` calls to `:binds`
111
+ #
112
+ # config = Configuration.new({}) do |user_config, file_config, default_config|
113
+ # user_config.port 3003
114
+ # end
115
+ # config.load
116
+ # puts config.options[:port]
117
+ # # => 3003
118
+ #
119
+ # It is expected that `load` is called on the configuration instance after setting
120
+ # config. This method expands any values in `config_file` and puts them into the
121
+ # correct configuration option hash.
122
+ #
123
+ # Once all configuration is complete it is expected that `clamp` will be called
124
+ # on the instance. This will expand any procs stored under "default" values. This
125
+ # is done because an environment variable may have been modified while loading
126
+ # configuration files.
127
+ class Configuration
128
+ DEFAULTS = {
129
+ auto_trim_time: 30,
130
+ binds: ['tcp://0.0.0.0:9292'.freeze],
131
+ clean_thread_locals: false,
132
+ debug: false,
133
+ enable_keep_alives: true,
134
+ early_hints: nil,
135
+ environment: 'development'.freeze,
136
+ # Number of seconds to wait until we get the first data for the request.
137
+ first_data_timeout: 30,
138
+ # Number of seconds to wait until the next request before shutting down.
139
+ idle_timeout: nil,
140
+ io_selector_backend: :auto,
141
+ log_requests: false,
142
+ logger: STDOUT,
143
+ # How many requests to attempt inline before sending a client back to
144
+ # the reactor to be subject to normal ordering. The idea here is that
145
+ # we amortize the cost of going back to the reactor for a well behaved
146
+ # but very "greedy" client across 10 requests. This prevents a not
147
+ # well behaved client from monopolizing the thread forever.
148
+ max_fast_inline: 10,
149
+ max_threads: Puma.mri? ? 5 : 16,
150
+ min_threads: 0,
151
+ mode: :http,
152
+ mutate_stdout_and_stderr_to_sync_on_write: true,
153
+ out_of_band: [],
154
+ # Number of seconds for another request within a persistent session.
155
+ persistent_timeout: 20,
156
+ queue_requests: true,
157
+ rackup: 'config.ru'.freeze,
158
+ raise_exception_on_sigterm: true,
159
+ reaping_time: 1,
160
+ remote_address: :socket,
161
+ silence_single_worker_warning: false,
162
+ silence_fork_callback_warning: false,
163
+ tag: File.basename(Dir.getwd),
164
+ tcp_host: '0.0.0.0'.freeze,
165
+ tcp_port: 9292,
166
+ wait_for_less_busy_worker: 0.005,
167
+ worker_boot_timeout: 60,
168
+ worker_check_interval: 5,
169
+ worker_culling_strategy: :youngest,
170
+ worker_shutdown_timeout: 30,
171
+ worker_timeout: 60,
172
+ workers: 0,
173
+ http_content_length_limit: nil
174
+ }
175
+
176
+ def initialize(user_options={}, default_options = {}, env = ENV, &block)
177
+ default_options = self.puma_default_options(env).merge(default_options)
178
+
179
+ @options = UserFileDefaultOptions.new(user_options, default_options)
180
+ @plugins = PluginLoader.new
181
+ @user_dsl = DSL.new(@options.user_options, self)
182
+ @file_dsl = DSL.new(@options.file_options, self)
183
+ @default_dsl = DSL.new(@options.default_options, self)
184
+
185
+ if !@options[:prune_bundler]
186
+ default_options[:preload_app] = (@options[:workers] > 1) && Puma.forkable?
187
+ end
188
+
189
+ @puma_bundler_pruned = env.key? 'PUMA_BUNDLER_PRUNED'
190
+
191
+ if block
192
+ configure(&block)
193
+ end
194
+ end
195
+
196
+ attr_reader :options, :plugins
197
+
198
+ def configure
199
+ yield @user_dsl, @file_dsl, @default_dsl
200
+ ensure
201
+ @user_dsl._offer_plugins
202
+ @file_dsl._offer_plugins
203
+ @default_dsl._offer_plugins
204
+ end
205
+
206
+ def initialize_copy(other)
207
+ @conf = nil
208
+ @cli_options = nil
209
+ @options = @options.dup
210
+ end
211
+
212
+ def flatten
213
+ dup.flatten!
214
+ end
215
+
216
+ def flatten!
217
+ @options = @options.flatten
218
+ self
219
+ end
220
+
221
+ def puma_default_options(env = ENV)
222
+ defaults = DEFAULTS.dup
223
+ puma_options_from_env(env).each { |k,v| defaults[k] = v if v }
224
+ defaults
225
+ end
226
+
227
+ def puma_options_from_env(env = ENV)
228
+ min = env['PUMA_MIN_THREADS'] || env['MIN_THREADS']
229
+ max = env['PUMA_MAX_THREADS'] || env['MAX_THREADS']
230
+ workers = env['WEB_CONCURRENCY']
231
+
232
+ {
233
+ min_threads: min && Integer(min),
234
+ max_threads: max && Integer(max),
235
+ workers: workers && Integer(workers),
236
+ environment: env['APP_ENV'] || env['RACK_ENV'] || env['RAILS_ENV'],
237
+ }
238
+ end
239
+
240
+ def load
241
+ config_files.each { |config_file| @file_dsl._load_from(config_file) }
242
+
243
+ @options
244
+ end
245
+
246
+ def config_files
247
+ files = @options.all_of(:config_files)
248
+
249
+ return [] if files == ['-']
250
+ return files if files.any?
251
+
252
+ first_default_file = %W(config/puma/#{@options[:environment]}.rb config/puma.rb).find do |f|
253
+ File.exist?(f)
254
+ end
255
+
256
+ [first_default_file]
257
+ end
258
+
259
+ # Call once all configuration (included from rackup files)
260
+ # is loaded to flesh out any defaults
261
+ def clamp
262
+ @options.finalize_values
263
+ end
264
+
265
+ # Injects the Configuration object into the env
266
+ class ConfigMiddleware
267
+ def initialize(config, app)
268
+ @config = config
269
+ @app = app
270
+ end
271
+
272
+ def call(env)
273
+ env[Const::PUMA_CONFIG] = @config
274
+ @app.call(env)
275
+ end
276
+ end
277
+
278
+ # Indicate if there is a properly configured app
279
+ #
280
+ def app_configured?
281
+ @options[:app] || File.exist?(rackup)
282
+ end
283
+
284
+ def rackup
285
+ @options[:rackup]
286
+ end
287
+
288
+ # Load the specified rackup file, pull options from
289
+ # the rackup file, and set @app.
290
+ #
291
+ def app
292
+ found = options[:app] || load_rackup
293
+
294
+ if @options[:log_requests]
295
+ require_relative 'commonlogger'
296
+ logger = @options[:logger]
297
+ found = CommonLogger.new(found, logger)
298
+ end
299
+
300
+ ConfigMiddleware.new(self, found)
301
+ end
302
+
303
+ # Return which environment we're running in
304
+ def environment
305
+ @options[:environment]
306
+ end
307
+
308
+ def load_plugin(name)
309
+ @plugins.create name
310
+ end
311
+
312
+ # @param key [:Symbol] hook to run
313
+ # @param arg [Launcher, Int] `:on_restart` passes Launcher
314
+ #
315
+ def run_hooks(key, arg, log_writer, hook_data = nil)
316
+ @options.all_of(key).each do |b|
317
+ begin
318
+ if Array === b
319
+ hook_data[b[1]] ||= Hash.new
320
+ b[0].call arg, hook_data[b[1]]
321
+ else
322
+ b.call arg
323
+ end
324
+ rescue => e
325
+ log_writer.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
326
+ log_writer.debug e.backtrace.join("\n")
327
+ end
328
+ end
329
+ end
330
+
331
+ def final_options
332
+ @options.final_options
333
+ end
334
+
335
+ def self.temp_path
336
+ require 'tmpdir'
337
+
338
+ t = (Time.now.to_f * 1000).to_i
339
+ "#{Dir.tmpdir}/puma-status-#{t}-#{$$}"
340
+ end
341
+
342
+ private
343
+
344
+ # Load and use the normal Rack builder if we can, otherwise
345
+ # fallback to our minimal version.
346
+ def rack_builder
347
+ # Load bundler now if we can so that we can pickup rack from
348
+ # a Gemfile
349
+ if @puma_bundler_pruned
350
+ begin
351
+ require 'bundler/setup'
352
+ rescue LoadError
353
+ end
354
+ end
355
+
356
+ begin
357
+ require 'rack'
358
+ require 'rack/builder'
359
+ ::Rack::Builder
360
+ rescue LoadError
361
+ require_relative 'rack/builder'
362
+ Puma::Rack::Builder
363
+ end
364
+ end
365
+
366
+ def load_rackup
367
+ raise "Missing rackup file '#{rackup}'" unless File.exist?(rackup)
368
+
369
+ rack_app, rack_options = rack_builder.parse_file(rackup)
370
+ rack_options = rack_options || {}
371
+
372
+ @options.file_options.merge!(rack_options)
373
+
374
+ config_ru_binds = []
375
+ rack_options.each do |k, v|
376
+ config_ru_binds << v if k.to_s.start_with?("bind")
377
+ end
378
+
379
+ @options.file_options[:binds] = config_ru_binds unless config_ru_binds.empty?
380
+
381
+ rack_app
382
+ end
383
+
384
+ def self.random_token
385
+ require 'securerandom' unless defined?(SecureRandom)
386
+
387
+ SecureRandom.hex(16)
388
+ end
389
+ end
390
+ end