rack-cors 0.2.2 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rack-cors might be problematic. Click here for more details.
- data/.gitignore +1 -0
- data/README.rdoc +14 -10
- data/Rakefile +17 -0
- data/VERSION +1 -1
- data/lib/rack/cors.rb +26 -12
- data/rack-cors.gemspec +9 -3
- data/test/cors_test.rb +49 -15
- data/test/dsl_test.rb +32 -0
- data/test/test.ru +13 -11
- metadata +22 -7
data/.gitignore
CHANGED
data/README.rdoc
CHANGED
@@ -23,18 +23,22 @@ In your Gemfile:
|
|
23
23
|
|
24
24
|
You configure Rack::Cors by passing a block to the <tt>use</tt> command:
|
25
25
|
|
26
|
-
use Rack::Cors do
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
26
|
+
use Rack::Cors do
|
27
|
+
allow do
|
28
|
+
origins 'localhost:3000', '127.0.0.1:3000',
|
29
|
+
/http:\/\/192\.168\.0\.\d{1,3}(:\d+)?/
|
30
|
+
# regular expressions can be used here
|
31
|
+
|
32
|
+
resource '/file/list_all/', :headers => 'x-domain-token'
|
33
|
+
resource '/file/at/*',
|
32
34
|
:methods => [:get, :post, :put, :delete],
|
33
|
-
:headers => 'x-domain-token'
|
35
|
+
:headers => 'x-domain-token',
|
36
|
+
:expose => ['Some-Custom-Response-Header']
|
37
|
+
# headers to expose
|
34
38
|
end
|
35
39
|
|
36
|
-
|
37
|
-
|
38
|
-
|
40
|
+
allow do
|
41
|
+
origins '*'
|
42
|
+
resource '/public/*', :headers => :any, :methods => :get
|
39
43
|
end
|
40
44
|
end
|
data/Rakefile
CHANGED
@@ -1,13 +1,30 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
desc 'Run tests'
|
5
|
+
Rake::TestTask.new(:test) do |t|
|
6
|
+
t.pattern = 'test/*_test.rb'
|
7
|
+
t.verbose = true
|
8
|
+
t.warning = true
|
9
|
+
end
|
10
|
+
|
11
|
+
# ==================================================
|
12
|
+
# JEWELER TASKS
|
13
|
+
# ==================================================
|
1
14
|
begin
|
2
15
|
require 'jeweler'
|
3
16
|
Jeweler::Tasks.new do |gemspec|
|
4
17
|
gemspec.name = "rack-cors"
|
5
18
|
gemspec.summary = "Middleware for enabling Cross-Origin Resource Sharing in Rack apps"
|
19
|
+
gemspec.description = "Middleware that will make Rack-based apps CORS compatible. Read more here: http://blog.sourcebender.com/2010/06/09/introducin-rack-cors.html. Fork the project here: http://github.com/cyu/rack-cors"
|
6
20
|
gemspec.email = "csyu77@gmail.com"
|
7
21
|
gemspec.homepage = "http://github.com/cyu/rack-cors"
|
8
22
|
gemspec.authors = ["Calvin Yu"]
|
23
|
+
gemspec.add_dependency 'rack'
|
24
|
+
gemspec.files.exclude 'Gemfile'
|
9
25
|
end
|
10
26
|
Jeweler::GemcutterTasks.new
|
11
27
|
rescue LoadError
|
12
28
|
puts "Jeweler not available. Install it with: gem install jeweler"
|
13
29
|
end
|
30
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.4
|
data/lib/rack/cors.rb
CHANGED
@@ -2,18 +2,31 @@ require 'logger'
|
|
2
2
|
|
3
3
|
module Rack
|
4
4
|
class Cors
|
5
|
-
def initialize(app, opts={})
|
5
|
+
def initialize(app, opts={}, &block)
|
6
6
|
@app = app
|
7
7
|
@logger = opts[:logger]
|
8
|
-
|
8
|
+
|
9
|
+
if block.arity == 1
|
10
|
+
block.call(self)
|
11
|
+
else
|
12
|
+
instance_eval(&block)
|
13
|
+
end
|
9
14
|
end
|
10
15
|
|
11
|
-
def allow
|
16
|
+
def allow(&block)
|
12
17
|
all_resources << (resources = Resources.new)
|
13
|
-
|
18
|
+
|
19
|
+
if block.arity == 1
|
20
|
+
block.call(resources)
|
21
|
+
else
|
22
|
+
resources.instance_eval(&block)
|
23
|
+
end
|
14
24
|
end
|
15
25
|
|
16
26
|
def call(env)
|
27
|
+
env['HTTP_ORIGIN'] = 'file://' if env['HTTP_ORIGIN'] == 'null'
|
28
|
+
env['HTTP_ORIGIN'] ||= env['HTTP_X_ORIGIN']
|
29
|
+
|
17
30
|
cors_headers = nil
|
18
31
|
if env['HTTP_ORIGIN']
|
19
32
|
debug(env) do
|
@@ -77,12 +90,9 @@ module Rack
|
|
77
90
|
def origins(*args)
|
78
91
|
@origins = args.flatten.collect do |n|
|
79
92
|
case n
|
80
|
-
when /^https?:\/\// then n
|
81
|
-
when '*'
|
82
|
-
|
83
|
-
n
|
84
|
-
else
|
85
|
-
"http://#{n}"
|
93
|
+
when Regexp, /^https?:\/\// then n
|
94
|
+
when '*' then @public_resources = true; n
|
95
|
+
else "http://#{n}"
|
86
96
|
end
|
87
97
|
end
|
88
98
|
end
|
@@ -96,7 +106,7 @@ module Rack
|
|
96
106
|
end
|
97
107
|
|
98
108
|
def allow_origin?(source)
|
99
|
-
public_resources? ||
|
109
|
+
public_resources? || !!@origins.detect {|origin| origin === source}
|
100
110
|
end
|
101
111
|
|
102
112
|
def find_resource(path)
|
@@ -105,7 +115,7 @@ module Rack
|
|
105
115
|
end
|
106
116
|
|
107
117
|
class Resource
|
108
|
-
attr_accessor :path, :methods, :headers, :max_age, :credentials, :pattern
|
118
|
+
attr_accessor :path, :methods, :headers, :expose, :max_age, :credentials, :pattern
|
109
119
|
|
110
120
|
def initialize(public_resource, path, opts={})
|
111
121
|
self.path = path
|
@@ -121,6 +131,8 @@ module Rack
|
|
121
131
|
else
|
122
132
|
[opts[:headers]].flatten.collect{|h| h.downcase}
|
123
133
|
end
|
134
|
+
|
135
|
+
self.expose = opts[:expose] ? [opts[:expose]].flatten : nil
|
124
136
|
end
|
125
137
|
|
126
138
|
def match?(path)
|
@@ -133,8 +145,10 @@ module Rack
|
|
133
145
|
end
|
134
146
|
|
135
147
|
def to_headers(env)
|
148
|
+
x_origin = env['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']
|
136
149
|
h = { 'Access-Control-Allow-Origin' => public_resource? ? '*' : env['HTTP_ORIGIN'],
|
137
150
|
'Access-Control-Allow-Methods' => methods.collect{|m| m.to_s.upcase}.join(', '),
|
151
|
+
'Access-Control-Expose-Headers' => expose.nil? ? '' : expose.join(', '),
|
138
152
|
'Access-Control-Max-Age' => max_age.to_s }
|
139
153
|
h['Access-Control-Allow-Credentials'] = 'true' if credentials
|
140
154
|
h
|
data/rack-cors.gemspec
CHANGED
@@ -5,11 +5,12 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{rack-cors}
|
8
|
-
s.version = "0.2.
|
8
|
+
s.version = "0.2.4"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Calvin Yu"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2011-06-04}
|
13
|
+
s.description = %q{Middleware that will make Rack-based apps CORS compatible. Read more here: http://blog.sourcebender.com/2010/06/09/introducin-rack-cors.html. Fork the project here: http://github.com/cyu/rack-cors}
|
13
14
|
s.email = %q{csyu77@gmail.com}
|
14
15
|
s.extra_rdoc_files = [
|
15
16
|
"README.rdoc"
|
@@ -22,6 +23,7 @@ Gem::Specification.new do |s|
|
|
22
23
|
"lib/rack/cors.rb",
|
23
24
|
"rack-cors.gemspec",
|
24
25
|
"test/cors_test.rb",
|
26
|
+
"test/dsl_test.rb",
|
25
27
|
"test/test.ru"
|
26
28
|
]
|
27
29
|
s.homepage = %q{http://github.com/cyu/rack-cors}
|
@@ -30,7 +32,8 @@ Gem::Specification.new do |s|
|
|
30
32
|
s.rubygems_version = %q{1.3.7}
|
31
33
|
s.summary = %q{Middleware for enabling Cross-Origin Resource Sharing in Rack apps}
|
32
34
|
s.test_files = [
|
33
|
-
"test/cors_test.rb"
|
35
|
+
"test/cors_test.rb",
|
36
|
+
"test/dsl_test.rb"
|
34
37
|
]
|
35
38
|
|
36
39
|
if s.respond_to? :specification_version then
|
@@ -38,9 +41,12 @@ Gem::Specification.new do |s|
|
|
38
41
|
s.specification_version = 3
|
39
42
|
|
40
43
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
44
|
+
s.add_runtime_dependency(%q<rack>, [">= 0"])
|
41
45
|
else
|
46
|
+
s.add_dependency(%q<rack>, [">= 0"])
|
42
47
|
end
|
43
48
|
else
|
49
|
+
s.add_dependency(%q<rack>, [">= 0"])
|
44
50
|
end
|
45
51
|
end
|
46
52
|
|
data/test/cors_test.rb
CHANGED
@@ -6,7 +6,7 @@ Rack::Test::Session.class_eval do
|
|
6
6
|
def options(uri, params = {}, env = {}, &block)
|
7
7
|
env = env_for(uri, env.merge(:method => "OPTIONS", :params => params))
|
8
8
|
process_request(uri, env, &block)
|
9
|
-
end
|
9
|
+
end
|
10
10
|
end
|
11
11
|
|
12
12
|
Rack::Test::Methods.class_eval do
|
@@ -20,61 +20,95 @@ class CorsTest < Test::Unit::TestCase
|
|
20
20
|
eval "Rack::Builder.new {( " + File.read(File.dirname(__FILE__) + '/test.ru') + "\n )}"
|
21
21
|
end
|
22
22
|
|
23
|
+
should('support simple cors request') { cors_request }
|
24
|
+
|
25
|
+
should 'support regex origins configuration' do
|
26
|
+
cors_request :origin => 'http://192.168.0.1:1234'
|
27
|
+
end
|
28
|
+
|
29
|
+
should 'support alternative X-Origin header' do
|
30
|
+
header 'X-Origin', 'http://localhost:3000'
|
31
|
+
get '/'
|
32
|
+
assert_cors_success
|
33
|
+
end
|
34
|
+
|
35
|
+
should 'support expose header configuration' do
|
36
|
+
cors_request '/expose_single_header'
|
37
|
+
assert_equal 'expose-test', last_response.headers['Access-Control-Expose-Headers']
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
should 'support expose multiple header configuration' do
|
42
|
+
cors_request '/expose_multiple_headers'
|
43
|
+
assert_equal 'expose-test-1, expose-test-2', last_response.headers['Access-Control-Expose-Headers']
|
44
|
+
end
|
45
|
+
|
23
46
|
context 'preflight requests' do
|
24
47
|
should 'fail if origin is invalid' do
|
25
48
|
preflight_request('http://allyourdataarebelongtous.com', '/')
|
26
|
-
|
49
|
+
assert_cors_failure
|
27
50
|
end
|
28
51
|
|
29
52
|
should 'fail if Access-Control-Request-Method does not exist' do
|
30
53
|
preflight_request('http://localhost:3000', '/', :method => nil)
|
31
|
-
|
54
|
+
assert_cors_failure
|
32
55
|
end
|
33
56
|
|
34
57
|
should 'fail if Access-Control-Request-Method is not allowed' do
|
35
58
|
preflight_request('http://localhost:3000', '/get-only', :method => :post)
|
36
|
-
|
59
|
+
assert_cors_failure
|
37
60
|
end
|
38
61
|
|
39
62
|
should 'fail if header is not allowed' do
|
40
63
|
preflight_request('http://localhost:3000', '/single_header', :headers => 'Fooey')
|
41
|
-
|
64
|
+
assert_cors_failure
|
42
65
|
end
|
43
66
|
|
44
67
|
should 'allow any header if headers = :any' do
|
45
68
|
preflight_request('http://localhost:3000', '/', :headers => 'Fooey')
|
46
|
-
|
69
|
+
assert_cors_success
|
47
70
|
end
|
48
71
|
|
49
72
|
should 'allow header case insensitive match' do
|
50
73
|
preflight_request('http://localhost:3000', '/single_header', :headers => 'X-Domain-Token')
|
51
|
-
|
74
|
+
assert_cors_success
|
52
75
|
end
|
53
76
|
|
54
77
|
should 'allow multiple headers match' do
|
55
78
|
# Webkit style
|
56
79
|
preflight_request('http://localhost:3000', '/two_headers', :headers => 'X-Requested-With, X-Domain-Token')
|
57
|
-
|
80
|
+
assert_cors_success
|
58
81
|
|
59
82
|
# Gecko style
|
60
83
|
preflight_request('http://localhost:3000', '/two_headers', :headers => 'x-requested-with,x-domain-token')
|
61
|
-
|
84
|
+
assert_cors_success
|
62
85
|
end
|
63
86
|
|
64
87
|
should '* origin should allow any origin' do
|
65
88
|
preflight_request('http://locohost:3000', '/public')
|
66
|
-
|
89
|
+
assert_cors_success
|
67
90
|
assert_equal '*', last_response.headers['Access-Control-Allow-Origin']
|
68
91
|
end
|
69
92
|
|
70
93
|
should 'return a Content-Type' do
|
71
94
|
preflight_request('http://localhost:3000', '/')
|
72
|
-
|
95
|
+
assert_cors_success
|
73
96
|
assert_not_nil last_response.headers['Content-Type']
|
74
97
|
end
|
75
98
|
end
|
76
99
|
|
77
100
|
protected
|
101
|
+
def cors_request(*args)
|
102
|
+
path = args.first.is_a?(String) ? args.first : '/'
|
103
|
+
|
104
|
+
opts = args.last.is_a?(Hash) ? args.last : {:origin => 'http://localhost:3000'}
|
105
|
+
origin = opts[:origin]
|
106
|
+
|
107
|
+
header 'Origin', origin
|
108
|
+
get path
|
109
|
+
assert_cors_success
|
110
|
+
end
|
111
|
+
|
78
112
|
def preflight_request(origin, path, opts = {})
|
79
113
|
header 'Origin', origin
|
80
114
|
unless opts.key?(:method) && opts[:method].nil?
|
@@ -86,11 +120,11 @@ class CorsTest < Test::Unit::TestCase
|
|
86
120
|
options path
|
87
121
|
end
|
88
122
|
|
89
|
-
def
|
90
|
-
assert_not_nil last_response.headers['Access-Control-Allow-Origin']
|
123
|
+
def assert_cors_success
|
124
|
+
assert_not_nil last_response.headers['Access-Control-Allow-Origin'], 'missing Access-Control-Allow-Origin header'
|
91
125
|
end
|
92
126
|
|
93
|
-
def
|
94
|
-
assert_nil last_response.headers['Access-Control-Allow-Origin']
|
127
|
+
def assert_cors_failure
|
128
|
+
assert_nil last_response.headers['Access-Control-Allow-Origin'], 'no expecting Access-Control-Allow-Origin header'
|
95
129
|
end
|
96
130
|
end
|
data/test/dsl_test.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rack/cors'
|
3
|
+
require 'shoulda'
|
4
|
+
|
5
|
+
|
6
|
+
class DSLTest < Test::Unit::TestCase
|
7
|
+
should 'support explicit config object dsl mode' do
|
8
|
+
cors = Rack::Cors.new(Proc.new {}) do |cfg|
|
9
|
+
cfg.allow do |allow|
|
10
|
+
allow.origins 'localhost:3000', '127.0.0.1:3000'
|
11
|
+
allow.resource '/get-only', :methods => :get
|
12
|
+
allow.resource '/', :headers => :any
|
13
|
+
end
|
14
|
+
end
|
15
|
+
resources = cors.send :all_resources
|
16
|
+
assert_equal 1, resources.length
|
17
|
+
assert resources.first.allow_origin?('http://localhost:3000')
|
18
|
+
end
|
19
|
+
|
20
|
+
should 'support implicit config object dsl mode' do
|
21
|
+
cors = Rack::Cors.new(Proc.new {}) do
|
22
|
+
allow do
|
23
|
+
origins 'localhost:3000', '127.0.0.1:3000'
|
24
|
+
resource '/get-only', :methods => :get
|
25
|
+
resource '/', :headers => :any
|
26
|
+
end
|
27
|
+
end
|
28
|
+
resources = cors.send :all_resources
|
29
|
+
assert_equal 1, resources.length
|
30
|
+
assert resources.first.allow_origin?('http://localhost:3000')
|
31
|
+
end
|
32
|
+
end
|
data/test/test.ru
CHANGED
@@ -1,22 +1,24 @@
|
|
1
1
|
require 'rack/cors'
|
2
2
|
|
3
|
-
use Rack::Cors do
|
4
|
-
|
5
|
-
|
3
|
+
use Rack::Cors do
|
4
|
+
allow do
|
5
|
+
origins 'localhost:3000', '127.0.0.1:3000', /http:\/\/192\.168\.0\.\d{1,3}(:\d+)?/
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
resource '/get-only', :methods => :get
|
8
|
+
resource '/', :headers => :any
|
9
|
+
resource '/single_header', :headers => 'x-domain-token'
|
10
|
+
resource '/two_headers', :headers => %w{x-domain-token x-requested-with}
|
11
|
+
resource '/expose_single_header', :expose => 'expose-test'
|
12
|
+
resource '/expose_multiple_headers', :expose => %w{expose-test-1 expose-test-2}
|
13
|
+
# resource '/file/at/*',
|
12
14
|
# :methods => [:get, :post, :put, :delete],
|
13
15
|
# :headers => :any,
|
14
16
|
# :max_age => 0
|
15
17
|
end
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
|
19
|
+
allow do
|
20
|
+
origins '*'
|
21
|
+
resource '/public'
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-cors
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 31
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 0.2.
|
9
|
+
- 4
|
10
|
+
version: 0.2.4
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Calvin Yu
|
@@ -15,11 +15,24 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2011-06-04 00:00:00 -04:00
|
19
19
|
default_executable:
|
20
|
-
dependencies:
|
21
|
-
|
22
|
-
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rack
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
description: "Middleware that will make Rack-based apps CORS compatible. Read more here: http://blog.sourcebender.com/2010/06/09/introducin-rack-cors.html. Fork the project here: http://github.com/cyu/rack-cors"
|
23
36
|
email: csyu77@gmail.com
|
24
37
|
executables: []
|
25
38
|
|
@@ -35,6 +48,7 @@ files:
|
|
35
48
|
- lib/rack/cors.rb
|
36
49
|
- rack-cors.gemspec
|
37
50
|
- test/cors_test.rb
|
51
|
+
- test/dsl_test.rb
|
38
52
|
- test/test.ru
|
39
53
|
has_rdoc: true
|
40
54
|
homepage: http://github.com/cyu/rack-cors
|
@@ -72,3 +86,4 @@ specification_version: 3
|
|
72
86
|
summary: Middleware for enabling Cross-Origin Resource Sharing in Rack apps
|
73
87
|
test_files:
|
74
88
|
- test/cors_test.rb
|
89
|
+
- test/dsl_test.rb
|