puma 2.11.1 → 2.11.2
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.
- checksums.yaml +4 -4
- data/History.txt +23 -0
- data/Manifest.txt +1 -0
- data/lib/puma/cli.rb +269 -303
- data/lib/puma/cluster.rb +1 -0
- data/lib/puma/configuration.rb +62 -328
- data/lib/puma/const.rb +1 -1
- data/lib/puma/dsl.rb +280 -0
- data/lib/puma/server.rb +24 -7
- data/puma.gemspec +1 -1
- metadata +4 -39
- data/test/test_app_status.rb +0 -92
- data/test/test_cli.rb +0 -173
- data/test/test_config.rb +0 -26
- data/test/test_http10.rb +0 -27
- data/test/test_http11.rb +0 -144
- data/test/test_integration.rb +0 -206
- data/test/test_iobuffer.rb +0 -38
- data/test/test_minissl.rb +0 -29
- data/test/test_null_io.rb +0 -31
- data/test/test_persistent.rb +0 -238
- data/test/test_puma_server.rb +0 -288
- data/test/test_puma_server_ssl.rb +0 -137
- data/test/test_rack_handler.rb +0 -10
- data/test/test_rack_server.rb +0 -141
- data/test/test_tcp_rack.rb +0 -42
- data/test/test_thread_pool.rb +0 -182
- data/test/test_unix_socket.rb +0 -39
- data/test/test_ws.rb +0 -89
data/lib/puma/cluster.rb
CHANGED
data/lib/puma/configuration.rb
CHANGED
@@ -1,21 +1,16 @@
|
|
1
1
|
module Puma
|
2
2
|
|
3
|
-
|
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.
|
7
|
-
|
8
|
-
class << self
|
9
|
-
attr_accessor :cli_config
|
10
|
-
end
|
11
|
-
|
12
|
-
class Configuration
|
3
|
+
module ConfigDefault
|
13
4
|
DefaultRackup = "config.ru"
|
14
5
|
|
15
6
|
DefaultTCPHost = "0.0.0.0"
|
16
7
|
DefaultTCPPort = 9292
|
17
8
|
DefaultWorkerTimeout = 60
|
18
9
|
DefaultWorkerShutdownTimeout = 30
|
10
|
+
end
|
11
|
+
|
12
|
+
class Configuration
|
13
|
+
include ConfigDefault
|
19
14
|
|
20
15
|
def initialize(options)
|
21
16
|
@options = options
|
@@ -24,6 +19,7 @@ module Puma
|
|
24
19
|
@options[:on_restart] ||= []
|
25
20
|
@options[:before_worker_shutdown] ||= []
|
26
21
|
@options[:before_worker_boot] ||= []
|
22
|
+
@options[:before_worker_fork] ||= []
|
27
23
|
@options[:after_worker_boot] ||= []
|
28
24
|
@options[:worker_timeout] ||= DefaultWorkerTimeout
|
29
25
|
@options[:worker_shutdown_timeout] ||= DefaultWorkerShutdownTimeout
|
@@ -36,38 +32,11 @@ module Puma
|
|
36
32
|
end
|
37
33
|
|
38
34
|
def load
|
39
|
-
|
40
|
-
DSL.new(@options)._load_from path
|
41
|
-
end
|
42
|
-
|
43
|
-
# Rakeup default option support
|
44
|
-
if host = @options[:Host]
|
45
|
-
port = @options[:Port] || DefaultTCPPort
|
46
|
-
|
47
|
-
@options[:binds] << "tcp://#{host}:#{port}"
|
48
|
-
end
|
49
|
-
|
50
|
-
if @options[:binds].empty?
|
51
|
-
@options[:binds] << "tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"
|
52
|
-
end
|
35
|
+
DSL.load(@options, @options[:config_file])
|
53
36
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
@options[:control_url_temp] = path
|
58
|
-
end
|
59
|
-
|
60
|
-
unless @options[:control_auth_token]
|
61
|
-
setup_random_token
|
62
|
-
end
|
63
|
-
|
64
|
-
unless @options[:tag]
|
65
|
-
@options[:tag] = infer_tag
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def infer_tag
|
70
|
-
File.basename Dir.getwd
|
37
|
+
setup_binds
|
38
|
+
setup_control
|
39
|
+
@options[:tag] ||= infer_tag
|
71
40
|
end
|
72
41
|
|
73
42
|
# Injects the Configuration object into the env
|
@@ -97,68 +66,21 @@ module Puma
|
|
97
66
|
# the rackup file, and set @app.
|
98
67
|
#
|
99
68
|
def app
|
100
|
-
|
101
|
-
|
102
|
-
unless app
|
103
|
-
unless File.exist?(rackup)
|
104
|
-
raise "Missing rackup file '#{rackup}'"
|
105
|
-
end
|
106
|
-
|
107
|
-
app, options = Rack::Builder.parse_file rackup
|
108
|
-
@options.merge! options
|
109
|
-
|
110
|
-
config_ru_binds = []
|
111
|
-
|
112
|
-
options.each do |key,val|
|
113
|
-
if key.to_s[0,4] == "bind"
|
114
|
-
config_ru_binds << val
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
@options[:binds] = config_ru_binds unless config_ru_binds.empty?
|
119
|
-
end
|
69
|
+
found = options[:app] || load_rackup
|
120
70
|
|
121
71
|
if @options[:mode] == :tcp
|
122
72
|
require 'puma/tcp_logger'
|
123
73
|
|
124
74
|
logger = @options[:logger] || STDOUT
|
125
|
-
return TCPLogger.new(logger,
|
75
|
+
return TCPLogger.new(logger, found, @options[:quiet])
|
126
76
|
end
|
127
77
|
|
128
78
|
if !@options[:quiet] and @options[:environment] == "development"
|
129
79
|
logger = @options[:logger] || STDOUT
|
130
|
-
|
80
|
+
found = Rack::CommonLogger.new(found, logger)
|
131
81
|
end
|
132
82
|
|
133
|
-
|
134
|
-
end
|
135
|
-
|
136
|
-
def setup_random_token
|
137
|
-
begin
|
138
|
-
require 'openssl'
|
139
|
-
rescue LoadError
|
140
|
-
end
|
141
|
-
|
142
|
-
count = 16
|
143
|
-
|
144
|
-
bytes = nil
|
145
|
-
|
146
|
-
if defined? OpenSSL::Random
|
147
|
-
bytes = OpenSSL::Random.random_bytes(count)
|
148
|
-
elsif File.exist?("/dev/urandom")
|
149
|
-
File.open("/dev/urandom") do |f|
|
150
|
-
bytes = f.read(count)
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
if bytes
|
155
|
-
token = ""
|
156
|
-
bytes.each_byte { |b| token << b.to_s(16) }
|
157
|
-
else
|
158
|
-
token = (0..count).to_a.map { rand(255).to_s(16) }.join
|
159
|
-
end
|
160
|
-
|
161
|
-
@options[:control_auth_token] = token
|
83
|
+
ConfigMiddleware.new(self, found)
|
162
84
|
end
|
163
85
|
|
164
86
|
def self.temp_path
|
@@ -168,265 +90,77 @@ module Puma
|
|
168
90
|
"#{Dir.tmpdir}/puma-status-#{t}-#{$$}"
|
169
91
|
end
|
170
92
|
|
171
|
-
|
172
|
-
#
|
173
|
-
class DSL
|
174
|
-
def initialize(options)
|
175
|
-
@options = options
|
176
|
-
end
|
177
|
-
|
178
|
-
def _load_from(path)
|
179
|
-
instance_eval File.read(path), path, 1
|
180
|
-
end
|
181
|
-
|
182
|
-
# Use +obj+ or +block+ as the Rack app. This allows a config file to
|
183
|
-
# be the app itself.
|
184
|
-
#
|
185
|
-
def app(obj=nil, &block)
|
186
|
-
obj ||= block
|
93
|
+
private
|
187
94
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
end
|
192
|
-
|
193
|
-
# Start the Puma control rack app on +url+. This app can be communicated
|
194
|
-
# with to control the main server.
|
195
|
-
#
|
196
|
-
def activate_control_app(url="auto", opts=nil)
|
197
|
-
@options[:control_url] = url
|
198
|
-
|
199
|
-
if opts
|
200
|
-
if tok = opts[:auth_token]
|
201
|
-
@options[:control_auth_token] = tok
|
202
|
-
end
|
203
|
-
|
204
|
-
if opts[:no_token]
|
205
|
-
@options[:control_auth_token] = :none
|
206
|
-
end
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
# Bind the server to +url+. tcp:// and unix:// are the only accepted
|
211
|
-
# protocols.
|
212
|
-
#
|
213
|
-
def bind(url)
|
214
|
-
@options[:binds] << url
|
215
|
-
end
|
216
|
-
|
217
|
-
# Define the TCP port to bind to. Use +bind+ for more advanced options.
|
218
|
-
#
|
219
|
-
def port(port)
|
220
|
-
@options[:binds] << "tcp://#{Configuration::DefaultTCPHost}:#{port}"
|
221
|
-
end
|
222
|
-
|
223
|
-
# Work around leaky apps that leave garbage in Thread locals
|
224
|
-
# across requests
|
225
|
-
#
|
226
|
-
def clean_thread_locals(which=true)
|
227
|
-
@options[:clean_thread_locals] = which
|
228
|
-
end
|
229
|
-
|
230
|
-
# Daemonize the server into the background. Highly suggest that
|
231
|
-
# this be combined with +pidfile+ and +stdout_redirect+.
|
232
|
-
def daemonize(which=true)
|
233
|
-
@options[:daemon] = which
|
234
|
-
end
|
235
|
-
|
236
|
-
# When shutting down, drain the accept socket of pending
|
237
|
-
# connections and proces them. This loops over the accept
|
238
|
-
# socket until there are no more read events and then stops
|
239
|
-
# looking and waits for the requests to finish.
|
240
|
-
def drain_on_shutdown(which=true)
|
241
|
-
@options[:drain_on_shutdown] = which
|
242
|
-
end
|
243
|
-
|
244
|
-
# Set the environment in which the Rack's app will run.
|
245
|
-
def environment(environment)
|
246
|
-
@options[:environment] = environment
|
247
|
-
end
|
248
|
-
|
249
|
-
# Code to run before doing a restart. This code should
|
250
|
-
# close logfiles, database connections, etc.
|
251
|
-
#
|
252
|
-
# This can be called multiple times to add code each time.
|
253
|
-
#
|
254
|
-
def on_restart(&block)
|
255
|
-
@options[:on_restart] << block
|
256
|
-
end
|
257
|
-
|
258
|
-
# Command to use to restart puma. This should be just how to
|
259
|
-
# load puma itself (ie. 'ruby -Ilib bin/puma'), not the arguments
|
260
|
-
# to puma, as those are the same as the original process.
|
261
|
-
#
|
262
|
-
def restart_command(cmd)
|
263
|
-
@options[:restart_cmd] = cmd
|
264
|
-
end
|
265
|
-
|
266
|
-
# Store the pid of the server in the file at +path+.
|
267
|
-
def pidfile(path)
|
268
|
-
@options[:pidfile] = path
|
269
|
-
end
|
270
|
-
|
271
|
-
# Disable request logging.
|
272
|
-
#
|
273
|
-
def quiet
|
274
|
-
@options[:quiet] = true
|
275
|
-
end
|
276
|
-
|
277
|
-
# Load +path+ as a rackup file.
|
278
|
-
#
|
279
|
-
def rackup(path)
|
280
|
-
@options[:rackup] = path.to_s
|
281
|
-
end
|
282
|
-
|
283
|
-
# Redirect STDOUT and STDERR to files specified.
|
284
|
-
def stdout_redirect(stdout=nil, stderr=nil, append=false)
|
285
|
-
@options[:redirect_stdout] = stdout
|
286
|
-
@options[:redirect_stderr] = stderr
|
287
|
-
@options[:redirect_append] = append
|
288
|
-
end
|
289
|
-
|
290
|
-
# Configure +min+ to be the minimum number of threads to use to answer
|
291
|
-
# requests and +max+ the maximum.
|
292
|
-
#
|
293
|
-
def threads(min, max)
|
294
|
-
min = Integer(min)
|
295
|
-
max = Integer(max)
|
296
|
-
if min > max
|
297
|
-
raise "The minimum (#{min}) number of threads must be less than the max (#{max})"
|
298
|
-
end
|
299
|
-
|
300
|
-
@options[:min_threads] = min
|
301
|
-
@options[:max_threads] = max
|
302
|
-
end
|
303
|
-
|
304
|
-
def ssl_bind(host, port, opts)
|
305
|
-
o = [
|
306
|
-
"cert=#{opts[:cert]}",
|
307
|
-
"key=#{opts[:key]}"
|
308
|
-
]
|
95
|
+
def infer_tag
|
96
|
+
File.basename(Dir.getwd)
|
97
|
+
end
|
309
98
|
|
310
|
-
|
311
|
-
|
99
|
+
def load_rackup
|
100
|
+
raise "Missing rackup file '#{rackup}'" unless File.exist?(rackup)
|
312
101
|
|
313
|
-
|
314
|
-
|
315
|
-
#
|
316
|
-
def state_path(path)
|
317
|
-
@options[:state] = path.to_s
|
318
|
-
end
|
102
|
+
rack_app, rack_options = Rack::Builder.parse_file(rackup)
|
103
|
+
@options.merge!(rack_options)
|
319
104
|
|
320
|
-
|
321
|
-
|
322
|
-
def workers(count)
|
323
|
-
@options[:workers] = count.to_i
|
105
|
+
config_ru_binds = rack_options.each_with_object([]) do |(k, v), b|
|
106
|
+
b << v if k.start_with?('bind')
|
324
107
|
end
|
108
|
+
@options[:binds] = config_ru_binds unless config_ru_binds.empty?
|
325
109
|
|
326
|
-
|
327
|
-
|
328
|
-
# can block if necessary to wait for background operations unknown
|
329
|
-
# to puma to finish before the process terminates.
|
330
|
-
#
|
331
|
-
# This can be called multiple times to add hooks.
|
332
|
-
#
|
333
|
-
def on_worker_shutdown(&block)
|
334
|
-
@options[:before_worker_shutdown] << block
|
335
|
-
end
|
110
|
+
rack_app
|
111
|
+
end
|
336
112
|
|
337
|
-
|
338
|
-
#
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
@options[:before_worker_boot] << block
|
113
|
+
def setup_binds
|
114
|
+
# Rakeup default option support
|
115
|
+
host = @options[:Host]
|
116
|
+
if host
|
117
|
+
port = @options[:Port] || DefaultTCPPort
|
118
|
+
@options[:binds] << "tcp://#{host}:#{port}"
|
344
119
|
end
|
345
120
|
|
346
|
-
|
347
|
-
|
348
|
-
#
|
349
|
-
# This can be called multiple times to add hooks.
|
350
|
-
#
|
351
|
-
def after_worker_boot(&block)
|
352
|
-
@options[:after_worker_boot] << block
|
121
|
+
if @options[:binds].empty?
|
122
|
+
@options[:binds] << "tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"
|
353
123
|
end
|
354
124
|
|
355
|
-
|
356
|
-
|
357
|
-
@options[:directory] = dir.to_s
|
358
|
-
@options[:worker_directory] = dir.to_s
|
359
|
-
end
|
125
|
+
@options[:binds].uniq!
|
126
|
+
end
|
360
127
|
|
361
|
-
|
362
|
-
|
363
|
-
|
128
|
+
def setup_control
|
129
|
+
if @options[:control_url] == 'auto'
|
130
|
+
path = Configuration.temp_path
|
131
|
+
@options[:control_url] = "unix://#{path}"
|
132
|
+
@options[:control_url_temp] = path
|
364
133
|
end
|
365
134
|
|
366
|
-
|
367
|
-
|
368
|
-
# with using the phased restart feature, you can't use both.
|
369
|
-
#
|
370
|
-
def preload_app!(answer=true)
|
371
|
-
@options[:preload_app] = answer
|
372
|
-
end
|
135
|
+
setup_random_token unless @options[:control_auth_token]
|
136
|
+
end
|
373
137
|
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
obj ||= block
|
379
|
-
raise "Provide either a #call'able or a block" unless obj
|
380
|
-
@options[:lowlevel_error_handler] = obj
|
138
|
+
def setup_random_token
|
139
|
+
begin
|
140
|
+
require 'openssl'
|
141
|
+
rescue LoadError
|
381
142
|
end
|
382
143
|
|
383
|
-
|
384
|
-
# properly reloaded when not using preload.
|
385
|
-
#
|
386
|
-
# When set, if puma detects that it's been invoked in the
|
387
|
-
# context of Bundler, it will cleanup the environment and
|
388
|
-
# re-run itself outside the Bundler environment, but directly
|
389
|
-
# using the files that Bundler has setup.
|
390
|
-
#
|
391
|
-
# This means that puma is now decoupled from your Bundler
|
392
|
-
# context and when each worker loads, it will be loading a
|
393
|
-
# new Bundler context and thus can float around as the release
|
394
|
-
# dictates.
|
395
|
-
def prune_bundler(answer=true)
|
396
|
-
@options[:prune_bundler] = answer
|
397
|
-
end
|
144
|
+
count = 16
|
398
145
|
|
399
|
-
|
400
|
-
def tag(string)
|
401
|
-
@options[:tag] = string
|
402
|
-
end
|
146
|
+
bytes = nil
|
403
147
|
|
404
|
-
|
405
|
-
|
406
|
-
|
148
|
+
if defined? OpenSSL::Random
|
149
|
+
bytes = OpenSSL::Random.random_bytes(count)
|
150
|
+
elsif File.exist?("/dev/urandom")
|
151
|
+
File.open('/dev/urandom') { |f| bytes = f.read(count) }
|
407
152
|
end
|
408
153
|
|
409
|
-
|
410
|
-
|
411
|
-
|
154
|
+
if bytes
|
155
|
+
token = ""
|
156
|
+
bytes.each_byte { |b| token << b.to_s(16) }
|
157
|
+
else
|
158
|
+
token = (0..count).to_a.map { rand(255).to_s(16) }.join
|
412
159
|
end
|
413
160
|
|
414
|
-
|
415
|
-
# and queue them before passing them to the handlers.
|
416
|
-
# When set to false, each worker process accepts exactly as
|
417
|
-
# many requests as it is configured to simultaneously handle.
|
418
|
-
#
|
419
|
-
# Queueing requests generally improves performance. In some
|
420
|
-
# cases, such as a single threaded application, it may be
|
421
|
-
# better to ensure requests get balanced across workers.
|
422
|
-
#
|
423
|
-
# Note that setting this to false disables HTTP keepalive and
|
424
|
-
# slow clients will occupy a handler thread while the request
|
425
|
-
# is being sent. A reverse proxy, such as nginx, can handle
|
426
|
-
# slow clients and queue requests before they reach puma.
|
427
|
-
def queue_requests(answer=true)
|
428
|
-
@options[:queue_requests] = answer
|
429
|
-
end
|
161
|
+
@options[:control_auth_token] = token
|
430
162
|
end
|
431
163
|
end
|
432
164
|
end
|
165
|
+
|
166
|
+
require 'puma/dsl'
|