puma 5.0.4 → 5.5.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.
- checksums.yaml +4 -4
- data/History.md +250 -48
- data/README.md +90 -24
- data/docs/architecture.md +57 -20
- data/docs/compile_options.md +21 -0
- data/docs/deployment.md +53 -67
- data/docs/fork_worker.md +2 -0
- data/docs/jungle/rc.d/README.md +1 -1
- data/docs/kubernetes.md +66 -0
- data/docs/plugins.md +15 -15
- data/docs/rails_dev_mode.md +28 -0
- data/docs/restart.md +7 -7
- data/docs/signals.md +10 -10
- data/docs/stats.md +142 -0
- data/docs/systemd.md +85 -66
- data/ext/puma_http11/extconf.rb +36 -6
- data/ext/puma_http11/http11_parser.c +64 -59
- data/ext/puma_http11/http11_parser.h +1 -1
- data/ext/puma_http11/http11_parser.java.rl +1 -1
- data/ext/puma_http11/http11_parser.rl +1 -1
- data/ext/puma_http11/http11_parser_common.rl +1 -1
- data/ext/puma_http11/mini_ssl.c +177 -84
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +39 -41
- data/ext/puma_http11/puma_http11.c +8 -2
- data/lib/puma/app/status.rb +4 -7
- data/lib/puma/binder.rb +121 -46
- data/lib/puma/cli.rb +9 -0
- data/lib/puma/client.rb +58 -19
- data/lib/puma/cluster/worker.rb +19 -16
- data/lib/puma/cluster/worker_handle.rb +9 -2
- data/lib/puma/cluster.rb +46 -22
- data/lib/puma/configuration.rb +18 -2
- data/lib/puma/const.rb +14 -4
- data/lib/puma/control_cli.rb +76 -71
- data/lib/puma/detect.rb +14 -10
- data/lib/puma/dsl.rb +143 -26
- data/lib/puma/error_logger.rb +12 -5
- data/lib/puma/events.rb +18 -3
- data/lib/puma/json_serialization.rb +96 -0
- data/lib/puma/launcher.rb +54 -6
- data/lib/puma/minissl/context_builder.rb +6 -0
- data/lib/puma/minissl.rb +54 -38
- data/lib/puma/null_io.rb +12 -0
- data/lib/puma/plugin.rb +1 -1
- data/lib/puma/queue_close.rb +7 -7
- data/lib/puma/rack/builder.rb +1 -1
- data/lib/puma/reactor.rb +19 -12
- data/lib/puma/request.rb +45 -16
- data/lib/puma/runner.rb +38 -13
- data/lib/puma/server.rb +62 -123
- data/lib/puma/state_file.rb +5 -3
- data/lib/puma/systemd.rb +46 -0
- data/lib/puma/thread_pool.rb +10 -7
- data/lib/puma/util.rb +8 -1
- data/lib/puma.rb +36 -10
- data/lib/rack/handler/puma.rb +1 -0
- metadata +15 -9
data/lib/puma/minissl.rb
CHANGED
@@ -73,7 +73,6 @@ module Puma
|
|
73
73
|
|
74
74
|
def engine_read_all
|
75
75
|
output = @engine.read
|
76
|
-
raise SSLError.exception "HTTP connection?" if bad_tlsv1_3?
|
77
76
|
while output and additional_output = @engine.read
|
78
77
|
output << additional_output
|
79
78
|
end
|
@@ -100,6 +99,7 @@ module Puma
|
|
100
99
|
# ourselves.
|
101
100
|
raise IO::EAGAINWaitReadable
|
102
101
|
elsif data.nil?
|
102
|
+
raise SSLError.exception "HTTP connection?" if bad_tlsv1_3?
|
103
103
|
return nil
|
104
104
|
end
|
105
105
|
|
@@ -117,22 +117,23 @@ module Puma
|
|
117
117
|
def write(data)
|
118
118
|
return 0 if data.empty?
|
119
119
|
|
120
|
-
|
120
|
+
data_size = data.bytesize
|
121
|
+
need = data_size
|
121
122
|
|
122
123
|
while true
|
123
124
|
wrote = @engine.write data
|
124
|
-
enc = @engine.extract
|
125
125
|
|
126
|
-
|
127
|
-
|
128
|
-
|
126
|
+
enc_wr = ''.dup
|
127
|
+
while (enc = @engine.extract)
|
128
|
+
enc_wr << enc
|
129
129
|
end
|
130
|
+
@socket.write enc_wr unless enc_wr.empty?
|
130
131
|
|
131
132
|
need -= wrote
|
132
133
|
|
133
|
-
return
|
134
|
+
return data_size if need == 0
|
134
135
|
|
135
|
-
data = data
|
136
|
+
data = data.byteslice(wrote..-1)
|
136
137
|
end
|
137
138
|
end
|
138
139
|
|
@@ -160,30 +161,15 @@ module Puma
|
|
160
161
|
@socket.flush
|
161
162
|
end
|
162
163
|
|
163
|
-
def read_and_drop(timeout = 1)
|
164
|
-
return :timeout unless IO.select([@socket], nil, nil, timeout)
|
165
|
-
case @socket.read_nonblock(1024, exception: false)
|
166
|
-
when nil
|
167
|
-
:eof
|
168
|
-
when :wait_readable
|
169
|
-
:eagain
|
170
|
-
else
|
171
|
-
:drop
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
def should_drop_bytes?
|
176
|
-
@engine.init? || !@engine.shutdown
|
177
|
-
end
|
178
|
-
|
179
164
|
def close
|
180
165
|
begin
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
166
|
+
unless @engine.shutdown
|
167
|
+
while alert_data = @engine.extract
|
168
|
+
@socket.write alert_data
|
169
|
+
end
|
170
|
+
end
|
185
171
|
rescue IOError, SystemCallError
|
186
|
-
|
172
|
+
Puma::Util.purge_interrupt_queue
|
187
173
|
# nothing
|
188
174
|
ensure
|
189
175
|
@socket.close
|
@@ -245,6 +231,7 @@ module Puma
|
|
245
231
|
attr_reader :cert
|
246
232
|
attr_reader :ca
|
247
233
|
attr_accessor :ssl_cipher_filter
|
234
|
+
attr_accessor :verification_flags
|
248
235
|
|
249
236
|
def key=(key)
|
250
237
|
raise ArgumentError, "No such key file '#{key}'" unless File.exist? key
|
@@ -287,33 +274,58 @@ module Puma
|
|
287
274
|
VERIFY_PEER = 1
|
288
275
|
VERIFY_FAIL_IF_NO_PEER_CERT = 2
|
289
276
|
|
277
|
+
# https://github.com/openssl/openssl/blob/master/include/openssl/x509_vfy.h.in
|
278
|
+
# /* Certificate verify flags */
|
279
|
+
VERIFICATION_FLAGS = {
|
280
|
+
"USE_CHECK_TIME" => 0x2,
|
281
|
+
"CRL_CHECK" => 0x4,
|
282
|
+
"CRL_CHECK_ALL" => 0x8,
|
283
|
+
"IGNORE_CRITICAL" => 0x10,
|
284
|
+
"X509_STRICT" => 0x20,
|
285
|
+
"ALLOW_PROXY_CERTS" => 0x40,
|
286
|
+
"POLICY_CHECK" => 0x80,
|
287
|
+
"EXPLICIT_POLICY" => 0x100,
|
288
|
+
"INHIBIT_ANY" => 0x200,
|
289
|
+
"INHIBIT_MAP" => 0x400,
|
290
|
+
"NOTIFY_POLICY" => 0x800,
|
291
|
+
"EXTENDED_CRL_SUPPORT" => 0x1000,
|
292
|
+
"USE_DELTAS" => 0x2000,
|
293
|
+
"CHECK_SS_SIGNATURE" => 0x4000,
|
294
|
+
"TRUSTED_FIRST" => 0x8000,
|
295
|
+
"SUITEB_128_LOS_ONLY" => 0x10000,
|
296
|
+
"SUITEB_192_LOS" => 0x20000,
|
297
|
+
"SUITEB_128_LOS" => 0x30000,
|
298
|
+
"PARTIAL_CHAIN" => 0x80000,
|
299
|
+
"NO_ALT_CHAINS" => 0x100000,
|
300
|
+
"NO_CHECK_TIME" => 0x200000
|
301
|
+
}.freeze
|
302
|
+
|
290
303
|
class Server
|
291
304
|
def initialize(socket, ctx)
|
292
305
|
@socket = socket
|
293
306
|
@ctx = ctx
|
294
|
-
|
295
|
-
|
296
|
-
# @!attribute [r] to_io
|
297
|
-
def to_io
|
298
|
-
@socket
|
307
|
+
@eng_ctx = IS_JRUBY ? @ctx : SSLContext.new(ctx)
|
299
308
|
end
|
300
309
|
|
301
310
|
def accept
|
302
311
|
@ctx.check
|
303
312
|
io = @socket.accept
|
304
|
-
engine = Engine.server @
|
305
|
-
|
313
|
+
engine = Engine.server @eng_ctx
|
306
314
|
Socket.new io, engine
|
307
315
|
end
|
308
316
|
|
309
317
|
def accept_nonblock
|
310
318
|
@ctx.check
|
311
319
|
io = @socket.accept_nonblock
|
312
|
-
engine = Engine.server @
|
313
|
-
|
320
|
+
engine = Engine.server @eng_ctx
|
314
321
|
Socket.new io, engine
|
315
322
|
end
|
316
323
|
|
324
|
+
# @!attribute [r] to_io
|
325
|
+
def to_io
|
326
|
+
@socket
|
327
|
+
end
|
328
|
+
|
317
329
|
# @!attribute [r] addr
|
318
330
|
# @version 5.0.0
|
319
331
|
def addr
|
@@ -323,6 +335,10 @@ module Puma
|
|
323
335
|
def close
|
324
336
|
@socket.close unless @socket.closed? # closed? call is for Windows
|
325
337
|
end
|
338
|
+
|
339
|
+
def closed?
|
340
|
+
@socket.closed?
|
341
|
+
end
|
326
342
|
end
|
327
343
|
end
|
328
344
|
end
|
data/lib/puma/null_io.rb
CHANGED
@@ -9,6 +9,10 @@ module Puma
|
|
9
9
|
nil
|
10
10
|
end
|
11
11
|
|
12
|
+
def string
|
13
|
+
""
|
14
|
+
end
|
15
|
+
|
12
16
|
def each
|
13
17
|
end
|
14
18
|
|
@@ -32,6 +36,10 @@ module Puma
|
|
32
36
|
true
|
33
37
|
end
|
34
38
|
|
39
|
+
def sync
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
35
43
|
def sync=(v)
|
36
44
|
end
|
37
45
|
|
@@ -40,5 +48,9 @@ module Puma
|
|
40
48
|
|
41
49
|
def write(*ary)
|
42
50
|
end
|
51
|
+
|
52
|
+
def flush
|
53
|
+
self
|
54
|
+
end
|
43
55
|
end
|
44
56
|
end
|
data/lib/puma/plugin.rb
CHANGED
data/lib/puma/queue_close.rb
CHANGED
@@ -5,22 +5,22 @@ module Puma
|
|
5
5
|
# Add a simple implementation for earlier Ruby versions.
|
6
6
|
#
|
7
7
|
module QueueClose
|
8
|
-
def initialize
|
9
|
-
@closed = false
|
10
|
-
super
|
11
|
-
end
|
12
8
|
def close
|
9
|
+
num_waiting.times {push nil}
|
13
10
|
@closed = true
|
14
11
|
end
|
15
12
|
def closed?
|
16
|
-
@closed
|
13
|
+
@closed ||= false
|
17
14
|
end
|
18
15
|
def push(object)
|
19
|
-
|
20
|
-
raise ClosedQueueError if @closed
|
16
|
+
raise ClosedQueueError if closed?
|
21
17
|
super
|
22
18
|
end
|
23
19
|
alias << push
|
20
|
+
def pop(non_block=false)
|
21
|
+
return nil if !non_block && closed? && empty?
|
22
|
+
super
|
23
|
+
end
|
24
24
|
end
|
25
25
|
::Queue.prepend QueueClose
|
26
26
|
end
|
data/lib/puma/rack/builder.rb
CHANGED
data/lib/puma/reactor.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
require 'puma/queue_close' unless ::Queue.instance_methods.include? :close
|
4
4
|
|
5
5
|
module Puma
|
6
|
+
class UnsupportedBackend < StandardError; end
|
7
|
+
|
6
8
|
# Monitors a collection of IO objects, calling a block whenever
|
7
9
|
# any monitored object either receives data or times out, or when the Reactor shuts down.
|
8
10
|
#
|
@@ -18,9 +20,12 @@ module Puma
|
|
18
20
|
# Create a new Reactor to monitor IO objects added by #add.
|
19
21
|
# The provided block will be invoked when an IO has data available to read,
|
20
22
|
# its timeout elapses, or when the Reactor shuts down.
|
21
|
-
def initialize(&block)
|
23
|
+
def initialize(backend, &block)
|
22
24
|
require 'nio'
|
23
|
-
|
25
|
+
unless backend == :auto || NIO::Selector.backends.include?(backend)
|
26
|
+
raise "unsupported IO selector backend: #{backend} (available backends: #{NIO::Selector.backends.join(', ')})"
|
27
|
+
end
|
28
|
+
@selector = backend == :auto ? NIO::Selector.new : NIO::Selector.new(backend)
|
24
29
|
@input = Queue.new
|
25
30
|
@timeouts = []
|
26
31
|
@block = block
|
@@ -38,11 +43,11 @@ module Puma
|
|
38
43
|
end
|
39
44
|
end
|
40
45
|
|
41
|
-
# Add a new
|
46
|
+
# Add a new client to monitor.
|
42
47
|
# The object must respond to #timeout and #timeout_at.
|
43
48
|
# Returns false if the reactor is already shut down.
|
44
|
-
def add(
|
45
|
-
@input <<
|
49
|
+
def add(client)
|
50
|
+
@input << client
|
46
51
|
@selector.wakeup
|
47
52
|
true
|
48
53
|
rescue ClosedQueueError
|
@@ -92,17 +97,19 @@ module Puma
|
|
92
97
|
end
|
93
98
|
|
94
99
|
# Start monitoring the object.
|
95
|
-
def register(
|
96
|
-
@selector.register(
|
97
|
-
@timeouts <<
|
100
|
+
def register(client)
|
101
|
+
@selector.register(client.to_io, :r).value = client
|
102
|
+
@timeouts << client
|
103
|
+
rescue ArgumentError
|
104
|
+
# unreadable clients raise error when processed by NIO
|
98
105
|
end
|
99
106
|
|
100
107
|
# 'Wake up' a monitored object by calling the provided block.
|
101
108
|
# Stop monitoring the object if the block returns `true`.
|
102
|
-
def wakeup!(
|
103
|
-
if @block.call
|
104
|
-
@selector.deregister
|
105
|
-
@timeouts.delete
|
109
|
+
def wakeup!(client)
|
110
|
+
if @block.call client
|
111
|
+
@selector.deregister client.to_io
|
112
|
+
@timeouts.delete client
|
106
113
|
end
|
107
114
|
end
|
108
115
|
end
|
data/lib/puma/request.rb
CHANGED
@@ -26,11 +26,12 @@ module Puma
|
|
26
26
|
# Finally, it'll return +true+ on keep-alive connections.
|
27
27
|
# @param client [Puma::Client]
|
28
28
|
# @param lines [Puma::IOBuffer]
|
29
|
+
# @param requests [Integer]
|
29
30
|
# @return [Boolean,:async]
|
30
31
|
#
|
31
|
-
def handle_request(client, lines)
|
32
|
+
def handle_request(client, lines, requests)
|
32
33
|
env = client.env
|
33
|
-
io
|
34
|
+
io = client.io # io may be a MiniSSL::Socket
|
34
35
|
|
35
36
|
return false if closed_socket?(io)
|
36
37
|
|
@@ -50,7 +51,7 @@ module Puma
|
|
50
51
|
head = env[REQUEST_METHOD] == HEAD
|
51
52
|
|
52
53
|
env[RACK_INPUT] = body
|
53
|
-
env[RACK_URL_SCHEME]
|
54
|
+
env[RACK_URL_SCHEME] ||= default_server_port(env) == PORT_443 ? HTTPS : HTTP
|
54
55
|
|
55
56
|
if @early_hints
|
56
57
|
env[EARLY_HINTS] = lambda { |headers|
|
@@ -110,7 +111,7 @@ module Puma
|
|
110
111
|
|
111
112
|
cork_socket io
|
112
113
|
|
113
|
-
str_headers(env, status, headers, res_info, lines)
|
114
|
+
str_headers(env, status, headers, res_info, lines, requests, client)
|
114
115
|
|
115
116
|
line_ending = LINE_END
|
116
117
|
|
@@ -148,8 +149,9 @@ module Puma
|
|
148
149
|
res_body.each do |part|
|
149
150
|
next if part.bytesize.zero?
|
150
151
|
if chunked
|
151
|
-
|
152
|
-
|
152
|
+
fast_write io, (part.bytesize.to_s(16) << line_ending)
|
153
|
+
fast_write io, part # part may have different encoding
|
154
|
+
fast_write io, line_ending
|
153
155
|
else
|
154
156
|
fast_write io, part
|
155
157
|
end
|
@@ -174,7 +176,7 @@ module Puma
|
|
174
176
|
after_reply.each { |o| o.call }
|
175
177
|
end
|
176
178
|
|
177
|
-
|
179
|
+
res_info[:keep_alive]
|
178
180
|
end
|
179
181
|
|
180
182
|
# @param env [Hash] see Puma::Client#env, from request
|
@@ -199,7 +201,7 @@ module Puma
|
|
199
201
|
begin
|
200
202
|
n = io.syswrite str
|
201
203
|
rescue Errno::EAGAIN, Errno::EWOULDBLOCK
|
202
|
-
|
204
|
+
unless io.wait_writable WRITE_TIMEOUT
|
203
205
|
raise ConnectionError, "Socket timeout writing data"
|
204
206
|
end
|
205
207
|
|
@@ -230,7 +232,11 @@ module Puma
|
|
230
232
|
#
|
231
233
|
def normalize_env(env, client)
|
232
234
|
if host = env[HTTP_HOST]
|
233
|
-
|
235
|
+
# host can be a hostname, ipv4 or bracketed ipv6. Followed by an optional port.
|
236
|
+
if colon = host.rindex("]:") # IPV6 with port
|
237
|
+
env[SERVER_NAME] = host[0, colon+1]
|
238
|
+
env[SERVER_PORT] = host[colon+2, host.bytesize]
|
239
|
+
elsif !host.start_with?("[") && colon = host.index(":") # not hostname or IPV4 with port
|
234
240
|
env[SERVER_NAME] = host[0, colon]
|
235
241
|
env[SERVER_PORT] = host[colon+1, host.bytesize]
|
236
242
|
else
|
@@ -282,13 +288,20 @@ module Puma
|
|
282
288
|
end
|
283
289
|
# private :normalize_env
|
284
290
|
|
291
|
+
# @param header_key [#to_s]
|
292
|
+
# @return [Boolean]
|
293
|
+
#
|
294
|
+
def illegal_header_key?(header_key)
|
295
|
+
!!(ILLEGAL_HEADER_KEY_REGEX =~ header_key.to_s)
|
296
|
+
end
|
297
|
+
|
285
298
|
# @param header_value [#to_s]
|
286
299
|
# @return [Boolean]
|
287
300
|
#
|
288
|
-
def
|
289
|
-
!!(
|
301
|
+
def illegal_header_value?(header_value)
|
302
|
+
!!(ILLEGAL_HEADER_VALUE_REGEX =~ header_value.to_s)
|
290
303
|
end
|
291
|
-
private :
|
304
|
+
private :illegal_header_key?, :illegal_header_value?
|
292
305
|
|
293
306
|
# Fixup any headers with `,` in the name to have `_` now. We emit
|
294
307
|
# headers with `,` in them during the parse phase to avoid ambiguity
|
@@ -334,9 +347,11 @@ module Puma
|
|
334
347
|
def str_early_hints(headers)
|
335
348
|
eh_str = "HTTP/1.1 103 Early Hints\r\n".dup
|
336
349
|
headers.each_pair do |k, vs|
|
350
|
+
next if illegal_header_key?(k)
|
351
|
+
|
337
352
|
if vs.respond_to?(:to_s) && !vs.to_s.empty?
|
338
353
|
vs.to_s.split(NEWLINE).each do |v|
|
339
|
-
next if
|
354
|
+
next if illegal_header_value?(v)
|
340
355
|
eh_str << "#{k}: #{v}\r\n"
|
341
356
|
end
|
342
357
|
else
|
@@ -353,9 +368,11 @@ module Puma
|
|
353
368
|
# @param headers [Hash] the headers returned by the Rack application
|
354
369
|
# @param res_info [Hash] used to pass info between this method and #handle_request
|
355
370
|
# @param lines [Puma::IOBuffer] modified inn place
|
371
|
+
# @param requests [Integer] number of inline requests handled
|
372
|
+
# @param client [Puma::Client]
|
356
373
|
# @version 5.0.3
|
357
374
|
#
|
358
|
-
def str_headers(env, status, headers, res_info, lines)
|
375
|
+
def str_headers(env, status, headers, res_info, lines, requests, client)
|
359
376
|
line_ending = LINE_END
|
360
377
|
colon = COLON
|
361
378
|
|
@@ -396,12 +413,22 @@ module Puma
|
|
396
413
|
# if running without request queueing
|
397
414
|
res_info[:keep_alive] &&= @queue_requests
|
398
415
|
|
416
|
+
# Close the connection after a reasonable number of inline requests
|
417
|
+
# if the server is at capacity and the listener has a new connection ready.
|
418
|
+
# This allows Puma to service connections fairly when the number
|
419
|
+
# of concurrent connections exceeds the size of the threadpool.
|
420
|
+
res_info[:keep_alive] &&= requests < @max_fast_inline ||
|
421
|
+
@thread_pool.busy_threads < @max_threads ||
|
422
|
+
!client.listener.to_io.wait_readable(0)
|
423
|
+
|
399
424
|
res_info[:response_hijack] = nil
|
400
425
|
|
401
426
|
headers.each do |k, vs|
|
427
|
+
next if illegal_header_key?(k)
|
428
|
+
|
402
429
|
case k.downcase
|
403
430
|
when CONTENT_LENGTH2
|
404
|
-
next if
|
431
|
+
next if illegal_header_value?(vs)
|
405
432
|
res_info[:content_length] = vs
|
406
433
|
next
|
407
434
|
when TRANSFER_ENCODING
|
@@ -410,11 +437,13 @@ module Puma
|
|
410
437
|
when HIJACK
|
411
438
|
res_info[:response_hijack] = vs
|
412
439
|
next
|
440
|
+
when BANNED_HEADER_KEY
|
441
|
+
next
|
413
442
|
end
|
414
443
|
|
415
444
|
if vs.respond_to?(:to_s) && !vs.to_s.empty?
|
416
445
|
vs.to_s.split(NEWLINE).each do |v|
|
417
|
-
next if
|
446
|
+
next if illegal_header_value?(v)
|
418
447
|
lines.append k, colon, v, line_ending
|
419
448
|
end
|
420
449
|
else
|
data/lib/puma/runner.rb
CHANGED
@@ -15,6 +15,16 @@ module Puma
|
|
15
15
|
@app = nil
|
16
16
|
@control = nil
|
17
17
|
@started_at = Time.now
|
18
|
+
@wakeup = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def wakeup!
|
22
|
+
return unless @wakeup
|
23
|
+
|
24
|
+
@wakeup.write "!" unless @wakeup.closed?
|
25
|
+
|
26
|
+
rescue SystemCallError, IOError
|
27
|
+
Puma::Util.purge_interrupt_queue
|
18
28
|
end
|
19
29
|
|
20
30
|
def development?
|
@@ -55,11 +65,11 @@ module Puma
|
|
55
65
|
app = Puma::App::Status.new @launcher, token
|
56
66
|
|
57
67
|
control = Puma::Server.new app, @launcher.events,
|
58
|
-
{ min_threads: 0, max_threads: 1 }
|
68
|
+
{ min_threads: 0, max_threads: 1, queue_requests: false }
|
59
69
|
|
60
70
|
control.binder.parse [str], self, 'Starting control server'
|
61
71
|
|
62
|
-
control.run
|
72
|
+
control.run thread_name: 'control'
|
63
73
|
@control = control
|
64
74
|
end
|
65
75
|
|
@@ -86,9 +96,16 @@ module Puma
|
|
86
96
|
max_t = @options[:max_threads]
|
87
97
|
|
88
98
|
log "Puma starting in #{mode} mode..."
|
89
|
-
log "*
|
90
|
-
log "*
|
91
|
-
log "*
|
99
|
+
log "* Puma version: #{Puma::Const::PUMA_VERSION} (#{ruby_engine}) (\"#{Puma::Const::CODE_NAME}\")"
|
100
|
+
log "* Min threads: #{min_t}"
|
101
|
+
log "* Max threads: #{max_t}"
|
102
|
+
log "* Environment: #{ENV['RACK_ENV']}"
|
103
|
+
|
104
|
+
if mode == "cluster"
|
105
|
+
log "* Master PID: #{Process.pid}"
|
106
|
+
else
|
107
|
+
log "* PID: #{Process.pid}"
|
108
|
+
end
|
92
109
|
end
|
93
110
|
|
94
111
|
def redirected_io?
|
@@ -101,23 +118,24 @@ module Puma
|
|
101
118
|
append = @options[:redirect_append]
|
102
119
|
|
103
120
|
if stdout
|
104
|
-
|
105
|
-
raise "Cannot redirect STDOUT to #{stdout}"
|
106
|
-
end
|
121
|
+
ensure_output_directory_exists(stdout, 'STDOUT')
|
107
122
|
|
108
123
|
STDOUT.reopen stdout, (append ? "a" : "w")
|
109
|
-
STDOUT.sync = true
|
110
124
|
STDOUT.puts "=== puma startup: #{Time.now} ==="
|
125
|
+
STDOUT.flush unless STDOUT.sync
|
111
126
|
end
|
112
127
|
|
113
128
|
if stderr
|
114
|
-
|
115
|
-
raise "Cannot redirect STDERR to #{stderr}"
|
116
|
-
end
|
129
|
+
ensure_output_directory_exists(stderr, 'STDERR')
|
117
130
|
|
118
131
|
STDERR.reopen stderr, (append ? "a" : "w")
|
119
|
-
STDERR.sync = true
|
120
132
|
STDERR.puts "=== puma startup: #{Time.now} ==="
|
133
|
+
STDERR.flush unless STDERR.sync
|
134
|
+
end
|
135
|
+
|
136
|
+
if @options[:mutate_stdout_and_stderr_to_sync_on_write]
|
137
|
+
STDOUT.sync = true
|
138
|
+
STDERR.sync = true
|
121
139
|
end
|
122
140
|
end
|
123
141
|
|
@@ -147,5 +165,12 @@ module Puma
|
|
147
165
|
server.inherit_binder @launcher.binder
|
148
166
|
server
|
149
167
|
end
|
168
|
+
|
169
|
+
private
|
170
|
+
def ensure_output_directory_exists(path, io_name)
|
171
|
+
unless Dir.exist?(File.dirname(path))
|
172
|
+
raise "Cannot redirect #{io_name} to #{path}"
|
173
|
+
end
|
174
|
+
end
|
150
175
|
end
|
151
176
|
end
|