Skip to content

Commit

Permalink
Add a request wrappers that logs out why a console isn't displayed
Browse files Browse the repository at this point in the history
  • Loading branch information
gsamokovarov committed Jan 30, 2015
1 parent 7b38e47 commit 005285c
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 7 deletions.
5 changes: 5 additions & 0 deletions lib/web_console.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'binding_of_caller'

require 'active_support/lazy_load_hooks'
require 'active_support/logger'

require 'web_console/core_ext/exception'
require 'web_console/engine'
Expand All @@ -13,7 +14,11 @@
require 'web_console/middleware'
require 'web_console/whitelist'
require 'web_console/request'
require 'web_console/whiny_request'

module WebConsole
mattr_accessor :logger
@@logger = ActiveSupport::Logger.new($stderr)

ActiveSupport.run_load_hooks(:web_console, self)
end
6 changes: 6 additions & 0 deletions lib/web_console/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,11 @@ def render_exception_with_web_console(env, exception)
Request.whitelisted_ips = Whitelist.new(whitelisted_ips)
end
end

initializer 'web_console.whiny_requests' do
if config.web_console.key?(:whiny_requests)
Middleware.whiny_requests = config.web_console.whiny_requests
end
end
end
end
10 changes: 9 additions & 1 deletion lib/web_console/middleware.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ class Middleware
binding_change_re: %r{/repl_sessions/(?<id>.+?)/trace\z}
}

cattr_accessor :whiny_requests
@@whiny_requests = true

def initialize(app, options = {})
@app = app
@options = DEFAULT_OPTIONS.merge(options)
end

def call(env)
request = Request.new(env)
request = create_request(env)
return @app.call(env) unless request.from_whitelited_ip?

if id = id_for_repl_session_update(request)
Expand Down Expand Up @@ -43,6 +46,11 @@ def call(env)

private

def create_request(env)
request = Request.new(env)
whiny_requests ? WhinyRequest.new(request) : request
end

def update_re
@options[:update_re]
end
Expand Down
12 changes: 7 additions & 5 deletions lib/web_console/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ module WebConsole
class Request < ActionDispatch::Request
# While most of the servers will return blank content type if none given,
# Puma will return text/plain.
ACCEPTABLE_CONTENT_TYPE = [Mime::HTML, Mime::TEXT]
cattr_accessor :acceptable_content_types
@@acceptable_content_types = [Mime::HTML, Mime::TEXT]

# Configurable whitelisted IPs.
# Configurable set of whitelisted networks.
cattr_accessor :whitelisted_ips
@@whitelisted_ips = Whitelist.new

Expand All @@ -19,10 +20,11 @@ def from_whitelited_ip?

# Returns whether the request is from an acceptable content type.
#
# We can render a console for HTML and TEXT. If a client didn't
# specified any content type, we'll render it as well.
# We can render a console for HTML and TEXT by default. If a client didn't
# specified any content type and the server returned it as blank, we'll
# render it as well.
def acceptable_content_type?
content_type.blank? || content_type.in?(ACCEPTABLE_CONTENT_TYPE)
content_type.blank? || content_type.in?(acceptable_content_types)
end
end
end
38 changes: 38 additions & 0 deletions lib/web_console/whiny_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module WebConsole
# Noisy wrapper around +Request+.
#
# If any calls to +from_whitelisted_ip?+ and +acceptable_content_type?+
# return false, an info log message will be displayed in users' logs.
class WhinyRequest < SimpleDelegator
def from_whitelited_ip?
whine_unless request.from_whitelited_ip? do
"Cannot render console from #{request.remote_ip}! " \
"Allowed networks: #{request.whitelisted_ips}"
end
end

def acceptable_content_type?
whine_unless request.acceptable_content_type? do
"Cannot render console with content type #{request.content_type}" \
"Allowed content types: #{request.acceptable_content_types}"
end
end

private

def whine_unless(condition)
unless condition
logger.info { yield }
end
condition
end

def logger
env['action_dispatch.logger'] || WebConsole.logger
end

def request
__getobj__
end
end
end
22 changes: 22 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,28 @@ def assert_select(*)
include SilenceRailsDomTesting
end

# A copy of Kernel#capture in active_support/core_ext/kernel/reporting.rb as
# its getting deprecated past 4.2. Its not thread safe, but I don't need it to
# be in the tests
def capture(stream)
stream = stream.to_s
captured_stream = Tempfile.new(stream)
stream_io = eval("$#{stream}")
origin_stream = stream_io.dup
stream_io.reopen(captured_stream)

yield

stream_io.rewind
return captured_stream.read
ensure
captured_stream.close
captured_stream.unlink
stream_io.reopen(origin_stream)
end

alias silence capture

# Load fixtures from the engine
if ActiveSupport::TestCase.method_defined?(:fixture_path=)
ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__)
Expand Down
9 changes: 9 additions & 0 deletions test/web_console/engine_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ class EngineTest < ActiveSupport::TestCase
end
end

test 'config.whiny_request removes extra logging' do
new_uninitialized_app do |app|
app.config.web_console.whiny_requests = false
app.initialize!

assert_not Middleware.whiny_requests
end
end

private

def new_uninitialized_app(root = File.expand_path('../../dummy', __FILE__))
Expand Down
4 changes: 3 additions & 1 deletion test/web_console/middleware_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ def call(env)
test "doesn't render console from non whitelisted IP" do
Request.stubs(:whitelisted_ips).returns(IPAddr.new('127.0.0.1'))

get '/', nil, 'CONTENT_TYPE' => 'text/html', 'REMOTE_ADDR' => '1.1.1.1', 'web-console.binding' => binding
silence(:stderr) do
get '/', nil, 'CONTENT_TYPE' => 'text/html', 'REMOTE_ADDR' => '1.1.1.1', 'web-console.binding' => binding
end

assert_select '#console', 0
end
Expand Down
33 changes: 33 additions & 0 deletions test/web_console/whiny_request_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
require 'test_helper'

module WebConsole
class WhinyRequestTest < ActiveSupport::TestCase
test '#from_whitelited_ip? logs out to stderr' do
Request.stubs(:whitelisted_ips).returns(IPAddr.new('127.0.0.1'))
assert_output_to_stderr do
req = request('http://example.com', 'REMOTE_ADDR' => '0.0.0.0')
assert_not req.from_whitelited_ip?
end
end

test '#acceptable_content_type? logs out to stderr' do
Request.stubs(:acceptable_content_types).returns([])
assert_output_to_stderr do
req = request('http://example.com', 'CONTENT_TYPE' => 'application/json')
assert_not req.acceptable_content_type?
end
end

private

def assert_output_to_stderr
output = capture(:stderr) { yield }
assert_not output.blank?
end

def request(*args)
request = Request.new(Rack::MockRequest.env_for(*args))
WhinyRequest.new(request)
end
end
end

0 comments on commit 005285c

Please sign in to comment.