Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions lib/net/http.rb
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,9 @@ class HTTPHeaderSyntaxError < StandardError; end
# Returns the flags for server the certification verification at the beginning of the SSL/TLS session.
# - {:verify_mode=}[rdoc-ref:Net::HTTP#verify_mode=]:
# Sets the flags for server the certification verification at the beginning of the SSL/TLS session.
# - {#set_debug_output}[rdoc-ref:Net::HTTP#set_debug_output]:
# Sets the output stream for debugging, which logs credentials in plain text;
# never use it in production code.
#
# === Addresses and Ports
#
Expand Down Expand Up @@ -1260,8 +1263,22 @@ def inspect
# read 2 bytes
# Conn keep-alive
#
# Debug output contains credentials, so enabling it writes a warning to
# <tt>$stderr</tt>. The warning is written at most once per thread, and is
# not written when <tt>$DEBUG</tt> is set, which already declares that the
# process is being debugged, or when +output+ is +nil+, which disables debug
# output. Like any other Ruby warning it can be intercepted by overriding
# Warning.warn.
#
def set_debug_output(output)
warn 'Net::HTTP#set_debug_output called after HTTP started', uplevel: 1 if started?
if output and !$DEBUG and !Thread.current.thread_variable_get(:net_http_debug_output_warned)
Thread.current.thread_variable_set(:net_http_debug_output_warned, true)
warn 'Net::HTTP#set_debug_output: every request and response, including ' +
'Authorization and Cookie headers and message bodies, will be written ' +
'to the given stream in plain text; never enable this in production',
uplevel: 1
end
@debug_output = output
end

Expand Down
88 changes: 88 additions & 0 deletions test/net/http/test_http.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@

class TestNetHTTP < Test::Unit::TestCase

def setup
Thread.current.thread_variable_set(:net_http_debug_output_warned, nil)
end

def test_class_Proxy
no_proxy_class = Net::HTTP.Proxy nil

Expand Down Expand Up @@ -266,6 +270,76 @@ def test_default_configuration
Net::HTTP.default_configuration = nil
end

def test_set_debug_output_warns
http = Net::HTTP.new 'hostname.example'

assert_warning(/#{__FILE__}:#{__LINE__+1}: warning: Net::HTTP#set_debug_output: /) do
http.set_debug_output StringIO.new
end
end

def test_set_debug_output_names_what_is_logged
http = Net::HTTP.new 'hostname.example'

assert_warning(/Authorization and Cookie headers and message bodies/) do
http.set_debug_output StringIO.new
end
end

def test_set_debug_output_nil_does_not_warn
http = Net::HTTP.new 'hostname.example'

assert_warning('') do
http.set_debug_output nil
end
end

def test_set_debug_output_DEBUG_does_not_warn
debug, $DEBUG = $DEBUG, true

http = Net::HTTP.new 'hostname.example'

assert_warning('') do
http.set_debug_output StringIO.new
end
ensure
$DEBUG = debug
end

def test_set_debug_output_warns_once_per_thread
http = Net::HTTP.new 'hostname.example'

assert_warning(/Net::HTTP#set_debug_output: /) do
http.set_debug_output StringIO.new
end

assert_warning('') do
http.set_debug_output StringIO.new
end
end

def test_set_debug_output_warns_once_across_objects
assert_warning(/Net::HTTP#set_debug_output: /) do
Net::HTTP.new('hostname.example').set_debug_output StringIO.new
end

assert_warning('') do
Net::HTTP.new('hostname.example').set_debug_output StringIO.new
end
end

def test_set_debug_output_warns_once_in_subclasses
assert_warning(/Net::HTTP#set_debug_output: /) do
Net::HTTP.new('hostname.example').set_debug_output StringIO.new
end

proxy = Net::HTTP::Proxy('proxy.example', 8000)

assert_warning('') do
proxy.new('hostname.example').set_debug_output StringIO.new
end
end

end

module TestNetHTTP_version_1_1_methods
Expand Down Expand Up @@ -1470,4 +1544,18 @@ def test_get
assert_equal expected, ret
RUBY
end

# The once-per-thread flag is kept in thread-local storage rather than on
# Net::HTTP, because non-main Ractors cannot set instance variables of
# classes. Recording it there would raise Ractor::IsolationError.
def test_set_debug_output
assert_ractor(<<~RUBY, require: 'net/http', ignore_stderr: true)
require 'stringio'
ret = Ractor.new {
Net::HTTP.new('hostname.example').set_debug_output(StringIO.new)
:ok
}.value
assert_equal :ok, ret
RUBY
end
end if defined?(Ractor) && Ractor.method_defined?(:value)