gitlab-puma 4.3.1.gitlab.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +1537 -0
  3. data/LICENSE +26 -0
  4. data/README.md +291 -0
  5. data/bin/puma +10 -0
  6. data/bin/puma-wild +31 -0
  7. data/bin/pumactl +12 -0
  8. data/docs/architecture.md +37 -0
  9. data/docs/deployment.md +111 -0
  10. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  11. data/docs/images/puma-connection-flow.png +0 -0
  12. data/docs/images/puma-general-arch.png +0 -0
  13. data/docs/nginx.md +80 -0
  14. data/docs/plugins.md +38 -0
  15. data/docs/restart.md +41 -0
  16. data/docs/signals.md +96 -0
  17. data/docs/systemd.md +290 -0
  18. data/docs/tcp_mode.md +96 -0
  19. data/ext/puma_http11/PumaHttp11Service.java +19 -0
  20. data/ext/puma_http11/ext_help.h +15 -0
  21. data/ext/puma_http11/extconf.rb +28 -0
  22. data/ext/puma_http11/http11_parser.c +1044 -0
  23. data/ext/puma_http11/http11_parser.h +65 -0
  24. data/ext/puma_http11/http11_parser.java.rl +145 -0
  25. data/ext/puma_http11/http11_parser.rl +147 -0
  26. data/ext/puma_http11/http11_parser_common.rl +54 -0
  27. data/ext/puma_http11/io_buffer.c +155 -0
  28. data/ext/puma_http11/mini_ssl.c +553 -0
  29. data/ext/puma_http11/org/jruby/puma/Http11.java +226 -0
  30. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +455 -0
  31. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +72 -0
  32. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +363 -0
  33. data/ext/puma_http11/puma_http11.c +502 -0
  34. data/lib/puma.rb +31 -0
  35. data/lib/puma/accept_nonblock.rb +29 -0
  36. data/lib/puma/app/status.rb +80 -0
  37. data/lib/puma/binder.rb +385 -0
  38. data/lib/puma/cli.rb +239 -0
  39. data/lib/puma/client.rb +494 -0
  40. data/lib/puma/cluster.rb +554 -0
  41. data/lib/puma/commonlogger.rb +108 -0
  42. data/lib/puma/configuration.rb +362 -0
  43. data/lib/puma/const.rb +242 -0
  44. data/lib/puma/control_cli.rb +289 -0
  45. data/lib/puma/detect.rb +15 -0
  46. data/lib/puma/dsl.rb +740 -0
  47. data/lib/puma/events.rb +156 -0
  48. data/lib/puma/io_buffer.rb +4 -0
  49. data/lib/puma/jruby_restart.rb +84 -0
  50. data/lib/puma/launcher.rb +475 -0
  51. data/lib/puma/minissl.rb +278 -0
  52. data/lib/puma/minissl/context_builder.rb +76 -0
  53. data/lib/puma/null_io.rb +44 -0
  54. data/lib/puma/plugin.rb +120 -0
  55. data/lib/puma/plugin/tmp_restart.rb +36 -0
  56. data/lib/puma/rack/builder.rb +301 -0
  57. data/lib/puma/rack/urlmap.rb +93 -0
  58. data/lib/puma/rack_default.rb +9 -0
  59. data/lib/puma/reactor.rb +400 -0
  60. data/lib/puma/runner.rb +192 -0
  61. data/lib/puma/server.rb +1053 -0
  62. data/lib/puma/single.rb +123 -0
  63. data/lib/puma/state_file.rb +31 -0
  64. data/lib/puma/tcp_logger.rb +41 -0
  65. data/lib/puma/thread_pool.rb +348 -0
  66. data/lib/puma/util.rb +124 -0
  67. data/lib/rack/handler/puma.rb +115 -0
  68. data/tools/docker/Dockerfile +16 -0
  69. data/tools/jungle/README.md +19 -0
  70. data/tools/jungle/init.d/README.md +61 -0
  71. data/tools/jungle/init.d/puma +421 -0
  72. data/tools/jungle/init.d/run-puma +18 -0
  73. data/tools/jungle/rc.d/README.md +74 -0
  74. data/tools/jungle/rc.d/puma +61 -0
  75. data/tools/jungle/rc.d/puma.conf +10 -0
  76. data/tools/jungle/upstart/README.md +61 -0
  77. data/tools/jungle/upstart/puma-manager.conf +31 -0
  78. data/tools/jungle/upstart/puma.conf +69 -0
  79. data/tools/trickletest.rb +44 -0
  80. metadata +147 -0
