puma 2.7.0 → 3.1.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puma might be problematic. Click here for more details.

Files changed (79) hide show
  1. checksums.yaml +5 -13
  2. data/DEPLOYMENT.md +91 -0
  3. data/Gemfile +3 -2
  4. data/History.txt +624 -1
  5. data/Manifest.txt +15 -3
  6. data/README.md +129 -14
  7. data/Rakefile +3 -3
  8. data/bin/puma-wild +31 -0
  9. data/bin/pumactl +1 -1
  10. data/docs/nginx.md +1 -1
  11. data/docs/signals.md +43 -0
  12. data/ext/puma_http11/extconf.rb +7 -2
  13. data/ext/puma_http11/http11_parser.java.rl +5 -5
  14. data/ext/puma_http11/io_buffer.c +1 -1
  15. data/ext/puma_http11/mini_ssl.c +233 -18
  16. data/ext/puma_http11/org/jruby/puma/Http11.java +12 -3
  17. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +39 -39
  18. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +245 -195
  19. data/ext/puma_http11/puma_http11.c +12 -4
  20. data/lib/puma.rb +1 -0
  21. data/lib/puma/app/status.rb +7 -0
  22. data/lib/puma/binder.rb +108 -39
  23. data/lib/puma/capistrano.rb +23 -6
  24. data/lib/puma/cli.rb +141 -446
  25. data/lib/puma/client.rb +48 -1
  26. data/lib/puma/cluster.rb +207 -58
  27. data/lib/puma/commonlogger.rb +107 -0
  28. data/lib/puma/configuration.rb +262 -235
  29. data/lib/puma/const.rb +97 -14
  30. data/lib/puma/control_cli.rb +85 -77
  31. data/lib/puma/convenient.rb +23 -0
  32. data/lib/puma/daemon_ext.rb +11 -4
  33. data/lib/puma/detect.rb +8 -1
  34. data/lib/puma/dsl.rb +456 -0
  35. data/lib/puma/events.rb +35 -18
  36. data/lib/puma/jruby_restart.rb +1 -1
  37. data/lib/puma/launcher.rb +399 -0
  38. data/lib/puma/minissl.rb +49 -20
  39. data/lib/puma/null_io.rb +15 -0
  40. data/lib/puma/plugin.rb +104 -0
  41. data/lib/puma/plugin/tmp_restart.rb +35 -0
  42. data/lib/puma/rack/backports/uri/common_18.rb +56 -0
  43. data/lib/puma/rack/backports/uri/common_192.rb +52 -0
  44. data/lib/puma/rack/backports/uri/common_193.rb +29 -0
  45. data/lib/puma/rack/builder.rb +295 -0
  46. data/lib/puma/rack/urlmap.rb +90 -0
  47. data/lib/puma/reactor.rb +14 -1
  48. data/lib/puma/runner.rb +35 -17
  49. data/lib/puma/server.rb +161 -58
  50. data/lib/puma/single.rb +15 -10
  51. data/lib/puma/state_file.rb +29 -0
  52. data/lib/puma/thread_pool.rb +88 -13
  53. data/lib/puma/util.rb +123 -0
  54. data/lib/rack/handler/puma.rb +35 -29
  55. data/puma.gemspec +2 -4
  56. data/tools/jungle/init.d/README.md +2 -2
  57. data/tools/jungle/init.d/puma +69 -7
  58. data/tools/jungle/upstart/puma.conf +8 -2
  59. metadata +51 -71
  60. data/COPYING +0 -55
  61. data/TODO +0 -5
  62. data/lib/puma/rack_patch.rb +0 -45
  63. data/test/test_app_status.rb +0 -92
  64. data/test/test_cli.rb +0 -173
  65. data/test/test_config.rb +0 -16
  66. data/test/test_http10.rb +0 -27
  67. data/test/test_http11.rb +0 -145
  68. data/test/test_integration.rb +0 -165
  69. data/test/test_iobuffer.rb +0 -38
  70. data/test/test_minissl.rb +0 -25
  71. data/test/test_null_io.rb +0 -31
  72. data/test/test_persistent.rb +0 -238
  73. data/test/test_puma_server.rb +0 -292
  74. data/test/test_rack_handler.rb +0 -10
  75. data/test/test_rack_server.rb +0 -141
  76. data/test/test_tcp_rack.rb +0 -42
  77. data/test/test_thread_pool.rb +0 -156
  78. data/test/test_unix_socket.rb +0 -39
  79. data/test/test_ws.rb +0 -89
