Skip to content

Commit

Permalink
Merge pull request #162 from gsamokovarov/content-inside-of-body
Browse files Browse the repository at this point in the history
Render the console inside the body tag
  • Loading branch information
gsamokovarov committed Sep 9, 2015
2 parents b9ce945 + a7b28cc commit 487df5a
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 154 deletions.
1 change: 1 addition & 0 deletions lib/web_console.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
require 'web_console/request'
require 'web_console/response'
require 'web_console/view'
require 'web_console/whiny_request'

module WebConsole
mattr_accessor :logger
Expand Down
19 changes: 12 additions & 7 deletions lib/web_console/middleware.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,7 @@ def initialize(app)
end

def call(env)
logger = if whiny_requests
env['action_dispatch.logger'] || WebConsole.logger
end

request = Request.new(env, logger)
request = create_regular_or_whiny_request(env)
return @app.call(env) unless request.from_whitelited_ip?

if id = id_for_repl_session_update(request)
Expand All @@ -29,15 +25,15 @@ def call(env)
end

status, headers, body = @app.call(env)
response = Response.new(body, status, headers, logger)

if exception = env['web_console.exception']
session = Session.from_exception(exception)
elsif binding = env['web_console.binding']
session = Session.from_binding(binding)
end

if session && response.acceptable_content_type?
if session && acceptable_content_type?(headers)
response = Response.new(body, status, headers)
template = Template.new(env, session)

response.headers["X-Web-Console-Session-Id"] = session.id
Expand All @@ -51,6 +47,10 @@ def call(env)

private

def acceptable_content_type?(headers)
Mime::Type.parse(headers['Content-Type']).first == Mime::HTML
end

def json_response(opts = {})
status = opts.fetch(:status, 200)
headers = { 'Content-Type' => 'application/json; charset = utf-8' }
Expand All @@ -66,6 +66,11 @@ def json_response_with_session(id, request, opts = {})
json_response(opts) { yield session }
end

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

