rack-cors 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
|