@@ -0,0 +1,107 @@
1
+ module Puma
2
+ # Rack::CommonLogger forwards every request to the given +app+, and
3
+ # logs a line in the
4
+ # {Apache common log format}[http://httpd.apache.org/docs/1.3/logs.html#common]
5
+ # to the +logger+.
6
+ #
7
+ # If +logger+ is nil, CommonLogger will fall back +rack.errors+, which is
8
+ # an instance of Rack::NullLogger.
9
+ #
10
+ # +logger+ can be any class, including the standard library Logger, and is
11
+ # expected to have either +write+ or +<<+ method, which accepts the CommonLogger::FORMAT.
12
+ # According to the SPEC, the error stream must also respond to +puts+
13
+ # (which takes a single argument that responds to +to_s+), and +flush+
14
+ # (which is called without arguments in order to make the error appear for
15
+ # sure)
16
+ class CommonLogger
17
+ # Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common
18
+ #
19
+ # lilith.local - - [07/Aug/2006 23:58:02 -0400] "GET / HTTP/1.1" 500 -
20
+ #
21
+ # %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
22
+ FORMAT = %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n}
23
+
24
+ def initialize(app, logger=nil)
25
+ @app = app
26
+ @logger = logger
27
+ end
28
+
29
+ def call(env)
30
+ began_at = Time.now
31
+ status, header, body = @app.call(env)
32
+ header = Util::HeaderHash.new(header)
33
+
34
+ # If we've been hijacked, then output a special line
35
+ if env['rack.hijack_io']
36
+ log_hijacking(env, 'HIJACK', header, began_at)
37
+ else
38
+ ary = env['rack.after_reply']
39
+ ary << lambda { log(env, status, header, began_at) }
40
+ end
41
+
42
+ [status, header, body]
43
+ end
44
+
45
+ HIJACK_FORMAT = %{%s - %s [%s] "%s %s%s %s" HIJACKED -1 %0.4f\n}
46
+
47
+ private
48
+
49
+ def log_hijacking(env, status, header, began_at)
50
+ now = Time.now
51
+
52
+ logger = @logger || env['rack.errors']
53
+ logger.write HIJACK_FORMAT % [
54
+ env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
55
+ env["REMOTE_USER"] || "-",
56
+ now.strftime("%d/%b/%Y %H:%M:%S"),
57
+ env["REQUEST_METHOD"],
58
+ env["PATH_INFO"],
59
+ env["QUERY_STRING"].empty? ? "" : "?"+env["QUERY_STRING"],
60
+ env["HTTP_VERSION"],
61
+ now - began_at ]
62
+ end
63
+
64
+ PATH_INFO = 'PATH_INFO'.freeze
65
+ REQUEST_METHOD = 'REQUEST_METHOD'.freeze
66
+ SCRIPT_NAME = 'SCRIPT_NAME'.freeze
67
+ QUERY_STRING = 'QUERY_STRING'.freeze
68
+ CACHE_CONTROL = 'Cache-Control'.freeze
69
+ CONTENT_LENGTH = 'Content-Length'.freeze
70
+ CONTENT_TYPE = 'Content-Type'.freeze
71
+
72
+ GET = 'GET'.freeze
73
+ HEAD = 'HEAD'.freeze
74
+
75
+
76
+ def log(env, status, header, began_at)
77
+ now = Time.now
78
+ length = extract_content_length(header)
79
+
80
+ msg = FORMAT % [
81
+ env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
82
+ env["REMOTE_USER"] || "-",
83
+ now.strftime("%d/%b/%Y:%H:%M:%S %z"),
84
+ env[REQUEST_METHOD],
85
+ env[PATH_INFO],
86
+ env[QUERY_STRING].empty? ? "" : "?"+env[QUERY_STRING],
87
+ env["HTTP_VERSION"],
88
+ status.to_s[0..3],
89
+ length,
90
+ now - began_at ]
91
+
92
+ logger = @logger || env['rack.errors']
93
+ # Standard library logger doesn't support write but it supports << which actually
94
+ # calls to write on the log device without formatting
95
+ if logger.respond_to?(:write)
96
+ logger.write(msg)
97
+ else
98
+ logger << msg
99
+ end
100
+ end
101
+
102
+ def extract_content_length(headers)
103
+ value = headers[CONTENT_LENGTH] or return '-'
104
+ value.to_s == '0' ? '-' : value
105
+ end
106
+ end
107
+ end
@@ -1,59 +1,218 @@
1
+ require 'puma/rack/builder'
2
+ require 'puma/plugin'
3
+
1
4
  module Puma
