rack-cors 0.4.1 → 2.0.1

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.

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