puma 6.6.0 → 7.0.1
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.
- checksums.yaml +4 -4
- data/History.md +110 -5
- data/README.md +18 -31
- data/docs/fork_worker.md +5 -5
- data/docs/kubernetes.md +6 -4
- data/docs/restart.md +2 -2
- data/docs/signals.md +11 -11
- data/docs/stats.md +2 -1
- data/docs/systemd.md +1 -1
- data/ext/puma_http11/extconf.rb +2 -17
- data/ext/puma_http11/mini_ssl.c +0 -5
- data/ext/puma_http11/org/jruby/puma/Http11.java +1 -1
- data/lib/puma/binder.rb +10 -8
- data/lib/puma/cli.rb +3 -5
- data/lib/puma/client.rb +74 -36
- data/lib/puma/cluster/worker.rb +9 -10
- data/lib/puma/cluster/worker_handle.rb +34 -5
- data/lib/puma/cluster.rb +35 -22
- data/lib/puma/commonlogger.rb +3 -3
- data/lib/puma/configuration.rb +86 -43
- data/lib/puma/const.rb +9 -10
- data/lib/puma/control_cli.rb +3 -2
- data/lib/puma/detect.rb +2 -0
- data/lib/puma/dsl.rb +110 -90
- data/lib/puma/error_logger.rb +3 -1
- data/lib/puma/events.rb +25 -10
- data/lib/puma/io_buffer.rb +8 -4
- data/lib/puma/launcher/bundle_pruner.rb +1 -1
- data/lib/puma/launcher.rb +28 -29
- data/lib/puma/minissl.rb +0 -1
- data/lib/puma/plugin/systemd.rb +3 -3
- data/lib/puma/rack/urlmap.rb +1 -1
- data/lib/puma/reactor.rb +19 -4
- data/lib/puma/request.rb +38 -31
- data/lib/puma/runner.rb +8 -17
- data/lib/puma/server.rb +80 -48
- data/lib/puma/single.rb +5 -2
- data/lib/puma/thread_pool.rb +27 -81
- data/lib/puma/util.rb +0 -7
- data/lib/puma.rb +10 -0
- data/lib/rack/handler/puma.rb +2 -2
- data/tools/Dockerfile +3 -1
- metadata +4 -4
data/lib/puma/client.rb
CHANGED
@@ -1,13 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class IO
|
4
|
-
# We need to use this for a jruby work around on both 1.8 and 1.9.
|
5
|
-
# So this either creates the constant (on 1.8), or harmlessly
|
6
|
-
# reopens it (on 1.9).
|
7
|
-
module WaitReadable
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
3
|
require_relative 'detect'
|
12
4
|
require_relative 'io_buffer'
|
13
5
|
require 'tempfile'
|
@@ -64,6 +56,11 @@ module Puma
|
|
64
56
|
|
65
57
|
TE_ERR_MSG = 'Invalid Transfer-Encoding'
|
66
58
|
|
59
|
+
# See:
|
60
|
+
# https://httpwg.org/specs/rfc9110.html#rfc.section.5.6.1.1
|
61
|
+
# https://httpwg.org/specs/rfc9112.html#rfc.section.6.1
|
62
|
+
STRIP_OWS = /\A[ \t]+|[ \t]+\z/
|
63
|
+
|
67
64
|
# The object used for a request with no body. All requests with
|
68
65
|
# no body share this one object since it has no state.
|
69
66
|
EmptyBody = NullIO.new
|
@@ -111,7 +108,8 @@ module Puma
|
|
111
108
|
end
|
112
109
|
|
113
110
|
attr_reader :env, :to_io, :body, :io, :timeout_at, :ready, :hijacked,
|
114
|
-
:tempfile, :io_buffer, :http_content_length_limit_exceeded
|
111
|
+
:tempfile, :io_buffer, :http_content_length_limit_exceeded,
|
112
|
+
:requests_served
|
115
113
|
|
116
114
|
attr_writer :peerip, :http_content_length_limit
|
117
115
|
|
@@ -133,9 +131,9 @@ module Puma
|
|
133
131
|
"#<Puma::Client:0x#{object_id.to_s(16)} @ready=#{@ready.inspect}>"
|
134
132
|
end
|
135
133
|
|
136
|
-
# For the hijack protocol
|
137
|
-
#
|
138
|
-
def
|
134
|
+
# For the full hijack protocol, `env['rack.hijack']` is set to
|
135
|
+
# `client.method :full_hijack`
|
136
|
+
def full_hijack
|
139
137
|
@hijacked = true
|
140
138
|
env[HIJACK_IO] ||= @io
|
141
139
|
end
|
@@ -150,11 +148,12 @@ module Puma
|
|
150
148
|
end
|
151
149
|
|
152
150
|
# Number of seconds until the timeout elapses.
|
151
|
+
# @!attribute [r] timeout
|
153
152
|
def timeout
|
154
153
|
[@timeout_at - Process.clock_gettime(Process::CLOCK_MONOTONIC), 0].max
|
155
154
|
end
|
156
155
|
|
157
|
-
def reset
|
156
|
+
def reset
|
158
157
|
@parser.reset
|
159
158
|
@io_buffer.reset
|
160
159
|
@read_header = true
|
@@ -166,11 +165,14 @@ module Puma
|
|
166
165
|
@peerip = nil if @remote_addr_header
|
167
166
|
@in_last_chunk = false
|
168
167
|
@http_content_length_limit_exceeded = false
|
168
|
+
end
|
169
169
|
|
170
|
+
# only used with back-to-back requests contained in the buffer
|
171
|
+
def process_back_to_back_requests
|
170
172
|
if @buffer
|
171
173
|
return false unless try_to_parse_proxy_protocol
|
172
174
|
|
173
|
-
@parsed_bytes =
|
175
|
+
@parsed_bytes = parser_execute
|
174
176
|
|
175
177
|
if @parser.finished?
|
176
178
|
return setup_body
|
@@ -178,25 +180,20 @@ module Puma
|
|
178
180
|
raise HttpParserError,
|
179
181
|
"HEADER is longer than allowed, aborting client early."
|
180
182
|
end
|
181
|
-
|
182
|
-
return false
|
183
|
-
else
|
184
|
-
begin
|
185
|
-
if fast_check && @to_io.wait_readable(FAST_TRACK_KA_TIMEOUT)
|
186
|
-
return try_to_finish
|
187
|
-
end
|
188
|
-
rescue IOError
|
189
|
-
# swallow it
|
190
|
-
end
|
191
183
|
end
|
192
184
|
end
|
193
185
|
|
186
|
+
# if a client sends back-to-back requests, the buffer may contain one or more
|
187
|
+
# of them.
|
188
|
+
def has_back_to_back_requests?
|
189
|
+
!(@buffer.nil? || @buffer.empty?)
|
190
|
+
end
|
191
|
+
|
194
192
|
def close
|
195
193
|
tempfile_close
|
196
194
|
begin
|
197
195
|
@io.close
|
198
196
|
rescue IOError, Errno::EBADF
|
199
|
-
Puma::Util.purge_interrupt_queue
|
200
197
|
end
|
201
198
|
end
|
202
199
|
|
@@ -273,26 +270,28 @@ module Puma
|
|
273
270
|
|
274
271
|
return false unless try_to_parse_proxy_protocol
|
275
272
|
|
276
|
-
@parsed_bytes =
|
273
|
+
@parsed_bytes = parser_execute
|
277
274
|
|
278
275
|
if @parser.finished? && above_http_content_limit(@parser.body.bytesize)
|
279
276
|
@http_content_length_limit_exceeded = true
|
280
277
|
end
|
281
278
|
|
282
279
|
if @parser.finished?
|
283
|
-
|
280
|
+
setup_body
|
284
281
|
elsif @parsed_bytes >= MAX_HEADER
|
285
282
|
raise HttpParserError,
|
286
283
|
"HEADER is longer than allowed, aborting client early."
|
284
|
+
else
|
285
|
+
false
|
287
286
|
end
|
288
|
-
|
289
|
-
false
|
290
287
|
end
|
291
288
|
|
292
289
|
def eagerly_finish
|
293
290
|
return true if @ready
|
294
|
-
|
295
|
-
|
291
|
+
while @to_io.wait_readable(0) # rubocop: disable Style/WhileUntilModifier
|
292
|
+
return true if try_to_finish
|
293
|
+
end
|
294
|
+
false
|
296
295
|
end
|
297
296
|
|
298
297
|
def finish(timeout)
|
@@ -300,6 +299,44 @@ module Puma
|
|
300
299
|
@to_io.wait_readable(timeout) || timeout! until try_to_finish
|
301
300
|
end
|
302
301
|
|
302
|
+
# Wraps `@parser.execute` and adds meaningful error messages
|
303
|
+
# @return [Integer] bytes of buffer read by parser
|
304
|
+
#
|
305
|
+
def parser_execute
|
306
|
+
@parser.execute(@env, @buffer, @parsed_bytes)
|
307
|
+
rescue => e
|
308
|
+
@env[HTTP_CONNECTION] = 'close'
|
309
|
+
raise e unless HttpParserError === e && e.message.include?('non-SSL')
|
310
|
+
|
311
|
+
req, _ = @buffer.split "\r\n\r\n"
|
312
|
+
request_line, headers = req.split "\r\n", 2
|
313
|
+
|
314
|
+
# below checks for request issues and changes error message accordingly
|
315
|
+
if !@env.key? REQUEST_METHOD
|
316
|
+
if request_line.count(' ') != 2
|
317
|
+
# maybe this is an SSL connection ?
|
318
|
+
raise e
|
319
|
+
else
|
320
|
+
method = request_line[/\A[^ ]+/]
|
321
|
+
raise e, "Invalid HTTP format, parsing fails. Bad method #{method}"
|
322
|
+
end
|
323
|
+
elsif !@env.key? REQUEST_PATH
|
324
|
+
path = request_line[/\A[^ ]+ +([^ ?\r\n]+)/, 1]
|
325
|
+
raise e, "Invalid HTTP format, parsing fails. Bad path #{path}"
|
326
|
+
elsif request_line.match?(/\A[^ ]+ +[^ ?\r\n]+\?/) && !@env.key?(QUERY_STRING)
|
327
|
+
query = request_line[/\A[^ ]+ +[^? ]+\?([^ ]+)/, 1]
|
328
|
+
raise e, "Invalid HTTP format, parsing fails. Bad query #{query}"
|
329
|
+
elsif !@env.key? SERVER_PROTOCOL
|
330
|
+
# protocol is bad
|
331
|
+
text = request_line[/[^ ]*\z/]
|
332
|
+
raise HttpParserError, "Invalid HTTP format, parsing fails. Bad protocol #{text}"
|
333
|
+
elsif !headers.empty?
|
334
|
+
# headers are bad
|
335
|
+
hdrs = headers.split("\r\n").map { |h| h.gsub "\n", '\n'}.join "\n"
|
336
|
+
raise HttpParserError, "Invalid HTTP format, parsing fails. Bad headers\n#{hdrs}"
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
303
340
|
def timeout!
|
304
341
|
write_error(408) if in_data_phase
|
305
342
|
raise ConnectionError
|
@@ -374,17 +411,18 @@ module Puma
|
|
374
411
|
if te
|
375
412
|
te_lwr = te.downcase
|
376
413
|
if te.include? ','
|
377
|
-
te_ary = te_lwr.split
|
414
|
+
te_ary = te_lwr.split(',').each { |te| te.gsub!(STRIP_OWS, "") }
|
378
415
|
te_count = te_ary.count CHUNKED
|
379
416
|
te_valid = te_ary[0..-2].all? { |e| ALLOWED_TRANSFER_ENCODING.include? e }
|
380
|
-
if
|
381
|
-
@env.delete TRANSFER_ENCODING2
|
382
|
-
return setup_chunked_body body
|
383
|
-
elsif te_count >= 1
|
417
|
+
if te_count > 1
|
384
418
|
raise HttpParserError , "#{TE_ERR_MSG}, multiple chunked: '#{te}'"
|
419
|
+
elsif te_ary.last != CHUNKED
|
420
|
+
raise HttpParserError , "#{TE_ERR_MSG}, last value must be chunked: '#{te}'"
|
385
421
|
elsif !te_valid
|
386
422
|
raise HttpParserError501, "#{TE_ERR_MSG}, unknown value: '#{te}'"
|
387
423
|
end
|
424
|
+
@env.delete TRANSFER_ENCODING2
|
425
|
+
return setup_chunked_body body
|
388
426
|
elsif te_lwr == CHUNKED
|
389
427
|
@env.delete TRANSFER_ENCODING2
|
390
428
|
return setup_chunked_body body
|
data/lib/puma/cluster/worker.rb
CHANGED
@@ -110,7 +110,6 @@ module Puma
|
|
110
110
|
begin
|
111
111
|
@worker_write << "#{PIPE_BOOT}#{Process.pid}:#{index}\n"
|
112
112
|
rescue SystemCallError, IOError
|
113
|
-
Puma::Util.purge_interrupt_queue
|
114
113
|
STDERR.puts "Master seems to have exited, exiting."
|
115
114
|
return
|
116
115
|
end
|
@@ -128,16 +127,16 @@ module Puma
|
|
128
127
|
|
129
128
|
while true
|
130
129
|
begin
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
io << payload
|
130
|
+
payload = base_payload.dup
|
131
|
+
|
132
|
+
hsh = server.stats
|
133
|
+
hsh.each do |k, v|
|
134
|
+
payload << %Q! "#{k}":#{v || 0},!
|
135
|
+
end
|
136
|
+
# sub call properly adds 'closing' string
|
137
|
+
io << payload.sub(/,\z/, " }\n")
|
138
|
+
server.reset_max
|
139
139
|
rescue IOError
|
140
|
-
Puma::Util.purge_interrupt_queue
|
141
140
|
break
|
142
141
|
end
|
143
142
|
sleep @options[:worker_check_interval]
|
@@ -4,13 +4,15 @@ module Puma
|
|
4
4
|
class Cluster < Runner
|
5
5
|
#—————————————————————— DO NOT USE — this class is for internal use only ———
|
6
6
|
|
7
|
-
|
8
7
|
# This class represents a worker process from the perspective of the puma
|
9
8
|
# master process. It contains information about the process and its health
|
10
9
|
# and it exposes methods to control the process via IPC. It does not
|
11
10
|
# include the actual logic executed by the worker process itself. For that,
|
12
11
|
# see Puma::Cluster::Worker.
|
13
12
|
class WorkerHandle # :nodoc:
|
13
|
+
# array of stat 'max' keys
|
14
|
+
WORKER_MAX_KEYS = [:backlog_max, :reactor_max]
|
15
|
+
|
14
16
|
def initialize(idx, pid, phase, options)
|
15
17
|
@index = idx
|
16
18
|
@pid = pid
|
@@ -23,6 +25,7 @@ module Puma
|
|
23
25
|
@last_checkin = Time.now
|
24
26
|
@last_status = {}
|
25
27
|
@term = false
|
28
|
+
@worker_max = Array.new WORKER_MAX_KEYS.length, 0
|
26
29
|
end
|
27
30
|
|
28
31
|
attr_reader :index, :pid, :phase, :signal, :last_checkin, :last_status, :started_at
|
@@ -51,12 +54,38 @@ module Puma
|
|
51
54
|
@term
|
52
55
|
end
|
53
56
|
|
54
|
-
STATUS_PATTERN = /{ "backlog":(?<backlog>\d*), "running":(?<running>\d*), "pool_capacity":(?<pool_capacity>\d*), "max_threads":(?<max_threads>\d*), "requests_count":(?<requests_count>\d*), "busy_threads":(?<busy_threads>\d*) }/
|
55
|
-
private_constant :STATUS_PATTERN
|
56
|
-
|
57
57
|
def ping!(status)
|
58
|
+
hsh = {}
|
59
|
+
k, v = nil, nil
|
60
|
+
status.tr('}{"', '').strip.split(", ") do |kv|
|
61
|
+
cntr = 0
|
62
|
+
kv.split(':') do |t|
|
63
|
+
if cntr == 0
|
64
|
+
k = t
|
65
|
+
cntr = 1
|
66
|
+
else
|
67
|
+
v = t
|
68
|
+
end
|
69
|
+
end
|
70
|
+
hsh[k.to_sym] = v.to_i
|
71
|
+
end
|
72
|
+
|
73
|
+
# check stat max values, we can't signal workers to reset the max values,
|
74
|
+
# so we do so here
|
75
|
+
WORKER_MAX_KEYS.each_with_index do |key, idx|
|
76
|
+
if hsh[key] < @worker_max[idx]
|
77
|
+
hsh[key] = @worker_max[idx]
|
78
|
+
else
|
79
|
+
@worker_max[idx] = hsh[key]
|
80
|
+
end
|
81
|
+
end
|
58
82
|
@last_checkin = Time.now
|
59
|
-
@last_status =
|
83
|
+
@last_status = hsh
|
84
|
+
end
|
85
|
+
|
86
|
+
# Resets max values to zero. Called whenever `Cluster#stats` is called
|
87
|
+
def reset_max
|
88
|
+
WORKER_MAX_KEYS.length.times { |idx| @worker_max[idx] = 0 }
|
60
89
|
end
|
61
90
|
|
62
91
|
# @see Puma::Cluster#check_workers
|
data/lib/puma/cluster.rb
CHANGED
@@ -22,6 +22,7 @@ module Puma
|
|
22
22
|
@workers = []
|
23
23
|
@next_check = Time.now
|
24
24
|
|
25
|
+
@worker_max = [] # keeps track of 'max' stat values
|
25
26
|
@phased_restart = false
|
26
27
|
end
|
27
28
|
|
@@ -44,10 +45,14 @@ module Puma
|
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
47
|
-
def start_phased_restart
|
48
|
-
@events.
|
48
|
+
def start_phased_restart(refork = false)
|
49
|
+
@events.fire_before_restart!
|
49
50
|
@phase += 1
|
50
|
-
|
51
|
+
if refork
|
52
|
+
log "- Starting worker refork, phase: #{@phase}"
|
53
|
+
else
|
54
|
+
log "- Starting phased worker restart, phase: #{@phase}"
|
55
|
+
end
|
51
56
|
|
52
57
|
# Be sure to change the directory again before loading
|
53
58
|
# the app. This way we can pick up new code.
|
@@ -166,7 +171,7 @@ module Puma
|
|
166
171
|
(@workers.map(&:pid) - idle_timed_out_worker_pids).empty?
|
167
172
|
end
|
168
173
|
|
169
|
-
def check_workers
|
174
|
+
def check_workers(refork = false)
|
170
175
|
return if @next_check >= Time.now
|
171
176
|
|
172
177
|
@next_check = Time.now + @options[:worker_check_interval]
|
@@ -184,7 +189,12 @@ module Puma
|
|
184
189
|
w = @workers.find { |x| x.phase != @phase }
|
185
190
|
|
186
191
|
if w
|
187
|
-
|
192
|
+
if refork
|
193
|
+
log "- Stopping #{w.pid} for refork..."
|
194
|
+
else
|
195
|
+
log "- Stopping #{w.pid} for phased upgrade..."
|
196
|
+
end
|
197
|
+
|
188
198
|
unless w.term?
|
189
199
|
w.term
|
190
200
|
log "- #{w.signal} sent to #{w.pid}..."
|
@@ -228,7 +238,7 @@ module Puma
|
|
228
238
|
def phased_restart(refork = false)
|
229
239
|
return false if @options[:preload_app] && !refork
|
230
240
|
|
231
|
-
@phased_restart = true
|
241
|
+
@phased_restart = refork ? :refork : true
|
232
242
|
wakeup!
|
233
243
|
|
234
244
|
true
|
@@ -258,11 +268,14 @@ module Puma
|
|
258
268
|
end
|
259
269
|
|
260
270
|
# Inside of a child process, this will return all zeroes, as @workers is only populated in
|
261
|
-
# the master process.
|
271
|
+
# the master process. Calling this also resets stat 'max' values to zero.
|
262
272
|
# @!attribute [r] stats
|
273
|
+
# @return [Hash]
|
274
|
+
|
263
275
|
def stats
|
264
276
|
old_worker_count = @workers.count { |w| w.phase != @phase }
|
265
277
|
worker_status = @workers.map do |w|
|
278
|
+
w.reset_max
|
266
279
|
{
|
267
280
|
started_at: utc_iso8601(w.started_at),
|
268
281
|
pid: w.pid,
|
@@ -273,7 +286,6 @@ module Puma
|
|
273
286
|
last_status: w.last_status,
|
274
287
|
}
|
275
288
|
end
|
276
|
-
|
277
289
|
{
|
278
290
|
started_at: utc_iso8601(@started_at),
|
279
291
|
workers: @workers.size,
|
@@ -342,7 +354,7 @@ module Puma
|
|
342
354
|
|
343
355
|
stop_workers
|
344
356
|
stop
|
345
|
-
@events.
|
357
|
+
@events.fire_after_stopped!
|
346
358
|
raise(SignalException, "SIGTERM") if @options[:raise_exception_on_sigterm]
|
347
359
|
exit 0 # Clean exit, workers were stopped
|
348
360
|
end
|
@@ -359,16 +371,12 @@ module Puma
|
|
359
371
|
|
360
372
|
if preload?
|
361
373
|
# Threads explicitly marked as fork safe will be ignored. Used in Rails,
|
362
|
-
# but may be used by anyone.
|
363
|
-
|
364
|
-
# where calling thread_variable_get on a Process::Waiter will segfault.
|
365
|
-
# We can drop that clause once those versions of Ruby are no longer
|
366
|
-
# supported.
|
367
|
-
fork_safe = ->(t) { !t.is_a?(Process::Waiter) && t.thread_variable_get(:fork_safe) }
|
374
|
+
# but may be used by anyone.
|
375
|
+
fork_safe = ->(t) { t.thread_variable_get(:fork_safe) }
|
368
376
|
|
369
377
|
before = Thread.list.reject(&fork_safe)
|
370
378
|
|
371
|
-
log "* Restarts: (\u2714) hot (\u2716) phased"
|
379
|
+
log "* Restarts: (\u2714) hot (\u2716) phased (#{@options[:fork_worker] ? "\u2714" : "\u2716"}) refork"
|
372
380
|
log "* Preloading application"
|
373
381
|
load_and_bind
|
374
382
|
|
@@ -386,7 +394,7 @@ module Puma
|
|
386
394
|
end
|
387
395
|
end
|
388
396
|
else
|
389
|
-
log "* Restarts: (\u2714) hot (\u2714) phased"
|
397
|
+
log "* Restarts: (\u2714) hot (\u2714) phased (#{@options[:fork_worker] ? "\u2714" : "\u2716"}) refork"
|
390
398
|
|
391
399
|
unless @config.app_configured?
|
392
400
|
error "No application configured, nothing to run"
|
@@ -413,6 +421,7 @@ module Puma
|
|
413
421
|
|
414
422
|
log "Use Ctrl-C to stop"
|
415
423
|
|
424
|
+
warn_ruby_mn_threads
|
416
425
|
single_worker_warning
|
417
426
|
|
418
427
|
redirect_io
|
@@ -448,13 +457,17 @@ module Puma
|
|
448
457
|
end
|
449
458
|
|
450
459
|
if @phased_restart
|
451
|
-
start_phased_restart
|
460
|
+
start_phased_restart(@phased_restart == :refork)
|
461
|
+
|
462
|
+
in_phased_restart = @phased_restart
|
452
463
|
@phased_restart = false
|
453
|
-
|
464
|
+
|
454
465
|
workers_not_booted = @options[:workers]
|
466
|
+
# worker 0 is not restarted on refork
|
467
|
+
workers_not_booted -= 1 if in_phased_restart == :refork
|
455
468
|
end
|
456
469
|
|
457
|
-
check_workers
|
470
|
+
check_workers(in_phased_restart == :refork)
|
458
471
|
|
459
472
|
if read.wait_readable([0, @next_check - Time.now].max)
|
460
473
|
req = read.read_nonblock(1)
|
@@ -497,7 +510,7 @@ module Puma
|
|
497
510
|
end
|
498
511
|
|
499
512
|
if !booted && @workers.none? {|worker| worker.last_status.empty?}
|
500
|
-
@events.
|
513
|
+
@events.fire_after_booted!
|
501
514
|
debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
|
502
515
|
booted = true
|
503
516
|
end
|
@@ -514,7 +527,7 @@ module Puma
|
|
514
527
|
end
|
515
528
|
|
516
529
|
if in_phased_restart && workers_not_booted.zero?
|
517
|
-
@events.
|
530
|
+
@events.fire_after_booted!
|
518
531
|
debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
|
519
532
|
in_phased_restart = false
|
520
533
|
end
|
data/lib/puma/commonlogger.rb
CHANGED
@@ -29,13 +29,13 @@ module Puma
|
|
29
29
|
|
30
30
|
CONTENT_LENGTH = 'Content-Length' # should be lower case from app,
|
31
31
|
# Util::HeaderHash allows mixed
|
32
|
-
HTTP_VERSION = Const::HTTP_VERSION
|
33
32
|
HTTP_X_FORWARDED_FOR = Const::HTTP_X_FORWARDED_FOR
|
34
33
|
PATH_INFO = Const::PATH_INFO
|
35
34
|
QUERY_STRING = Const::QUERY_STRING
|
36
35
|
REMOTE_ADDR = Const::REMOTE_ADDR
|
37
36
|
REMOTE_USER = 'REMOTE_USER'
|
38
37
|
REQUEST_METHOD = Const::REQUEST_METHOD
|
38
|
+
SERVER_PROTOCOL = Const::SERVER_PROTOCOL
|
39
39
|
|
40
40
|
def initialize(app, logger=nil)
|
41
41
|
@app = app
|
@@ -70,7 +70,7 @@ module Puma
|
|
70
70
|
env[REQUEST_METHOD],
|
71
71
|
env[PATH_INFO],
|
72
72
|
env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
|
73
|
-
env[
|
73
|
+
env[SERVER_PROTOCOL],
|
74
74
|
now - began_at ]
|
75
75
|
|
76
76
|
write(msg)
|
@@ -87,7 +87,7 @@ module Puma
|
|
87
87
|
env[REQUEST_METHOD],
|
88
88
|
env[PATH_INFO],
|
89
89
|
env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
|
90
|
-
env[
|
90
|
+
env[SERVER_PROTOCOL],
|
91
91
|
status.to_s[0..3],
|
92
92
|
length,
|
93
93
|
now - began_at ]
|