2
5
 
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.
6
+ module ConfigDefault
7
+ DefaultRackup = "config.ru"
7
8
 
8
- class << self
9
- attr_accessor :cli_config
9
+ DefaultTCPHost = "0.0.0.0"
10
+ DefaultTCPPort = 9292
11
+ DefaultWorkerTimeout = 60
12
+ DefaultWorkerShutdownTimeout = 30
13
+ end
14
+
15
+ class LeveledOptions
16
+ def initialize(default={})
17
+ @cur = {}
18
+ @set = [@cur]
19
+ @defaults = default.dup
20
+ end
21
+
22
+ def initialize_copy(other)
23
+ @set = @set.map { |o| o.dup }
24
+ @cur = @set.last
25
+ end
26
+
27
+ def shift
28
+ @cur = {}
29
+ @set << @cur
30
+ end
31
+
32
+ def [](key)
33
+ @set.each do |o|
34
+ if o.key? key
35
+ return o[key]
36
+ end
37
+ end
38
+
39
+ v = @defaults[key]
40
+ if v.respond_to? :call
41
+ v.call
42
+ else
43
+ v
44
+ end
45
+ end
46
+
47
+ def fetch(key, default=nil)
48
+ val = self[key]
49
+ return val if val
50
+ default
51
+ end
52
+
53
+ attr_reader :cur
54
+
55
+ def all_of(key)
56
+ all = []
57
+
58
+ @set.each do |o|
59
+ if v = o[key]
60
+ if v.kind_of? Array
61
+ all += v
62
+ else
63
+ all << v
64
+ end
65
+ end
66
+ end
67
+
68
+ all
69
+ end
70
+
71
+ def []=(key, val)
72
+ @cur[key] = val
73
+ end
74
+
75
+ def key?(key)
76
+ @set.each do |o|
77
+ if o.key? key
78
+ return true
79
+ end
80
+ end
81
+
82
+ @default.key? key
83
+ end
84
+
85
+ def merge!(o)
86
+ o.each do |k,v|
87
+ @cur[k]= v
88
+ end
89
+ end
90
+
91
+ def flatten
92
+ options = {}
93
+
94
+ @set.each do |o|
95
+ o.each do |k,v|
96
+ options[k] ||= v
97
+ end
98
+ end
99
+
100
+ options
101
+ end
102
+
103
+ def explain
104
+ indent = ""
105
+
106
+ @set.each do |o|
107
+ o.keys.sort.each do |k|
108
+ puts "#{indent}#{k}: #{o[k].inspect}"
109
+ end
110
+
111
+ indent = " #{indent}"
112
+ end
113
+ end
114
+
115
+ def force_defaults
116
+ @defaults.each do |k,v|
117
+ if v.respond_to? :call
118
+ @defaults[k] = v.call
119
+ end
120
+ end
121
+ end
10
122
  end
11
123
 
12
124
  class Configuration
13
- DefaultRackup = "config.ru"
125
+ include ConfigDefault
14
126
 
15
- DefaultTCPHost = "0.0.0.0"
16
- DefaultTCPPort = 9292
127
+ def self.from_file(path)
128
+ cfg = new
17
129
 
18
- def initialize(options)
19
- @options = options
20
- @options[:mode] ||= :http
21
- @options[:binds] ||= []
22
- @options[:on_restart] ||= []
23
- @options[:worker_boot] ||= []
130
+ DSL.new(cfg.options, cfg)._load_from path
131
+
132
+ return cfg
24
133
  end
25
134
 
26
- attr_reader :options
135
+ def initialize(options={}, &blk)
136
+ @options = LeveledOptions.new(default_options)
137
+ @plugins = PluginLoader.new
138
+
139
+ # options.each do |k,v|
140
+ # @options[k] = v
141
+ # end
142
+
143
+ if blk
144
+ configure(&blk)
145
+ end
146
+ end
147
+
148
+ attr_reader :options, :plugins
149
+
150
+ def configure(&blk)
151
+ @options.shift
152
+ DSL.new(@options, self)._run(&blk)
153
+ end
27
154
 
