rack-cors 0.2.9 → 1.0.5

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.
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class Cors
3
- VERSION = "0.2.9"
3
+ VERSION = "1.0.5"
4
4
  end
5
5
  end
data/rack-cors.gemspec CHANGED
@@ -8,9 +8,9 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Rack::Cors::VERSION
9
9
  spec.authors = ["Calvin Yu"]
10
10
  spec.email = ["me@sourcebender.com"]
11
- spec.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}
11
+ spec.description = %q{Middleware that will make Rack-based apps CORS compatible. Fork the project here: https://github.com/cyu/rack-cors}
12
12
  spec.summary = %q{Middleware for enabling Cross-Origin Resource Sharing in Rack apps}
13
- spec.homepage = "http://github.com/cyu/rack-cors"
13
+ spec.homepage = "https://github.com/cyu/rack-cors"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files`.split($/).reject { |f| f == '.gitignore' or f =~ /^examples/ }
@@ -18,9 +18,10 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_development_dependency "bundler", "~> 1.3"
22
- spec.add_development_dependency "rake"
23
- spec.add_development_dependency "shoulda", ">= 0"
24
- spec.add_development_dependency "mocha", ">= 0.14.0"
25
- spec.add_development_dependency "rack-test", ">= 0"
21
+ spec.add_dependency "rack", ">= 1.6.0"
22
+ spec.add_development_dependency "bundler", ">= 1.16.0", '< 3'
23
+ spec.add_development_dependency "rake", "~> 12.3.0"
24
+ spec.add_development_dependency "minitest", "~> 5.11.0"
25
+ spec.add_development_dependency "mocha", "~> 1.6.0"
26
+ spec.add_development_dependency "rack-test", "~> 1.1.0"
26
27
  end
@@ -1,4 +1,4 @@
1
- CORS_SERVER = 'cors-server:3000'
1
+ CORS_SERVER = '127.0.0.1.xip.io:9292'
2
2
 
3
3
  describe 'CORS', ->
4
4
 
@@ -7,6 +7,31 @@ describe 'CORS', ->
7
7
  expect(data).to.eql('Hello world')
8
8
  done()
9
9
 
10
+ it 'should allow PUT access to dynamic resource', (done) ->
11
+ $.ajax("http://#{CORS_SERVER}/", type: 'PUT').done (data, textStatus, jqXHR) ->
12
+ expect(data).to.eql('Hello world')
13
+ done()
14
+
15
+ it 'should allow PATCH access to dynamic resource', (done) ->
16
+ $.ajax("http://#{CORS_SERVER}/", type: 'PATCH').done (data, textStatus, jqXHR) ->
17
+ expect(data).to.eql('Hello world')
18
+ done()
19
+
20
+ it 'should allow HEAD access to dynamic resource', (done) ->
21
+ $.ajax("http://#{CORS_SERVER}/", type: 'HEAD').done (data, textStatus, jqXHR) ->
22
+ expect(jqXHR.status).to.eql(200)
23
+ done()
24
+
25
+ it 'should allow DELETE access to dynamic resource', (done) ->
26
+ $.ajax("http://#{CORS_SERVER}/", type: 'DELETE').done (data, textStatus, jqXHR) ->
27
+ expect(data).to.eql('Hello world')
28
+ done()
29
+
30
+ it 'should allow OPTIONS access to dynamic resource', (done) ->
31
+ $.ajax("http://#{CORS_SERVER}/", type: 'OPTIONS').done (data, textStatus, jqXHR) ->
32
+ expect(jqXHR.status).to.eql(200)
33
+ done()
34
+
10
35
  it 'should allow access to static resource', (done) ->
11
36
  $.get "http://#{CORS_SERVER}/static.txt", (data, status, xhr) ->
12
37
  expect($.trim(data)).to.eql("hello world")
@@ -20,4 +45,3 @@ describe 'CORS', ->
20
45
  success:(data, status, xhr) ->
21
46
  expect($.trim(data)).to.eql("OK!")
22
47
  done()
23
-
@@ -1,17 +1,58 @@
1
+ // Generated by CoffeeScript 2.3.1
1
2
  (function() {
2
3
  var CORS_SERVER;
3
4
 
4
- CORS_SERVER = 'cors-server:3000';
5
+ CORS_SERVER = '127.0.0.1.xip.io:9292';
5
6
 
6
7
  describe('CORS', function() {
7
8
  it('should allow access to dynamic resource', function(done) {
8
- return $.get("http://" + CORS_SERVER + "/", function(data, status, xhr) {
9
+ return $.get(`http://${CORS_SERVER}/`, function(data, status, xhr) {
9
10
  expect(data).to.eql('Hello world');
10
11
  return done();
11
12
  });
