Avoid mutating stdout and stderr (#2486)

Uses `flush` after every write if the stdio it is not synchronized.

Closes: https://github.com/puma/puma/issues/1948

Co-authored-by: Nate Berkopec <nate.berkopec@gmail.com>
This commit is contained in:
Carlos Castellanos Vera 2020-12-02 18:43:53 +01:00 committed by GitHub
parent 50185aec02
commit a284179d9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 34 additions and 8 deletions

View File

@ -2,6 +2,7 @@
* Features
* Your feature goes here <Most recent on the top, like GitHub> (#Github Number)
* Uses `flush` after writing messages to avoid mutating $stdout and $stderr using `sync=true` ([#2486])
* Fail build if compiling extensions raises warnings on GH Actions ([#1953])
* Add MAKE_WARNINGS_INTO_ERRORS environment variable to toggle whether a build should treat all warnings into errors or not ([#1953])

View File

@ -237,6 +237,7 @@ module Puma
if sig.nil?
@stdout.puts "'#{@command}' not available via pid only"
@stdout.flush unless @stdout.sync
return
elsif sig.start_with? 'SIG'
Process.kill sig, @pid
@ -244,6 +245,7 @@ module Puma
begin
Process.kill 0, @pid
@stdout.puts 'Puma is started'
@stdout.flush unless @stdout.sync
rescue Errno::ESRCH
raise 'Puma is not running'
end

View File

@ -15,7 +15,6 @@ module Puma
def initialize(ioerr)
@ioerr = ioerr
@ioerr.sync = true
@debug = ENV.key? 'PUMA_DEBUG'
end
@ -32,7 +31,7 @@ module Puma
# and before all remaining info.
#
def info(options={})
ioerr.puts title(options)
log title(options)
end
# Print occured error details only if
@ -54,7 +53,7 @@ module Puma
string_block << request_dump(req) if request_parsed?(req)
string_block << error.backtrace if error
ioerr.puts string_block.join("\n")
log string_block.join("\n")
end
def title(options={})
@ -93,5 +92,13 @@ module Puma
def request_parsed?(req)
req && req.env[REQUEST_METHOD]
end
private
def log(str)
ioerr.puts str
ioerr.flush unless ioerr.sync
end
end
end

View File

@ -30,9 +30,6 @@ module Puma
@stdout = stdout
@stderr = stderr
@stdout.sync = true
@stderr.sync = true
@debug = ENV.key? 'PUMA_DEBUG'
@error_logger = ErrorLogger.new(@stderr)
@ -66,6 +63,8 @@ module Puma
#
def log(str)
@stdout.puts format(str) if @stdout.respond_to? :puts
@stdout.flush unless @stdout.sync
rescue Errno::EPIPE
end

View File

@ -113,8 +113,8 @@ module Puma
end
STDOUT.reopen stdout, (append ? "a" : "w")
STDOUT.sync = true
STDOUT.puts "=== puma startup: #{Time.now} ==="
STDOUT.flush unless STDOUT.sync
end
if stderr
@ -123,8 +123,8 @@ module Puma
end
STDERR.reopen stderr, (append ? "a" : "w")
STDERR.sync = true
STDERR.puts "=== puma startup: #{Time.now} ==="
STDERR.flush unless STDERR.sync
end
end

View File

@ -10,6 +10,14 @@ class TestErrorLogger < Minitest::Test
assert_equal STDERR, error_logger.ioerr
end
def test_stdio_respects_sync
error_logger = Puma::ErrorLogger.stdio
assert_equal STDERR.sync, error_logger.ioerr.sync
assert_equal STDERR, error_logger.ioerr
end
def test_info_with_only_error
_, err = capture_io do
Puma::ErrorLogger.stdio.info(error: StandardError.new('ready'))

View File

@ -24,6 +24,15 @@ class TestEvents < Minitest::Test
assert_equal STDERR, events.stderr
end
def test_stdio_respects_sync
events = Puma::Events.stdio
assert_equal STDOUT.sync, events.stdout.sync
assert_equal STDERR.sync, events.stderr.sync
assert_equal STDOUT, events.stdout
assert_equal STDERR, events.stderr
end
def test_register_callback_with_block
res = false