28
155
  def initialize_copy(other)
156
+ @conf = nil
157
+ @cli_options = nil
29
158
  @options = @options.dup
30
159
  end
31
160
 
161
+ def flatten
162
+ dup.flatten!
163
+ end
164
+
165
+ def flatten!
166
+ @options = @options.flatten
167
+ self
168
+ end
169
+
170
+ def 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 => lambda { ENV['RACK_ENV'] || "development" },
186
+ :rackup => DefaultRackup,
187
+ :logger => STDOUT
188
+ }
189
+ end
190
+
32
191
  def load
33
- if path = @options[:config_file]
34
- DSL.new(@options)._load_from path
35
- end
192
+ files = @options.all_of(:config_files)
36
193
 
37
- # Rakeup default option support
38
- if host = @options[:Host]
39
- port = @options[:Port] || DefaultTCPPort
194
+ if files.empty?
195
+ imp = %W(config/puma/#{@options[:environment]}.rb config/puma.rb).find { |f|
196
+ File.exist?(f)
197
+ }
40
198
 
41
- @options[:binds] << "tcp://#{host}:#{port}"
199
+ files << imp
200
+ elsif files == ["-"]
201
+ files = []
42
202
  end
43
203
 
44
- if @options[:binds].empty?
45
- @options[:binds] << "tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"
46
- end
204
+ files.each do |f|
205
+ @options.shift
47
206
 
48
- if @options[:control_url] == "auto"
49
- path = Configuration.temp_path
50
- @options[:control_url] = "unix://#{path}"
51
- @options[:control_url_temp] = path
207
+ DSL.load @options, self, f
52
208
  end
209
+ end
53
210
 
54
- unless @options[:control_auth_token]
55
- setup_random_token
56
- end
211
+ # Call once all configuration (included from rackup files)
212
+ # is loaded to flesh out any defaults
213
+ def clamp
214
+ @options.shift
215
+ @options.force_defaults
57
216
  end
58
217
 
59
218
  # Injects the Configuration object into the env
@@ -72,75 +231,45 @@ module Puma
72
231
  # Indicate if there is a properly configured app
73
232
  #
74
233
  def app_configured?
75
- @options[:app] || File.exists?(rackup)
234
+ @options[:app] || File.exist?(rackup)
76
235
  end
77
236
 
78
237
  def rackup
79
- @options[:rackup] || DefaultRackup
238
+ @options[:rackup]
80
239
  end
81
240
 
82
- # Load the specified rackup file, pull an options from
241
+ # Load the specified rackup file, pull options from
83
242
  # the rackup file, and set @app.
84
243
  #
85
244
  def app
86
- app = @options[:app]
87
-
88
- unless app
89
- unless File.exists?(rackup)
90
- raise "Missing rackup file '#{rackup}'"
91
- end
92
-
93
- app, options = Rack::Builder.parse_file rackup
94
- @options.merge! options
95
-
96
- options.each do |key,val|
97
- if key.to_s[0,4] == "bind"
98
- @options[:binds] << val
99
- end
100
- end
101
- end
245
+ found = options[:app] || load_rackup
102
246
 
103
247
  if @options[:mode] == :tcp
104
248
  require 'puma/tcp_logger'
105
249
 
106
- logger = @options[:logger] || STDOUT
107
- return TCPLogger.new(logger, app, @options[:quiet])
250
+ logger = @options[:logger]
251
+ return TCPLogger.new(logger, found, @options[:log_requests])
108
252
  end
109
253
 
110
- if !@options[:quiet] and @options[:environment] == "development"
111
- logger = @options[:logger] || STDOUT
112
- app = Rack::CommonLogger.new(app, logger)
254
+ if @options[:log_requests]
255
+ logger = @options[:logger]
256
+ found = CommonLogger.new(found, logger)
113
257
  end
114
258
 
115
- return ConfigMiddleware.new(self, app)
259
+ ConfigMiddleware.new(self, found)
116
260
  end
117
261
 