12
13
  });
14
+ it('should allow PUT access to dynamic resource', function(done) {
15
+ return $.ajax(`http://${CORS_SERVER}/`, {
16
+ type: 'PUT'
17
+ }).done(function(data, textStatus, jqXHR) {
18
+ expect(data).to.eql('Hello world');
19
+ return done();
20
+ });
21
+ });
22
+ it('should allow PATCH access to dynamic resource', function(done) {
23
+ return $.ajax(`http://${CORS_SERVER}/`, {
24
+ type: 'PATCH'
25
+ }).done(function(data, textStatus, jqXHR) {
26
+ expect(data).to.eql('Hello world');
27
+ return done();
28
+ });
29
+ });
30
+ it('should allow HEAD access to dynamic resource', function(done) {
31
+ return $.ajax(`http://${CORS_SERVER}/`, {
32
+ type: 'HEAD'
33
+ }).done(function(data, textStatus, jqXHR) {
34
+ expect(jqXHR.status).to.eql(200);
35
+ return done();
36
+ });
37
+ });
38
+ it('should allow DELETE access to dynamic resource', function(done) {
39
+ return $.ajax(`http://${CORS_SERVER}/`, {
40
+ type: 'DELETE'
41
+ }).done(function(data, textStatus, jqXHR) {
42
+ expect(data).to.eql('Hello world');
43
+ return done();
44
+ });
45
+ });
46
+ it('should allow OPTIONS access to dynamic resource', function(done) {
47
+ return $.ajax(`http://${CORS_SERVER}/`, {
48
+ type: 'OPTIONS'
49
+ }).done(function(data, textStatus, jqXHR) {
50
+ expect(jqXHR.status).to.eql(200);
51
+ return done();
52
+ });
53
+ });
13
54
  it('should allow access to static resource', function(done) {
14
- return $.get("http://" + CORS_SERVER + "/static.txt", function(data, status, xhr) {
55
+ return $.get(`http://${CORS_SERVER}/static.txt`, function(data, status, xhr) {
15
56
  expect($.trim(data)).to.eql("hello world");
16
57
  return done();
17
58
  });
@@ -19,7 +60,7 @@
19
60
  return it('should allow post resource', function(done) {
20
61
  return $.ajax({
21
62
  type: 'POST',
22
- url: "http://" + CORS_SERVER + "/cors",
63
+ url: `http://${CORS_SERVER}/cors`,
23
64
  beforeSend: function(xhr) {
24
65
  return xhr.setRequestHeader('X-Requested-With', 'XMLHTTPRequest');
25
66
  },
@@ -1,14 +1,15 @@
1
- require 'rubygems'
2
- require 'test/unit'
1
+ require 'minitest/autorun'
3
2
  require 'rack/test'
4
- require 'shoulda'
5
3
  require 'mocha/setup'
6
4
  require 'rack/cors'
5
+ require 'ostruct'
7
6
 
8
7
  Rack::Test::Session.class_eval do
9
- def options(uri, params = {}, env = {}, &block)
10
- env = env_for(uri, env.merge(:method => "OPTIONS", :params => params))
11
- process_request(uri, env, &block)
8
+ unless defined? :options
9
+ def options(uri, params = {}, env = {}, &block)
10
+ env = env_for(uri, env.merge(:method => "OPTIONS", :params => params))
11
+ process_request(uri, env, &block)
12
+ end
12
13
  end
13
14
  end
14
15
 
@@ -16,149 +17,479 @@ Rack::Test::Methods.class_eval do
16
17
  def_delegator :current_session, :options
17
18
  end
18
19
 
19
- class CorsTest < Test::Unit::TestCase
20
+ module MiniTest::Assertions
21
+ def assert_cors_success(response)
22
+ assert !response.headers['Access-Control-Allow-Origin'].nil?, "Expected a successful CORS response"
23
+ end
24
+
25
+ def assert_not_cors_success(response)
26
+ assert response.headers['Access-Control-Allow-Origin'].nil?, "Expected a failed CORS response"
27
+ end
28
+ end
29
+
30
+ class CaptureResult
31
+ def initialize(app, options = {})
32
+ @app = app
33
+ @result_holder = options[:holder]
34
+ end
35
+
36
+ def call(env)
37
+ response = @app.call(env)
38
+ @result_holder.cors_result = env[Rack::Cors::RACK_CORS]
39
+ return response
40
+ end
41
+ end
42
+
43
+ class FakeProxy
44
+ def initialize(app, options = {})
45
+ @app = app
46
+ end
47
+
48
+ def call(env)
49
+ status, headers, body = @app.call(env)
50
+ headers['Vary'] = %w(Origin User-Agent)
51
+ [status, headers, body]
52
+ end
53
+ end
54
+
55
+ Rack::MockResponse.infect_an_assertion :assert_cors_success, :must_render_cors_success, :only_one_argument
56
+ Rack::MockResponse.infect_an_assertion :assert_not_cors_success, :wont_render_cors_success, :only_one_argument
57
+
58
+ describe Rack::Cors do
20
59
  include Rack::Test::Methods
21
60
 
22
- def app
23
- eval "Rack::Builder.new {( " + File.read(File.dirname(__FILE__) + '/test.ru') + "\n )}"
61
+ attr_accessor :cors_result
62
+
63
+ def load_app(name, options = {})
64
+ test = self
65
+ Rack::Builder.new do
66
+ use CaptureResult, :holder => test
67
+ eval File.read(File.dirname(__FILE__) + "/#{name}.ru")
68
+ use FakeProxy if options[:proxy]
69
+ map('/') do
70
+ run proc { |env|
71
+ [200, {'Content-Type' => 'text/html'}, ['success']]
72
+ }
73
+ end
74
+ end
75
+ end
76
+
77
+ let(:app) { load_app('test') }
78
+
79
+ it 'should support simple CORS request' do
80
+ successful_cors_request
81
+ cors_result.must_be :hit
24
82
  end
25
83
 
26
- should('support simple cors request') { cors_request }
84
+ it "should not return CORS headers if Origin header isn't present" do
85
+ get '/'
86
+ last_response.wont_render_cors_success
87
+ cors_result.wont_be :hit
88
+ end
27
89
 
28
- should 'support OPTIONS cors request' do
29
- cors_request '/options', :method => :options
90
+ it 'should support OPTIONS CORS request' do
91
+ successful_cors_request '/options', :method => :options
30
92
  end
31
93
 
32
- should 'support regex origins configuration' do
33
- cors_request :origin => 'http://192.168.0.1:1234'
94
+ it 'should support regex origins configuration' do
95
+ successful_cors_request :origin => 'http://192.168.0.1:1234'
34
96
  end
35
97
 
36
- should 'support proc origins configuration' do
37
- cors_request '/proc-origin', :origin => 'http://10.10.10.10:3000'
98
+ it 'should support subdomain example' do
99
+ successful_cors_request :origin => 'http://subdomain.example.com'
38
100
  end
39
101
 
40
- should 'support alternative X-Origin header' do
102
+ it 'should support proc origins configuration' do
103
+ successful_cors_request '/proc-origin', :origin => 'http://10.10.10.10:3000'
104
+ end
105
+
106
+ it 'should support lambda origin configuration' do
107
+ successful_cors_request '/lambda-origin', :origin => 'http://10.10.10.10:3000'
108
+ end
109
+
110
+ it 'should support proc origins configuration (inverse)' do
111
+ cors_request '/proc-origin', :origin => 'http://bad.guy'
112
+ last_response.wont_render_cors_success
113
+ end
114
+
115
+ it 'should not mix up path rules across origins' do
116
+ header 'Origin', 'http://10.10.10.10:3000'
117
+ get '/' # / is configured in a separate rule block
118
+ last_response.wont_render_cors_success
119
+ end
120
+
121
+ it 'should support alternative X-Origin header' do
41
122
  header 'X-Origin', 'http://localhost:3000'
42
123
  get '/'
43
- assert_cors_success
124
+ last_response.must_render_cors_success
125
+ end
126
+
127
+ it 'should support expose header configuration' do
128
+ successful_cors_request '/expose_single_header'
129
+ last_response.headers['Access-Control-Expose-Headers'].must_equal 'expose-test'
44
130
  end
45
131
 
46
- should 'support expose header configuration' do
47
- cors_request '/expose_single_header'
48
- assert_equal 'expose-test', last_response.headers['Access-Control-Expose-Headers']
132
+ it 'should support expose multiple header configuration' do
133
+ successful_cors_request '/expose_multiple_headers'
134
+ last_response.headers['Access-Control-Expose-Headers'].must_equal 'expose-test-1, expose-test-2'
49
135
  end
50
136
 
51
- should 'support expose multiple header configuration' do
52
- cors_request '/expose_multiple_headers'
53
- assert_equal 'expose-test-1, expose-test-2', last_response.headers['Access-Control-Expose-Headers']
137
+ # Explanation: http://www.fastly.com/blog/best-practices-for-using-the-vary-header/
138
+ it "should add Vary header if resource matches even if Origin header isn't present" do
139
+ get '/'
140
+ last_response.wont_render_cors_success
141
+ last_response.headers['Vary'].must_equal 'Origin'
54
142
  end
55
143
 
56
- should 'add Vary header if Access-Control-Allow-Origin header was added and if it is specific' do
57
- cors_request '/', :origin => "http://192.168.0.3:8080"
58
- assert_cors_success
59
- assert_equal 'http://192.168.0.3:8080', last_response.headers['Access-Control-Allow-Origin']
60
- assert_not_nil last_response.headers['Vary'], 'missing Vary header'
144
+ it "should add Vary header based on :vary option" do
145
+ successful_cors_request '/vary_test'
146
+ last_response.headers['Vary'].must_equal 'Origin, Host'
61
147
  end
62
148
 
63
- should 'not add Vary header if Access-Control-Allow-Origin header was added and if it is generic (*)' do
64
- cors_request '/public_without_credentials', :origin => "http://192.168.1.3:8080"
65
- assert_cors_success
66
- assert_equal '*', last_response.headers['Access-Control-Allow-Origin']
67
- assert_nil last_response.headers['Vary'], 'no expecting Vary header'
149
+ it "decode URL and resolve paths before resource matching" do
150
+ header 'Origin', 'http://localhost:3000'
151
+ get '/public/a/..%2F..%2Fprivate/stuff'
152
+ last_response.wont_render_cors_success
68
153
  end
69
154
 
70
- should 'support multi allow configurations for the same resource' do
71
- cors_request '/multi-allow-config', :origin => "http://mucho-grande.com"
72
- assert_cors_success
73
- assert_equal 'http://mucho-grande.com', last_response.headers['Access-Control-Allow-Origin']
74
- assert_equal 'Origin', last_response.headers['Vary'], 'expecting Vary header'
155
+ describe 'with array of upstream Vary headers' do
156
+ let(:app) { load_app('test', { proxy: true }) }
75
157
 
76
- cors_request '/multi-allow-config', :origin => "http://192.168.1.3:8080"
77
- assert_cors_success
78
- assert_equal '*', last_response.headers['Access-Control-Allow-Origin']
79
- assert_nil last_response.headers['Vary'], 'no expecting Vary header'
158
+ it 'should add to them' do
159
+ successful_cors_request '/vary_test'
160
+ last_response.headers['Vary'].must_equal 'Origin, User-Agent, Host'
161
+ end
162
+ end
163
+
164
+ it 'should add Vary header if Access-Control-Allow-Origin header was added and if it is specific' do
165
+ successful_cors_request '/', :origin => "http://192.168.0.3:8080"
166
+ last_response.headers['Access-Control-Allow-Origin'].must_equal 'http://192.168.0.3:8080'
167
+ last_response.headers['Vary'].wont_be_nil
80
168
  end
81
169
 
82
- should 'not log debug messages if debug option is false' do
83
- app = mock
84
- app.stubs(:call).returns(200, {}, [''])
170
+ it 'should add Vary header even if Access-Control-Allow-Origin header was added and it is generic (*)' do
171
+ successful_cors_request '/public_without_credentials', :origin => "http://192.168.1.3:8080"
172
+ last_response.headers['Access-Control-Allow-Origin'].must_equal '*'
173
+ last_response.headers['Vary'].must_equal 'Origin'
174
+ end
85
175
 
86
- logger = mock
87
- logger.expects(:debug).never
176
+ it 'should support multi allow configurations for the same resource' do
177
+ successful_cors_request '/multi-allow-config', :origin => "http://mucho-grande.com"
178
+ last_response.headers['Access-Control-Allow-Origin'].must_equal 'http://mucho-grande.com'
179
+ last_response.headers['Vary'].must_equal 'Origin'
88
180
 
89
- cors = Rack::Cors.new(app, :debug => false, :logger => logger) {}
90
- cors.send(:debug, {}, 'testing')
181
+ successful_cors_request '/multi-allow-config', :origin => "http://192.168.1.3:8080"
182
+ last_response.headers['Access-Control-Allow-Origin'].must_equal '*'
183
+ last_response.headers['Vary'].must_equal 'Origin'
91
184
  end
92
185
 
93
- should 'log debug messages if debug option is true' do
94
- app = mock
95
- app.stubs(:call).returns(200, {}, [''])
186
+ it "should not return CORS headers on OPTIONS request if Access-Control-Allow-Origin is not present" do
187
+ options '/get-only'
188
+ last_response.headers['Access-Control-Allow-Origin'].must_be_nil
189
+ end
190
+
191
+ it "should not apply CORS headers if it does not match conditional on resource" do
192
+ header 'Origin', 'http://192.168.0.1:1234'
193
+ get '/conditional'
194
+ last_response.wont_render_cors_success
195
+ end
96
196
 
97
- logger = mock
98
- logger.expects(:debug)
197
+ it "should apply CORS headers if it does match conditional on resource" do
198
+ header 'X-OK', '1'
199
+ successful_cors_request '/conditional', :origin => 'http://192.168.0.1:1234'
200
+ end
99
201
 
100
- cors = Rack::Cors.new(app, :debug => true, :logger => logger) {}
101
- cors.send(:debug, {}, 'testing')
202
+ it "should not allow everything if Origin is configured as blank string" do
203
+ cors_request '/blank-origin', origin: "http://example.net"
204
+ last_response.wont_render_cors_success
102
205
  end
103
206
 
104
- context 'preflight requests' do
105
- should 'fail if origin is invalid' do
207
+ it "should not allow credentials for public resources" do
208
+ successful_cors_request '/public'
209
+ last_response.headers['Access-Control-Allow-Credentials'].must_be_nil
210
+ end
211
+
212
+ describe 'logging' do
213
+ it 'should not log debug messages if debug option is false' do
214
+ app = mock
215
+ app.stubs(:call).returns(200, {}, [''])
216
+
217
+ logger = mock
218
+ logger.expects(:debug).never
219
+
220
+ cors = Rack::Cors.new(app, :debug => false, :logger => logger) {}
221
+ cors.send(:debug, {}, 'testing')
222
+ end
223
+
224
+ it 'should log debug messages if debug option is true' do
225
+ app = mock
226
+ app.stubs(:call).returns(200, {}, [''])
227
+
228
+ logger = mock
229
+ logger.expects(:debug)
230
+
231
+ cors = Rack::Cors.new(app, :debug => true, :logger => logger) {}
232
+ cors.send(:debug, {}, 'testing')
233
+ end
234
+
235
+ it 'should use rack.logger if available' do
236
+ app = mock
237
+ app.stubs(:call).returns([200, {}, ['']])
238
+
239
+ logger = mock
240
+ logger.expects(:debug).at_least_once
241
+
242
+ cors = Rack::Cors.new(app, :debug => true) {}
243
+ cors.call({'rack.logger' => logger, 'HTTP_ORIGIN' => 'test.com'})
244
+ end
245
+
246
+ it 'should use logger proc' do
247
+ app = mock
248
+ app.stubs(:call).returns([200, {}, ['']])
249
+
250
+ logger = mock
251
+ logger.expects(:debug)
252
+
253
+ cors = Rack::Cors.new(app, :debug => true, :logger => proc { logger }) {}
254
+ cors.call({'HTTP_ORIGIN' => 'test.com'})
255
+ end
256
+
257
+ describe 'with Rails setup' do
258
+ after do
259
+ ::Rails.logger = nil if defined?(::Rails)
260
+ end
261
+
262
+ it 'should use Rails.logger if available' do
263
+ app = mock
264
+ app.stubs(:call).returns([200, {}, ['']])
265
+
266
+ logger = mock
267
+ logger.expects(:debug)
268
+
269
+ ::Rails = OpenStruct.new(:logger => logger)
270
+
271
+ cors = Rack::Cors.new(app, :debug => true) {}
272
+ cors.call({'HTTP_ORIGIN' => 'test.com'})
273
+ end
274
+ end
275
+
276
+ it 'should use Logger if none is set' do
277
+ app = mock
278
+ app.stubs(:call).returns([200, {}, ['']])
279
+
280
+ logger = mock
281
+ Logger.expects(:new).returns(logger)
282
+ logger.expects(:tap).returns(logger)
283
+ logger.expects(:debug)
284
+
285
+ cors = Rack::Cors.new(app, :debug => true) {}
286
+ cors.call({'HTTP_ORIGIN' => 'test.com'})
287
+ end
288
+ end
289
+
290
+ describe 'preflight requests' do
291
+ it 'should fail if origin is invalid' do
106
292
  preflight_request('http://allyourdataarebelongtous.com', '/')
107
- assert_cors_failure
293
+ last_response.wont_render_cors_success
294
+ cors_result.wont_be :hit
295
+ cors_result.must_be :preflight
108
296
  end
109
297
 
110
- should 'fail if Access-Control-Request-Method is not allowed' do
298
+ it 'should fail if Access-Control-Request-Method is not allowed' do
111
299
  preflight_request('http://localhost:3000', '/get-only', :method => :post)
112
- assert_cors_failure
300
+ last_response.wont_render_cors_success
301
+ cors_result.miss_reason.must_equal Rack::Cors::Result::MISS_DENY_METHOD
302
+ cors_result.wont_be :hit
303
+ cors_result.must_be :preflight
113
304
  end
114
305
 
115
- should 'fail if header is not allowed' do
306
+ it 'should fail if header is not allowed' do
116
307
  preflight_request('http://localhost:3000', '/single_header', :headers => 'Fooey')
117
- assert_cors_failure
308
+ last_response.wont_render_cors_success
309
+ cors_result.miss_reason.must_equal Rack::Cors::Result::MISS_DENY_HEADER
310
+ cors_result.wont_be :hit
311
+ cors_result.must_be :preflight
118
312
  end
119
313
 
120
- should 'allow any header if headers = :any' do
314
+ it 'should allow any header if headers = :any' do
121
315
  preflight_request('http://localhost:3000', '/', :headers => 'Fooey')
122
- assert_cors_success
316
+ last_response.must_render_cors_success
317
+ end
318
+
319
+ it 'should allow any method if methods = :any' do
320
+ preflight_request('http://localhost:3000', '/', :methods => :any)
321
+ last_response.must_render_cors_success
123
322
  end
124
323
 
125
- should 'allow header case insensitive match' do
324
+ it 'allows PATCH method' do
325
+ preflight_request('http://localhost:3000', '/', :methods => [ :patch ])
326
+ last_response.must_render_cors_success
327
+ end
328
+
329
+ it 'should allow header case insensitive match' do
126
330
  preflight_request('http://localhost:3000', '/single_header', :headers => 'X-Domain-Token')
127
- assert_cors_success
331
+ last_response.must_render_cors_success
128
332
  end
129
333
 
130
- should 'allow multiple headers match' do
334
+ it 'should allow multiple headers match' do
131
335
  # Webkit style
132
336
  preflight_request('http://localhost:3000', '/two_headers', :headers => 'X-Requested-With, X-Domain-Token')
133
- assert_cors_success
337
+ last_response.must_render_cors_success
134
338
 
135
339
  # Gecko style
136
340
  preflight_request('http://localhost:3000', '/two_headers', :headers => 'x-requested-with,x-domain-token')
137
- assert_cors_success
341
+ last_response.must_render_cors_success
138
342
  end
139
343
 
140
- should '* origin should allow any origin' do
344
+ it 'should * origin should allow any origin' do
141
345
  preflight_request('http://locohost:3000', '/public')
142
- assert_cors_success
143
- assert_equal 'http://locohost:3000', last_response.headers['Access-Control-Allow-Origin']
346
+ last_response.must_render_cors_success
347
+ last_response.headers['Access-Control-Allow-Origin'].must_equal '*'
144
348
  end
145
349
 
146
- should '* origin should allow any origin, and set * if no credentials required' do
350
+ it 'should * origin should allow any origin, and set * if no credentials required' do
147
351
  preflight_request('http://locohost:3000', '/public_without_credentials')
148
- assert_cors_success
149
- assert_equal '*', last_response.headers['Access-Control-Allow-Origin']
352
+ last_response.must_render_cors_success
353
+ last_response.headers['Access-Control-Allow-Origin'].must_equal '*'
354
+ end
355
+
356
+ it 'should return "file://" as header with "file://" as origin' do
357
+ preflight_request('file://', '/')
358
+ last_response.must_render_cors_success
359
+ last_response.headers['Access-Control-Allow-Origin'].must_equal 'file://'
360
+ end
361
+
362
+ describe '' do
363
+
364
+ let(:app) do
365
+ test = self
366
+ Rack::Builder.new do
367
+ use CaptureResult, holder: test
368
+ use Rack::Cors, debug: true, logger: Logger.new(StringIO.new) do
369
+ allow do
370
+ origins '*'
371
+ resource '/', :methods => :post
372
+ end
373
+ end
374
+ map('/') do
375
+ run ->(env) { [500, {}, ['FAIL!']] }
376
+ end
377
+ end
378
+ end
379
+
380
+ it "should not send failed preflight requests thru the app" do
381
+ preflight_request('http://localhost', '/', :method => :unsupported)
382
+ last_response.wont_render_cors_success
383
+ last_response.status.must_equal 200
384
+ cors_result.must_be :preflight
385
+ cors_result.wont_be :hit
386
+ cors_result.miss_reason.must_equal Rack::Cors::Result::MISS_DENY_METHOD
387
+ end
388
+ end
389
+ end
390
+
391
+ describe "with insecure configuration" do
392
+ let(:app) { load_app('insecure') }
393
+
394
+ it "should raise an error" do
395
+ proc { cors_request '/public' }.must_raise Rack::Cors::Resource::CorsMisconfigurationError
396
+ end
397
+ end
398
+
399
+ describe "with non HTTP config" do
400
+ let(:app) { load_app("non_http") }
401
+
402
+ it 'should support non http/https origins' do
403
+ successful_cors_request '/public', origin: 'content://com.company.app'
404
+ end
405
+ end
406
+
407
+ describe 'Rack::Lint' do
408
+ def app
409
+ @app ||= Rack::Builder.new do
410
+ use Rack::Cors
411
+ use Rack::Lint
412
+ run ->(env) { [200, {'Content-Type' => 'text/html'}, ['hello']] }
413
+ end
414
+ end
415
+
416
+ it 'is lint-compliant with non-CORS request' do
417
+ get '/'
418
+ last_response.status.must_equal 200
419
+ end
420
+ end
421
+
422
+ describe 'with app overriding CORS header' do
423
+ let(:app) do
424
+ Rack::Builder.new do
425
+ use Rack::Cors, debug: true, logger: Logger.new(StringIO.new) do
426
+ allow do
427
+ origins '*'
428
+ resource '/'
429
+ end
430
+ end
431
+ map('/') do
432
+ run ->(env) { [200, {'Access-Control-Allow-Origin' => 'http://foo.net'}, ['success']] }
433
+ end
434
+ end
435
+ end
436
+
437
+ it "should return app header" do
438
+ successful_cors_request origin: "http://example.net"
439
+ last_response.headers['Access-Control-Allow-Origin'].must_equal "http://foo.net"
440
+ end
441
+
442
+ it "should return original headers if in debug" do
443
+ successful_cors_request origin: "http://example.net"
444
+ last_response.headers['X-Rack-CORS-Original-Access-Control-Allow-Origin'].must_equal "*"
150
445
  end
446
+ end
151
447
 
152
- should '"null" origin, allowed as "file://", returned as "null" in header' do
153
- preflight_request('null', '/')
154
- assert_cors_success
155
- assert_equal 'null', last_response.headers['Access-Control-Allow-Origin']
448
+ describe 'with headers set to nil' do
449
+ let(:app) do
450
+ Rack::Builder.new do
451
+ use Rack::Cors do
452
+ allow do
453
+ origins '*'
454
+ resource '/', headers: nil
455
+ end
456
+ end
457
+ map('/') do
458
+ run ->(env) { [200, {'Content-Type' => 'text/html'}, ['hello']] }
459
+ end
460
+ end
156
461
  end
157
462
 
158
- should 'return a Content-Type' do
159
- preflight_request('http://localhost:3000', '/')
160
- assert_cors_success
161
- assert_not_nil last_response.headers['Content-Type']
463
+ it 'should succeed with CORS simple headers' do
464
+ preflight_request('http://localhost:3000', '/', :headers => 'Accept')
465
+ last_response.must_render_cors_success
466
+ end
467
+ end
468
+
469
+ describe 'with custom allowed headers' do
470
+ let(:app) do
471
+ Rack::Builder.new do
472
+ use Rack::Cors do
473
+ allow do
474
+ origins '*'
475
+ resource '/', headers: []
476
+ end
477
+ end
478
+ map('/') do
479
+ run ->(env) { [200, {'Content-Type' => 'text/html'}, ['hello']] }
480
+ end
481
+ end
482
+ end
483
+
484
+ it 'should succeed with CORS simple headers' do
485
+ preflight_request('http://localhost:3000', '/', :headers => 'Accept')
486
+ last_response.must_render_cors_success
487
+ preflight_request('http://localhost:3000', '/', :headers => 'Accept-Language')
488
+ last_response.must_render_cors_success
489
+ preflight_request('http://localhost:3000', '/', :headers => 'Content-Type')
490
+ last_response.must_render_cors_success
491
+ preflight_request('http://localhost:3000', '/', :headers => 'Content-Language')
492
+ last_response.must_render_cors_success
162
493
  end
163
494
  end
164
495
 
@@ -170,8 +501,12 @@ class CorsTest < Test::Unit::TestCase
170
501
  opts.merge! args.last if args.last.is_a?(Hash)
171
502
 
172
503
  header 'Origin', opts[:origin]
173
- current_session.__send__ opts[:method], path
174
- assert_cors_success
504
+ current_session.__send__ opts[:method], path, {}, test: self
505
+ end
506
+
507
+ def successful_cors_request(*args)
508
+ cors_request(*args)
509
+ last_response.must_render_cors_success
175
510
  end
176
511
 
177
512
  def preflight_request(origin, path, opts = {})
@@ -184,12 +519,4 @@ class CorsTest < Test::Unit::TestCase
184
519
  end
185
520
  options path
186
521
  end
187
-
188
- def assert_cors_success
189
- assert_not_nil last_response.headers['Access-Control-Allow-Origin'], 'missing Access-Control-Allow-Origin header'
190
- end
191
-
192
- def assert_cors_failure
193
- assert_nil last_response.headers['Access-Control-Allow-Origin'], 'no expecting Access-Control-Allow-Origin header'
194
- end
195
522
  end