@@ -0,0 +1,108 @@
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}[http://httpd.apache.org/docs/1.3/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: http://httpd.apache.org/docs/1.3/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
+ CONTENT_LENGTH = 'Content-Length'.freeze
29
+ PATH_INFO = 'PATH_INFO'.freeze
30
+ QUERY_STRING = 'QUERY_STRING'.freeze
31
+ REQUEST_METHOD = 'REQUEST_METHOD'.freeze
32
+
33
+ def initialize(app, logger=nil)
34
+ @app = app
35
+ @logger = logger
36
+ end
37
+
38
+ def call(env)
39
+ began_at = Time.now
40
+ status, header, body = @app.call(env)
41
+ header = Util::HeaderHash.new(header)
42
+
43
+ # If we've been hijacked, then output a special line
44
+ if env['rack.hijack_io']
45
+ log_hijacking(env, 'HIJACK', header, began_at)
46
+ else
47
+ ary = env['rack.after_reply']
48
+ ary << lambda { log(env, status, header, began_at) }
49
+ end
50
+
51
+ [status, header, body]
52
+ end
53
+
54
+ private
55
+
56
+ def log_hijacking(env, status, header, began_at)
57
+ now = Time.now
58
+
59
+ msg = HIJACK_FORMAT % [
60
+ env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
61
+ env["REMOTE_USER"] || "-",
62
+ now.strftime("%d/%b/%Y %H:%M:%S"),
63
+ env[REQUEST_METHOD],
64
+ env[PATH_INFO],
65
+ env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
66
+ env["HTTP_VERSION"],
67
+ now - began_at ]
68
+
69
+ write(msg)
70
+ end
71
+
72
+ def log(env, status, header, began_at)
73
+ now = Time.now
74
+ length = extract_content_length(header)
75
+
76
+ msg = FORMAT % [
77
+ env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
78
+ env["REMOTE_USER"] || "-",
79
+ now.strftime("%d/%b/%Y:%H:%M:%S %z"),
80
+ env[REQUEST_METHOD],
81
+ env[PATH_INFO],
82
+ env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
83
+ env["HTTP_VERSION"],
84
+ status.to_s[0..3],
85
+ length,
86
+ now - began_at ]
87
+
88
+ write(msg)
89
+ end
90
+
91
+ def write(msg)
92
+ logger = @logger || env['rack.errors']
93
+
94
+ # Standard library logger doesn't support write but it supports << which actually
95
+ # calls to write on the log device without formatting
96
+ if logger.respond_to?(:write)
97
+ logger.write(msg)
98
+ else
99
+ logger << msg
100
+ end
101
+ end
102
+
103
+ def extract_content_length(headers)
104
+ value = headers[CONTENT_LENGTH] or return '-'
105
+ value.to_s == '0' ? '-' : value
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,362 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'puma/rack/builder'
4
+ require 'puma/plugin'
5
+ require 'puma/const'
6
+
7
+ module Puma
8
+
9
+ module ConfigDefault
10
+ DefaultRackup = "config.ru"
11
+
12
+ DefaultTCPHost = "0.0.0.0"
13
+ DefaultTCPPort = 9292
14
+ DefaultWorkerTimeout = 60
15
+ DefaultWorkerShutdownTimeout = 30
16
+ end
17
+
18
+ # A class used for storing "leveled" configuration options.
19
+ #
20
+ # In this class any "user" specified options take precedence over any
21
+ # "file" specified options, take precedence over any "default" options.
22
+ #
23
+ # User input is preferred over "defaults":
24
+ # user_options = { foo: "bar" }
25
+ # default_options = { foo: "zoo" }
26
+ # options = UserFileDefaultOptions.new(user_options, default_options)
27
+ # puts options[:foo]
28
+ # # => "bar"
29
+ #
30
+ # All values can be accessed via `all_of`
31
+ #
32
+ # puts options.all_of(:foo)
33
+ # # => ["bar", "zoo"]
34
+ #
35
+ # A "file" option can be set. This config will be preferred over "default" options
36
+ # but will defer to any available "user" specified options.
37
+ #
38
+ # user_options = { foo: "bar" }
39
+ # default_options = { rackup: "zoo.rb" }
40
+ # options = UserFileDefaultOptions.new(user_options, default_options)
41
+ # options.file_options[:rackup] = "sup.rb"
42
+ # puts options[:rackup]
43
+ # # => "sup.rb"
44
+ #
45
+ # The "default" options can be set via procs. These are resolved during runtime
46
+ # via calls to `finalize_values`
47
+ class UserFileDefaultOptions
48
+ def initialize(user_options, default_options)
49
+ @user_options = user_options
50
+ @file_options = {}
51
+ @default_options = default_options
52
+ end
53
+
54
+ attr_reader :user_options, :file_options, :default_options
55
+
56
+ def [](key)
57
+ return user_options[key] if user_options.key?(key)
58
+ return file_options[key] if file_options.key?(key)
59
+ return default_options[key] if default_options.key?(key)
60
+ end
61
+
62
+ def []=(key, value)
63
+ user_options[key] = value
64
+ end
65
+
66
+ def fetch(key, default_value = nil)
67
+ self[key] || default_value
68
+ end
69
+
70
+ def all_of(key)
71
+ user = user_options[key]
72
+ file = file_options[key]
73
+ default = default_options[key]
74
+
75
+ user = [user] unless user.is_a?(Array)
76
+ file = [file] unless file.is_a?(Array)
77
+ default = [default] unless default.is_a?(Array)
78
+
79
+ user.compact!
80
+ file.compact!
81
+ default.compact!
82
+
83
+ user + file + default
84
+ end
85
+
86
+ def finalize_values
87
+ @default_options.each do |k,v|
88
+ if v.respond_to? :call
89
+ @default_options[k] = v.call
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ # The main configuration class of Puma.
96
+ #
97
+ # It can be initialized with a set of "user" options and "default" options.
98
+ # Defaults will be merged with `Configuration.puma_default_options`.
99
+ #
100
+ # This class works together with 2 main other classes the `UserFileDefaultOptions`
101
+ # which stores configuration options in order so the precedence is that user
102
+ # set configuration wins over "file" based configuration wins over "default"
103
+ # configuration. These configurations are set via the `DSL` class. This
104
+ # class powers the Puma config file syntax and does double duty as a configuration
105
+ # DSL used by the `Puma::CLI` and Puma rack handler.
106
+ #
107
+ # It also handles loading plugins.
108
+ #
109
+ # > Note: `:port` and `:host` are not valid keys. By they 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.load
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.
128
+ class Configuration
129
+ include ConfigDefault
130
+
131
+ def initialize(user_options={}, default_options = {}, &block)
132
+ default_options = self.puma_default_options.merge(default_options)
133
+
134
+ @options = UserFileDefaultOptions.new(user_options, default_options)
135
+ @plugins = PluginLoader.new
136
+ @user_dsl = DSL.new(@options.user_options, self)
137
+ @file_dsl = DSL.new(@options.file_options, self)
138
+ @default_dsl = DSL.new(@options.default_options, self)
139
+
140
+ if block
141
+ configure(&block)
142
+ end
143
+ end
144
+
145
+ attr_reader :options, :plugins
146
+
147
+ def configure
148
+ yield @user_dsl, @file_dsl, @default_dsl
149
+ ensure
150
+ @user_dsl._offer_plugins
151
+ @file_dsl._offer_plugins
152
+ @default_dsl._offer_plugins
153
+ end
154
+
155
+ def initialize_copy(other)
156
+ @conf = nil
157
+ @cli_options = nil
158
+ @options = @options.dup
159
+ end
160
+
161
+ def flatten
162
+ dup.flatten!
163
+ end
164
+
165
+ def flatten!
166
+ @options = @options.flatten
167
+ self
168
+ end
169
+
170
+ def puma_default_options
171
+ {
172
+ :min_threads => 0,
173
+ :max_threads => 16,
174
+ :log_requests => false,
175
+ :debug => false,
176
+ :binds => ["tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"],
177
+ :workers => 0,
178
+ :daemon => false,
179
+ :mode => :http,
180
+ :worker_timeout => DefaultWorkerTimeout,
181
+ :worker_boot_timeout => DefaultWorkerTimeout,
182
+ :worker_shutdown_timeout => DefaultWorkerShutdownTimeout,
183
+ :remote_address => :socket,
184
+ :tag => method(:infer_tag),
185
+ :environment => -> { ENV['RACK_ENV'] || "development" },
186
+ :rackup => DefaultRackup,
187
+ :logger => STDOUT,
188
+ :persistent_timeout => Const::PERSISTENT_TIMEOUT,
189
+ :first_data_timeout => Const::FIRST_DATA_TIMEOUT,
190
+ :raise_exception_on_sigterm => true
191
+ }
192
+ end
193
+
194
+ def load
195
+ config_files.each { |config_file| @file_dsl._load_from(config_file) }
196
+
197
+ @options
198
+ end
199
+
200
+ def config_files
201
+ files = @options.all_of(:config_files)
202
+
203
+ return [] if files == ['-']
204
+ return files if files.any?
205
+
206
+ first_default_file = %W(config/puma/#{environment_str}.rb config/puma.rb).find do |f|
207
+ File.exist?(f)
208
+ end
209
+
210
+ [first_default_file]
211
+ end
212
+
213
+ # Call once all configuration (included from rackup files)
214
+ # is loaded to flesh out any defaults
215
+ def clamp
216
+ @options.finalize_values
217
+ end
218
+
219
+ # Injects the Configuration object into the env
220
+ class ConfigMiddleware
221
+ def initialize(config, app)
222
+ @config = config
223
+ @app = app
224
+ end
225
+
226
+ def call(env)
227
+ env[Const::PUMA_CONFIG] = @config
228
+ @app.call(env)
229
+ end
230
+ end
231
+
232
+ # Indicate if there is a properly configured app
233
+ #
234
+ def app_configured?
235
+ @options[:app] || File.exist?(rackup)
236
+ end
237
+
238
+ def rackup
239
+ @options[:rackup]
240
+ end
241
+
242
+ # Load the specified rackup file, pull options from
243
+ # the rackup file, and set @app.
244
+ #
245
+ def app
246
+ found = options[:app] || load_rackup
247
+
248
+ if @options[:mode] == :tcp
249
+ require 'puma/tcp_logger'
250
+
251
+ logger = @options[:logger]
252
+ quiet = !@options[:log_requests]
253
+ return TCPLogger.new(logger, found, quiet)
254
+ end
255
+
256
+ if @options[:log_requests]
257
+ require 'puma/commonlogger'
258
+ logger = @options[:logger]
259
+ found = CommonLogger.new(found, logger)
260
+ end
261
+
262
+ ConfigMiddleware.new(self, found)
263
+ end
264
+
265
+ # Return which environment we're running in
266
+ def environment
267
+ @options[:environment]
268
+ end
269
+
270
+ def environment_str
271
+ environment.respond_to?(:call) ? environment.call : environment
272
+ end
273
+
274
+ def load_plugin(name)
275
+ @plugins.create name
276
+ end
277
+
278
+ def run_hooks(key, arg)
279
+ @options.all_of(key).each { |b| b.call arg }
280
+ end
281
+
282
+ def self.temp_path
283
+ require 'tmpdir'
284
+
285
+ t = (Time.now.to_f * 1000).to_i
286
+ "#{Dir.tmpdir}/puma-status-#{t}-#{$$}"
287
+ end
288
+
289
+ private
290
+
291
+ def infer_tag
292
+ File.basename(Dir.getwd)
293
+ end
294
+
295
+ # Load and use the normal Rack builder if we can, otherwise
296
+ # fallback to our minimal version.
297
+ def rack_builder
298
+ # Load bundler now if we can so that we can pickup rack from
299
+ # a Gemfile
300
+ if ENV.key? 'PUMA_BUNDLER_PRUNED'
301
+ begin
302
+ require 'bundler/setup'
303
+ rescue LoadError
304
+ end
305
+ end
306
+
307
+ begin
308
+ require 'rack'
309
+ require 'rack/builder'
310
+ rescue LoadError
311
+ # ok, use builtin version
312
+ return Puma::Rack::Builder
313
+ else
314
+ return ::Rack::Builder
315
+ end
316
+ end
317
+
318
+ def load_rackup
319
+ raise "Missing rackup file '#{rackup}'" unless File.exist?(rackup)
320
+
321
+ rack_app, rack_options = rack_builder.parse_file(rackup)
322
+ @options.file_options.merge!(rack_options)
323
+
324
+ config_ru_binds = []
325
+ rack_options.each do |k, v|
326
+ config_ru_binds << v if k.to_s.start_with?("bind")
327
+ end
328
+
329
+ @options.file_options[:binds] = config_ru_binds unless config_ru_binds.empty?
330
+
331
+ rack_app
332
+ end
333
+
334
+ def self.random_token
335
+ begin
336
+ require 'openssl'
337
+ rescue LoadError
338
+ end
339
+
340
+ count = 16
341
+
342
+ bytes = nil
343
+
344
+ if defined? OpenSSL::Random
345
+ bytes = OpenSSL::Random.random_bytes(count)
346
+ elsif File.exist?("/dev/urandom")
347
+ File.open('/dev/urandom') { |f| bytes = f.read(count) }
348
+ end
349
+
350
+ if bytes
351
+ token = "".dup
352
+ bytes.each_byte { |b| token << b.to_s(16) }
353
+ else
354
+ token = (0..count).to_a.map { rand(255).to_s(16) }.join
355
+ end
356
+
357
+ return token
358
+ end
359
+ end
360
+ end
361
+
362
+ require 'puma/dsl'