118
- def setup_random_token
119
- begin
120
- require 'openssl'
121
- rescue LoadError
122
- end
123
-
124
- count = 16
125
-
126
- bytes = nil
127
-
128
- if defined? OpenSSL::Random
129
- bytes = OpenSSL::Random.random_bytes(count)
130
- elsif File.exists?("/dev/urandom")
131
- File.open("/dev/urandom") do |f|
132
- bytes = f.read(count)
133
- end
134
- end
262
+ # Return which environment we're running in
263
+ def environment
264
+ @options[:environment]
265
+ end
135
266
 
136
- if bytes
137
- token = ""
138
- bytes.each_byte { |b| token << b.to_s(16) }
139
- else
140
- token = (0..count).to_a.map { rand(255).to_s(16) }.join
141
- end
267
+ def load_plugin(name)
268
+ @plugins.create name
269
+ end
142
270
 
143
- @options[:control_auth_token] = token
271
+ def run_hooks(key, arg)
272
+ @options.all_of(key).each { |b| b.call arg }
144
273
  end
145
274
 
146
275
  def self.temp_path
@@ -150,181 +279,79 @@ module Puma
150
279
  "#{Dir.tmpdir}/puma-status-#{t}-#{$$}"
151
280
  end
152
281
 
153
- # The methods that are available for use inside the config file.
154
- #
155
- class DSL
156
- def initialize(options)
157
- @options = options
158
- end
159
-
160
- def _load_from(path)
161
- instance_eval File.read(path), path, 1
162
- end
163
-
164
- # Use +obj+ or +block+ as the Rack app. This allows a config file to
165
- # be the app itself.
166
- #
167
- def app(obj=nil, &block)
168
- obj ||= block
282
+ private
169
283
 
170
- raise "Provide either a #call'able or a block" unless obj
171
-
172
- @options[:app] = obj
173
- end
174
-
175
- # Start the Puma control rack app on +url+. This app can be communicated
176
- # with to control the main server.
177
- #
178
- def activate_control_app(url="auto", opts=nil)
179
- @options[:control_url] = url
180
-
181
- if opts
182
- if tok = opts[:auth_token]
183
- @options[:control_auth_token] = tok
184
- end
284
+ def infer_tag
285
+ File.basename(Dir.getwd)
286
+ end
185
287
 
186
- if opts[:no_token]
187
- @options[:control_auth_token] = :none
188
- end
288
+ # Load and use the normal Rack builder if we can, otherwise
289
+ # fallback to our minimal version.
290
+ def rack_builder
291
+ # Load bundler now if we can so that we can pickup rack from
292
+ # a Gemfile
293
+ if ENV.key? 'PUMA_BUNDLER_PRUNED'
294
+ begin
295
+ require 'bundler/setup'
296
+ rescue LoadError
189
297
  end
190
298
  end
191
299
 
192
- # Bind the server to +url+. tcp:// and unix:// are the only accepted
193
- # protocols.
194
- #
195
- def bind(url)
196
- @options[:binds] << url
197
- end
198
-
199
- # Define the TCP port to bind to. Use +bind+ for more advanced options.
200
- #
201
- def port(port)
202
- @options[:binds] << "tcp://#{Configuration::DefaultTCPHost}:#{port}"
203
- end
204
-
205
- # Daemonize the server into the background. Highly suggest that
206
- # this be combined with +pidfile+ and +stdout_redirect+.
207
- def daemonize(which=true)
208
- @options[:daemon] = which
209
- end
210
-
211
- # When shutting down, drain the accept socket of pending
212
- # connections and proces them. This loops over the accept
213
- # socket until there are no more read events and then stops
214
- # looking and waits for the requests to finish.
215
- def drain_on_shutdown(which=true)
216
- @options[:drain_on_shutdown] = which
217
- end
218
-
219
- # Set the environment in which the Rack's app will run.
220
- def environment(environment)
221
- @options[:environment] = environment
222
- end
223
-
224
- # Code to run before doing a restart. This code should
225
- # close logfiles, database connections, etc.
226
- #
227
- # This can be called multiple times to add code each time.
228
- #
229
- def on_restart(&block)
230
- @options[:on_restart] << block
231
- end
232
-
233
- # Command to use to restart puma. This should be just how to
234
- # load puma itself (ie. 'ruby -Ilib bin/puma'), not the arguments
235
- # to puma, as those are the same as the original process.
236
- #
237
- def restart_command(cmd)
238
- @options[:restart_cmd] = cmd
300
+ begin
301
+ require 'rack'
302
+ require 'rack/builder'
303
+ rescue LoadError
304
+ # ok, use builtin version
305
+ return Puma::Rack::Builder
306
+ else
307
+ return ::Rack::Builder
239
308
  end
