Skip to content

Commit

Permalink
Merge pull request #145 from sh19910711/patch/0012/accept-header
Browse files Browse the repository at this point in the history
Require versioning in accept header
  • Loading branch information
gsamokovarov committed Jul 10, 2015
2 parents e2da15d + c105af7 commit 9a376c1
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 26 deletions.
68 changes: 42 additions & 26 deletions lib/web_console/middleware.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class Middleware
this request hit doesn't store %{id} in memory.
END

UNACCEPTABLE_REQUEST_MESSAGE = "A supported version is expected in the Accept header."

cattr_accessor :whiny_requests
@@whiny_requests = true

Expand All @@ -29,9 +31,9 @@ def call(env)
return @app.call(env) unless request.from_whitelited_ip?

if id = id_for_repl_session_update(request)
return update_repl_session(id, request.params)
return update_repl_session(id, request)
elsif id = id_for_repl_session_stack_frame_change(request)
return change_stack_trace(id, request.params)
return change_stack_trace(id, request)
end

status, headers, body = @app.call(env)
Expand All @@ -56,6 +58,28 @@ def call(env)

private

def json_response(opts = {})
status = opts.fetch(:status, 200)
headers = { 'Content-Type' => 'application/json; charset = utf-8' }
body = yield.to_json

Rack::Response.new(body, status, headers).finish
end

def json_response_with_session(id, request, opts = {})
json_response(opts) do
unless request.acceptable?
return respond_with_unacceptable_request
end

unless session = Session.find(id)
return respond_with_unavailable_session(id)
end

yield session
end
end

def create_regular_or_whiny_request(env)
request = Request.new(env)
whiny_requests ? WhinyRequest.new(request) : request
Expand All @@ -81,38 +105,30 @@ def id_for_repl_session_stack_frame_change(request)
end
end

def update_repl_session(id, params)
unless session = Session.find(id)
return respond_with_unavailable_session(id)
def update_repl_session(id, request)
json_response_with_session(id, request) do |session|
{ output: session.eval(request.params[:input]) }
end

status = 200
headers = { 'Content-Type' => 'application/json; charset = utf-8' }
body = { output: session.eval(params[:input]) }.to_json

Rack::Response.new(body, status, headers).finish
end

def change_stack_trace(id, params)
unless session = Session.find(id)
return respond_with_unavailable_session(id)
end

session.switch_binding_to(params[:frame_id])
def change_stack_trace(id, request)
json_response_with_session(id, request) do |session|
session.switch_binding_to(request.params[:frame_id])

status = 200
headers = { 'Content-Type' => 'application/json; charset = utf-8' }
body = { ok: true }.to_json

Rack::Response.new(body, status, headers).finish
{ ok: true }
end
end

def respond_with_unavailable_session(id)
status = 404
headers = { 'Content-Type' => 'application/json; charset = utf-8' }
body = { output: format(UNAVAILABLE_SESSION_MESSAGE, id: id)}.to_json
json_response(status: 404) do
{ output: format(UNAVAILABLE_SESSION_MESSAGE, id: id)}
end
end

Rack::Response.new(body, status, headers).finish
def respond_with_unacceptable_request
json_response(status: 406) do
{ error: UNACCEPTABLE_REQUEST_MESSAGE }
end
end
end
end
8 changes: 8 additions & 0 deletions lib/web_console/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ class Request < ActionDispatch::Request
cattr_accessor :whitelisted_ips
@@whitelisted_ips = Whitelist.new

# 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

# Returns whether a request came from a whitelisted IP.
#
# For a request to hit Web Console features, it needs to come from a white
Expand All @@ -32,6 +35,11 @@ def acceptable_content_type?
content_type.blank? || content_type.in?(acceptable_content_types)
end

# Returns whether the request is acceptable.
def acceptable?
xhr? && accepts.any? { |mime| Mime::WEB_CONSOLE_V2 == mime }
end

class GetSecureIp < ActionDispatch::RemoteIp::GetIp
def initialize(env, proxies)
@env = env
Expand Down
1 change: 1 addition & 0 deletions lib/web_console/templates/console.js.erb
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@ function request(method, url, params, callback) {
xhr.open(method, url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.setRequestHeader("Accept", "<%= Mime::WEB_CONSOLE_V2 %>");
xhr.send(params);

xhr.onreadystatechange = function() {
Expand Down
2 changes: 2 additions & 0 deletions test/templates/config.ru
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
require "action_view"
require "pathname"
require "action_dispatch"
require "web_console"

TEST_ROOT = Pathname(__FILE__).dirname

Expand Down
14 changes: 14 additions & 0 deletions test/web_console/middleware_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,22 @@ def call(env)
assert_equal(404, response.status)
end

test "doesn't accept request for old version and reutrn 406" do
xhr :put, "/repl_sessions/no_such_session", { input: "__LINE__" },
{"HTTP_ACCEPT" => "application/vnd.web-console.v0"}

assert_equal(406, response.status)
end

private

# Override the xhr testing helper of ActionDispatch to customize http headers
def xhr(*args)
args[3] ||= {}
args[3]['HTTP_ACCEPT'] ||= "#{Mime::WEB_CONSOLE_V2}"
super
end

def raise_exception
raise
rescue => exc
Expand Down
17 changes: 17 additions & 0 deletions test/web_console/request_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,27 @@ class RequestTest < ActiveSupport::TestCase
assert_not req.acceptable_content_type?
end

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

assert req.acceptable?
end

test '#acceptable? is falsy for request without vendor mime type' do
req = xhr('http://example.com', 'HTTP_ACCEPT' => 'text/plain; charset=utf-8')

assert_not req.acceptable?
end

private

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

def xhr(*args)
args[1]['HTTP_X_REQUESTED_WITH'] ||= 'XMLHttpRequest'
request(*args)
end
end
end

0 comments on commit 9a376c1

Please sign in to comment.