def repl_sessions_re
@_repl_sessions_re ||= %r{#{mount_point}/repl_sessions/(?<id>[^/]+)}
end
Expand Down
21 changes: 1 addition & 20 deletions lib/web_console/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,12 @@ class Request < ActionDispatch::Request
# Define a vendor MIME type. We can call it using Mime::WEB_CONSOLE_V2 constant.
Mime::Type.register 'application/vnd.web-console.v2', :web_console_v2

def initialize(env, logger = nil)
@logger = logger
super(env)
end

# Returns whether a request came from a whitelisted IP.
#
# For a request to hit Web Console features, it needs to come from a white
# listed IP.
def from_whitelited_ip?
if whitelisted_ips.include?(strict_remote_ip)
true
else
log_whilelisted_ip_restriction
false
end
whitelisted_ips.include?(strict_remote_ip)
end

# Determines the remote IP using our much stricter whitelist.
Expand All @@ -38,15 +28,6 @@ def acceptable?

private

attr_reader :logger

def log_whilelisted_ip_restriction
if logger
logger.info "Cannot render console from #{remote_ip}! " \
"Allowed networks: #{whitelisted_ips}"
end
end

class GetSecureIp < ActionDispatch::RemoteIp::GetIp
def initialize(req, proxies)
# After rails/rails@07b2ff0 ActionDispatch::RemoteIp::GetIp initializes
Expand Down
42 changes: 14 additions & 28 deletions lib/web_console/response.rb
Original file line number Diff line number Diff line change
@@ -1,37 +1,23 @@
module WebConsole
# Web Console tailored response object.
class Response < Rack::Response
def initialize(body, status, headers, logger = nil)
@logger = logger
super(body, status, headers)
end
# A response object that writes content before the closing </body> tag, if
# possible.
#
# The object quacks like Rack::Response.
class Response < Struct.new(:body, :status, :headers)
def write(content)
raw_body = Array(body).first.to_s

# Returns whether the response is from an acceptable content type.
#
# We can render a console only for HTML responses.
def acceptable_content_type?
if content_type == Mime::HTML
true
if position = raw_body.rindex('</body>')
raw_body.insert(position, content)
else
log_not_acceptable_content_type
false
raw_body << content
end
end

def content_type
formats = Mime::Type.parse(headers['Content-Type'])
formats.first
self.body = raw_body
end

private

attr_reader :logger

def log_not_acceptable_content_type
if logger
logger.info "Cannot render console with content type " \
"#{content_type}. Console can be rendered only in HTML responses"
end
end
def finish
Rack::Response.new(body, status, headers).finish
end
end
end
31 changes: 31 additions & 0 deletions lib/web_console/whiny_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
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

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
24 changes: 14 additions & 10 deletions test/web_console/helper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,7 @@ class BaseApplication
include Helper

def call(env)
Rack::Response.new(<<-HTML.strip_heredoc, status, headers).finish
<html>
<head>
<title>Hello world</title>
</head>
<body>
<p id="hello-world">Hello world</p>
</body>
</html>
HTML
[ status, headers, body ]
end

private
Expand All @@ -31,6 +22,19 @@ def status
def headers
{ 'Content-Type' => "#{Mime::HTML}; charset=utf-8" }
end

def body
Array(<<-HTML.strip_heredoc)
<html>
<head>
<title>Hello world</title>
</head>
<body>
<p id="hello-world">Hello world</p>
</body>
</html>
HTML
end
end

class SingleConsoleApplication < BaseApplication
Expand Down
38 changes: 28 additions & 10 deletions test/web_console/middleware_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,7 @@ def initialize(options = {})
end

def call(env)
Rack::Response.new(<<-HTML.strip_heredoc, status, headers).finish
<html>
<head>
<title>Hello world</title>
</head>
<body>
<p id="hello-world">Hello world</p>
</body>
</html>
HTML
[ status, headers, body ]
end

private
Expand All @@ -29,6 +20,19 @@ def status
def headers
{ 'Content-Type' => "#{@response_content_type}; charset=utf-8" }
end

def body
Array(<<-HTML.strip_heredoc)
<html>
<head>
<title>Hello world</title>
</head>
<body>
<p id="hello-world">Hello world</p>
</body>
</html>
HTML
end
end

setup do
Expand All @@ -47,9 +51,23 @@ def headers
test 'render console in an html application from web_console.exception' do
get '/', nil, 'web_console.exception' => raise_exception

assert_select 'body > #console'
end

test 'render console if response format is HTML' do
@app = Middleware.new(Application.new(response_content_type: Mime::HTML))
get '/', nil, 'web_console.binding' => binding

assert_select '#console'
end

test 'does not render console if response format is not HTML' do
@app = Middleware.new(Application.new(response_content_type: Mime::JSON))
get '/', nil, 'web_console.binding' => binding

assert_select '#console', 0
end

test 'returns X-Web-Console-Session-Id as response header' do
get '/', nil, 'web_console.binding' => binding

Expand Down
16 changes: 0 additions & 16 deletions test/web_console/request_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,6 @@ class RequestTest < ActiveSupport::TestCase
assert_not req.from_whitelited_ip?
end

test '#from_whitelited_ip? logs out to stderr' do
assert_output_to_stderr do
req = request_with_logger('http://example.com', 'REMOTE_ADDR' => '0.0.0.0')
assert_not req.from_whitelited_ip?
end
end

test '#acceptable? is truthy for current version' do
req = xhr('http://example.com', 'HTTP_ACCEPT' => "#{Mime::WEB_CONSOLE_V2}")

Expand All @@ -67,10 +60,6 @@ def request(*args)
Request.new(mock_env(*args))
end

def request_with_logger(*args)
Request.new(mock_env(*args), WebConsole.logger)
end

def mock_env(*args)
Rack::MockRequest.env_for(*args)
end
Expand All @@ -79,10 +68,5 @@ def xhr(*args)
args[1]['HTTP_X_REQUESTED_WITH'] ||= 'XMLHttpRequest'
request(*args)
end

def assert_output_to_stderr
output = capture(:stderr) { yield }
assert_not output.blank?
end
end
end
63 changes: 0 additions & 63 deletions test/web_console/response_test.rb

This file was deleted.

Loading

0 comments on commit 487df5a

Please sign in to comment.