309
+ end
240
310
 
241
- # Store the pid of the server in the file at +path+.
242
- def pidfile(path)
243
- @options[:pidfile] = path
244
- end
311
+ def load_rackup
312
+ raise "Missing rackup file '#{rackup}'" unless File.exist?(rackup)
245
313
 
246
- # Disable request logging.
247
- #
248
- def quiet
249
- @options[:quiet] = true
250
- end
314
+ @options.shift
251
315
 
252
- # Load +path+ as a rackup file.
253
- #
254
- def rackup(path)
255
- @options[:rackup] = path.to_s
256
- end
316
+ rack_app, rack_options = rack_builder.parse_file(rackup)
317
+ @options.merge!(rack_options)
257
318
 
258
- # Redirect STDOUT and STDERR to files specified.
259
- def stdout_redirect(stdout=nil, stderr=nil, append=false)
260
- @options[:redirect_stdout] = stdout
261
- @options[:redirect_stderr] = stderr
262
- @options[:redirect_append] = append
319
+ config_ru_binds = []
320
+ rack_options.each do |k, v|
321
+ config_ru_binds << v if k.to_s.start_with?("bind")
263
322
  end
264
323
 
265
- # Configure +min+ to be the minimum number of threads to use to answer
266
- # requests and +max+ the maximum.
267
- #
268
- def threads(min, max)
269
- min = Integer(min)
270
- max = Integer(max)
271
- if min > max
272
- raise "The minimum (#{min}) number of threads must be less than the max (#{max})"
273
- end
274
-
275
- @options[:min_threads] = min
276
- @options[:max_threads] = max
277
- end
324
+ @options[:binds] = config_ru_binds unless config_ru_binds.empty?
278
325
 
279
- def ssl_bind(host, port, opts)
280
- o = [
281
- "cert=#{opts[:cert]}",
282
- "key=#{opts[:key]}"
283
- ]
284
-
285
- @options[:binds] << "ssl://#{host}:#{port}?#{o.join('&')}"
286
- end
326
+ rack_app
327
+ end
287
328
 
288
- # Use +path+ as the file to store the server info state. This is
289
- # used by pumactl to query and control the server.
290
- #
291
- def state_path(path)
292
- @options[:state] = path.to_s
329
+ def self.random_token
330
+ begin
331
+ require 'openssl'
332
+ rescue LoadError
293
333
  end
294
334
 
295
- # *Cluster mode only* How many worker processes to run.
296
- #
297
- def workers(count)
298
- @options[:workers] = count.to_i
299
- end
335
+ count = 16
300
336
 
301
- # *Cluster mode only* Code to run when a worker boots to setup
302
- # the process before booting the app.
303
- #
304
- # This can be called multiple times to add hooks.
305
- #
306
- def on_worker_boot(&block)
307
- @options[:worker_boot] << block
308
- end
337
+ bytes = nil
309
338
 
310
- # The directory to operate out of.
311
- def directory(dir)
312
- @options[:directory] = dir.to_s
313
- @options[:worker_directory] = dir.to_s
339
+ if defined? OpenSSL::Random
340
+ bytes = OpenSSL::Random.random_bytes(count)
341
+ elsif File.exist?("/dev/urandom")
342
+ File.open('/dev/urandom') { |f| bytes = f.read(count) }
314
343
  end
315
344
 
316
- # Run the app as a raw TCP app instead of an HTTP rack app
317
- def tcp_mode
318
- @options[:mode] = :tcp
345
+ if bytes
346
+ token = ""
347
+ bytes.each_byte { |b| token << b.to_s(16) }
348
+ else
349
+ token = (0..count).to_a.map { rand(255).to_s(16) }.join
319
350
  end
320
351
 
321
- # *Cluster mode only* Preload the application before starting
322
- # the workers and setting up the listen ports. This conflicts
323
- # with using the phased restart feature, you can't use both.
324
- #
325
- def preload_app!(answer=true)
326
- @options[:preload_app] = answer
327
- end
352
+ return token
328
353
  end
329
354
  end
330
355
  end
356
+
357
+ require 'puma/dsl'