Skip to content

Commit

Permalink
Calculate the Allow header from routes
Browse files Browse the repository at this point in the history
The Allow header is also used to determine the
Access-Control-Allow-Methods header value.
  • Loading branch information
jdesrosiers committed Jun 29, 2017
1 parent 811b33b commit 8e9cff7
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 25 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,4 @@ Settings

Comming Soon
------------
* Automatically determine `Allow` headers based on existing routes
* Route specific settings
35 changes: 23 additions & 12 deletions lib/sinatra/cors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ def cors
logger.warn bad_method_message
return
end

unless headers_are_allowed?
logger.warn bad_headers_message
return
Expand Down Expand Up @@ -41,16 +42,15 @@ def is_preflight_request?
end

def method_is_allowed?
allow_methods = settings.allow_methods || response.headers["Allow"] || ""
request_method = request.env["HTTP_ACCESS_CONTROL_REQUEST_METHOD"] || ""
allow_methods.split.include? request_method
allow_methods = settings.allow_methods.split & response.headers["Allow"].split
request_method = request.env["HTTP_ACCESS_CONTROL_REQUEST_METHOD"]
allow_methods.include? request_method
end

def headers_are_allowed?
allow_headers = settings.allow_headers || ""
allow_headers = settings.allow_headers
request_headers = request.env["HTTP_ACCESS_CONTROL_REQUEST_HEADERS"] || ""
diff = request_headers.split - allow_headers.split
diff.size == 0
(request_headers.split - allow_headers.split).empty?
end

def origin_is_allowed?
Expand Down Expand Up @@ -81,19 +81,30 @@ def bad_origin_message
def self.registered(app)
app.helpers Cors::Helpers

app.disable :allow_origin
app.disable :allow_methods
app.disable :allow_headers
app.set :allow_origin, ""
app.set :allow_methods, ""
app.set :allow_headers, ""
app.disable :max_age
app.disable :expose_headers
app.disable :allow_credentials

app.set(:is_cors_preflight) { |bool|
app.set(:is_cors_preflight) do |bool|
condition { is_cors_request? && is_preflight_request? == bool }
}
end

app.options "*", is_cors_preflight: true do
response.headers["Allow"] = settings.allow_methods || ""
matches = []
settings.routes.each do |method, routes|
routes.each do |route|
process_route(route[0], route[1], route[2]) do |application, pattern|
matches << method
end
end
end

pass if matches.size == 1

response.headers["Allow"] = matches.join " "
end

app.after do
Expand Down
2 changes: 1 addition & 1 deletion sinatra-cors.gemspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Gem::Specification.new do |s|
s.name = "sinatra-cors"
s.version = "0.1.1"
s.version = "0.2.0"
s.date = "2017-06-28"
s.summary = "CORS support for Sinatra applications"
s.description = <<-EOT
Expand Down
31 changes: 21 additions & 10 deletions spec/cors_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,18 @@ def app

describe "A non-CORS OPTIONS request" do
it "should not be handled" do
options "/foo"
options "/foo/1"
expect(last_response).to be_not_found
end
end

describe "A CORS preflight request for a route that doesn't exist" do
it "should 404" do
rack_env = {
"HTTP_ORIGIN" => "http://example.com",
"HTTP_ACCESS_CONTROL_REQUEST_METHOD" => "GET",
}
options "/baz/1", {}, rack_env
expect(last_response).to be_not_found
end
end
Expand All @@ -24,7 +35,7 @@ def app
"HTTP_ORIGIN" => "http://example.com",
"HTTP_ACCESS_CONTROL_REQUEST_METHOD" => "DELETE",
}
options "/foo", {}, rack_env
options "/foo/1", {}, rack_env
end

it "should not return an Access-Control-Allow-Origin header" do
Expand All @@ -43,15 +54,15 @@ def app
"HTTP_ACCESS_CONTROL_REQUEST_METHOD" => "GET",
"HTTP_ACCESS_CONTROL_REQUEST_HEADERS" => "if-modified-since"
}
options "/foo", {}, rack_env
options "/foo/1", {}, rack_env
end

it "should be handled for all routes" do
expect(last_response).to be_ok
end

it "should have an allow header that matches the :allow_methods setting" do
expect(last_response["Allow"]).to eq("GET HEAD POST")
it "should have an Allow header build from existing routes" do
expect(last_response["Allow"]).to eq("GET HEAD DELETE OPTIONS")
end

it "should have an Access-Control-Allow-Methods header that includes only the method requested" do
Expand Down Expand Up @@ -79,7 +90,7 @@ def app
"HTTP_ORIGIN" => "http://example.com",
"HTTP_ACCESS_CONTROL_REQUEST_METHOD" => "GET",
}
options "/foo", {}, rack_env
options "/foo/1", {}, rack_env

expect(last_response["Access-Control-Max-Age"]).to eq("600")
end
Expand All @@ -90,7 +101,7 @@ def app
rack_env = {
"HTTP_ORIGIN" => "http://example.com",
}
get "/foo", {}, rack_env
get "/foo/1", {}, rack_env
end

it "should have an Access-Control-Allow-Origin header that includes only the origin of the request" do
Expand All @@ -111,7 +122,7 @@ def make_request(allow_origin)
rack_env = {
"HTTP_ORIGIN" => "http://example.com",
}
get "/foo", {}, rack_env
get "/foo/1", {}, rack_env
end

it "should be 'null' if the origin is not allowed" do
Expand Down Expand Up @@ -140,7 +151,7 @@ def make_request(allow_origin)
rack_env = {
"HTTP_ORIGIN" => "http://example.com",
}
get "/foo", {}, rack_env
get "/foo/1", {}, rack_env

expect(last_response["Access-Control-Allow-Credentials"]).to eq("true")
end
Expand All @@ -156,7 +167,7 @@ def make_request(allow_origin)
rack_env = {
"HTTP_ORIGIN" => "http://example.com",
}
get "/foo", {}, rack_env
get "/foo/1", {}, rack_env

expect(last_response["Access-Control-Expose-Headers"]).to eq("location link")
end
Expand Down
10 changes: 9 additions & 1 deletion spec/fixture.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
require "sinatra"
require "./lib/sinatra/cors"

get "/foo" do
get "/foo/:id" do
"foo"
end

delete "/foo/:id" do
"foo"
end

post "/bar/:id" do
"bar"
end

register Sinatra::Cors

set :allow_origin, "http://example.com http://foo.com"
Expand Down

0 comments on commit 8e9cff7

Please sign in to comment.