rack-cors 0.2.9 → 0.3.0

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.

checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- OTQyY2Q0OGNmOGZjMWNhOTc0OTdmOTY3ZTk0NDY0NzhmODE5YTI5Yg==
5
- data.tar.gz: !binary |-
6
- ZDQyMTJlMTg3MDczMmE0ZjFkMmZjOGExMGU3NDE0NmQ2MGY1YzBlZQ==
2
+ SHA1:
3
+ metadata.gz: 7fa6e09583a5f2886f25edeea60c8684dd4570e2
4
+ data.tar.gz: d6527b5970f02a3a43dfc305cf8c3edde91ae00b
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- NGViNTEzOTIzZjc2ZjVhNjZkZDBmODQ5NzAzNzk3ZThiYzZkMGU2YTU3MmUx
10
- NDc3ODlkMTIwNjFmNGZhOWUwZmNjMmEyN2YxZWQ1NTA3NDU3NjEzMGYyNDdi
11
- NTM4ZTI3NmQ4ZTE1MmNjOGY5ZTBlNDk2YTQyZDQ0NzA5ZTc2NTE=
12
- data.tar.gz: !binary |-
13
- NGIwN2QwMjQ1OTQwNzY3M2MzYWFiNWI0Y2RjMjVmMTEzMTk3YjE1ZGJhNjQw
14
- YzRjNzhkZTRmZGExYjU2YzU5Y2Q4OTkyNzAyYTFhODRmNGViOGI0MTQyNmNh
15
- MjAwYjUxZjRkMGZjMjRmMDJhYjRiOWU2ZmNjNTFjNmEzZTY2YWQ=
6
+ metadata.gz: 6b4048b21713e5b87d4fca48995253fb500e28e82e38a57a6af489bfa0989706e6cb008f9894d2d87c7b5c7dc215aa32fdfcb3d65748cf5152a79e8c59e1813f
7
+ data.tar.gz: dd9967c547e09f5eebecc4dfcaf1fab6f16395786af08f8c0dbb09d92e3c6c5400c5d828536b5e66588086c9d9fd1e5af7cfcdebe438c92689b7c2bf066421fd
@@ -0,0 +1,15 @@
1
+ # Change Log
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ ## 0.3.0 - 2014-10-19
5
+ ### Added
6
+ - Added support for defining a logger with a Proc
7
+ - Return a X-Rack-CORS header when in debug mode detailing how
8
+ Rack::Cors processed a request
9
+ - Added support for non HTTP/HTTPS origins when just a domain is
10
+ is specified
11
+
12
+ ### Changed
13
+ - Changed the log level of the fallback logger to DEBUG
14
+ - Print warning when attempting to use :any as an allowed method
15
+ - Treat incoming `Origin: null` headers as file://
@@ -0,0 +1,113 @@
1
+ # Rack CORS Middleware [![Build Status](https://travis-ci.org/cyu/rack-cors.svg?branch=master)](https://travis-ci.org/cyu/rack-cors)
2
+
3
+ `Rack::Cors` provides support for Cross-Origin Resource Sharing (CORS) for Rack compatible web applications.
4
+
5
+ The [CORS spec](http://www.w3.org/TR/cors/) allows web applications to make cross domain AJAX calls without using workarounds such as JSONP. See [Cross-domain Ajax with Cross-Origin Resource Sharing](http://www.nczonline.net/blog/2010/05/25/cross-domain-ajax-with-cross-origin-resource-sharing/)
6
+
7
+ ## Installation
8
+
9
+ Install the gem:
10
+
11
+ `gem install rack-cors`
12
+
13
+ Or in your Gemfile:
14
+
15
+ ```ruby
16
+ gem 'rack-cors', :require => 'rack/cors'
17
+ ```
18
+
19
+
20
+ ## Configuration
21
+
22
+ ### Rack
23
+
24
+ In `config.ru`, configure `Rack::Cors` by passing a block to the `use` command:
25
+
26
+ ```ruby
27
+ use Rack::Cors do
28
+ allow do
29
+ origins 'localhost:3000', '127.0.0.1:3000',
30
+ /http:\/\/192\.168\.0\.\d{1,3}(:\d+)?/
31
+ # regular expressions can be used here
32
+
33
+ resource '/file/list_all/', :headers => 'x-domain-token'
34
+ resource '/file/at/*',
35
+ :methods => [:get, :post, :put, :delete, :options],
36
+ :headers => 'x-domain-token',
37
+ :expose => ['Some-Custom-Response-Header'],
38
+ :max_age => 600
39
+ # headers to expose
40
+ end
41
+
42
+ allow do
43
+ origins '*'
44
+ resource '/public/*', :headers => :any, :methods => :get
45
+ end
46
+ end
47
+ ```
48
+
49
+ ### Rails
50
+ Put something like the code below in `config/application.rb` of your Rails application. For example, this will allow GET, POST or OPTIONS requests from any origin on any resource.
51
+
52
+ ```ruby
53
+ module YourApp
54
+ class Application < Rails::Application
55
+
56
+ # ...
57
+
58
+ config.middleware.insert_before 0, "Rack::Cors" do
59
+ allow do
60
+ origins '*'
61
+ resource '*', :headers => :any, :methods => [:get, :post, :options]
62
+ end
63
+ end
64
+
65
+ end
66
+ end
67
+ ```
68
+ Refer to [rails 3 example](https://github.com/cyu/rack-cors/tree/master/examples/rails3) and [rails 4 example](https://github.com/cyu/rack-cors/tree/master/examples/rails4) for more details.
69
+
70
+ See The [Rails Guide to Rack](http://guides.rubyonrails.org/rails_on_rack.html) for more details on rack middlewares or watch the [railscast](http://railscasts.com/episodes/151-rack-middleware.)
71
+
72
+ ### Configuration Reference
73
+
74
+ #### Middleware Options
75
+ * **debug** (boolean): Enables debug logging and `X-Rack-CORS` HTTP headers for debugging.
76
+ * **logger** (Object or Proc): Specify the logger to log to. If a proc is provided, it will be called when a logger is needed (this is helpful in cases where the logger is initialized after `Rack::Cors` is used, like `Rails.logger`.
77
+
78
+ #### Origin
79
+ Origins can be specified as a string, a regular expression, or as '*' to allow all origins.
80
+
81
+ #### Resource
82
+ A Resource path can be specified as exact string match (`/path/to/file.txt`) or with a '*' wildcard (`/all/files/in/*`). A resource that take the following options:
83
+
84
+ * **methods** (string or array): The HTTP methods allowed for the resource.
85
+ * **headers** (string or array or `:any`): The HTTP headers that will be allowed in the CORS resource request. Use `:any` to allow for any headers in the actual request.
86
+ * **expose** (string or an array): The HTTP headers in the resource response can can be exposed to the client.
87
+ * **credentials** (boolean): Sets the `Access-Control-Allow-Credentials` response header.
88
+ * **max_age** (number): Sets the `Access-Control-Max-Age` response header.
89
+
90
+
91
+ ## Common Gotchas
92
+
93
+ Incorrect positioning of `Rack::Cors` in the middleware stack can produce unexpected results. The Rails example above will put it above all middleware which should cover most issues.
94
+
95
+ Here are some common cases:
96
+
97
+ * **Serving static files.** Insert this middleware before `ActionDispatch::Static` so that static files are served with the proper CORS headers (see note below for a caveat). **NOTE:** that this might not work in production environments as static files are usually served from the web server (Nginx, Apache) and not the Rails container.
98
+
99
+ * **Caching in the middleware.** Insert this middleware before `Rack::Cache` so that the proper CORS headers are written and not cached ones.
100
+
101
+ * **Authentication via Warden** Warden will return immediately if a resource that requires authentication is accessed without authentication. If `Warden::Manager`is in the stack before `Rack::Cors`, it will return without the correct CORS headers being applied, resulting in a failed CORS request. Be sure to insert this middleware before 'Warden::Manager`.
102
+
103
+ To determine where to put the CORS middleware in the Rack stack, run the following command:
104
+
105
+ ```bash
106
+ bundle exec rake middleware
107
+ ```
108
+
109
+ In many cases, the Rack stack will be different running in production environments. For example, the `ActionDispatch::Static` middleware will not be part of the stack if `config.serve_static_assets = false`. You can run the following command to see what your middleware stack looks like in production:
110
+
111
+ ```bash
112
+ bundle exec RAILS_ENV=production rake middleware
113
+ ```
@@ -2,11 +2,23 @@ require 'logger'
2
2
 
3
3
  module Rack
4
4
  class Cors
5
+ ENV_KEY = 'X_RACK_CORS'.freeze
6
+
7
+ ORIGIN_HEADER_KEY = 'HTTP_ORIGIN'.freeze
8
+ PATH_INFO_HEADER_KEY = 'PATH_INFO'.freeze
9
+
5
10
  def initialize(app, opts={}, &block)
6
11
  @app = app
7
- @logger = opts[:logger]
8
12
  @debug_mode = !!opts[:debug]
9
13
 
14
+ if logger = opts[:logger]
15
+ if logger.respond_to? :call
16
+ @logger_proc = opts[:logger]
17
+ else
18
+ @logger = logger
19
+ end
20
+ end
21
+
10
22
  if block_given?
11
23
  if block.arity == 1
12
24
  block.call(self)
@@ -16,6 +28,10 @@ module Rack
16
28
  end
17
29
  end
18
30
 
31
+ def debug?
32
+ @debug_mode
33
+ end
34
+
19
35
  def allow(&block)
20
36
  all_resources << (resources = Resources.new)
21
37
 
@@ -27,14 +43,13 @@ module Rack
27
43
  end
28
44
 
29
45
  def call(env)
30
- env['HTTP_ORIGIN'] = 'file://' if env['HTTP_ORIGIN'] == 'null'
31
- env['HTTP_ORIGIN'] ||= env['HTTP_X_ORIGIN']
46
+ env[ORIGIN_HEADER_KEY] ||= env['HTTP_X_ORIGIN']
32
47
 
33
- cors_headers = nil
34
- if env['HTTP_ORIGIN']
48
+ add_headers = nil
49
+ if env[ORIGIN_HEADER_KEY]
35
50
  debug(env) do
36
51
  [ 'Incoming Headers:',
37
- " Origin: #{env['HTTP_ORIGIN']}",
52
+ " Origin: #{env[ORIGIN_HEADER_KEY]}",
38
53
  " Access-Control-Request-Method: #{env['HTTP_ACCESS_CONTROL_REQUEST_METHOD']}",
39
54
  " Access-Control-Request-Headers: #{env['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}"
40
55
  ].join("\n")
@@ -48,12 +63,16 @@ module Rack
48
63
  return [200, headers, []]
49
64
  end
50
65
  else
51
- cors_headers = process_cors(env)
66
+ add_headers = process_cors(env)
52
67
  end
68
+ else
69
+ Result.miss(env, Result::MISS_NO_ORIGIN)
53
70
  end
71
+
54
72
  status, headers, body = @app.call env
55
- if cors_headers
56
- headers = headers.merge(cors_headers)
73
+
74
+ if add_headers
75
+ headers = headers.merge(add_headers)
57
76
 
58
77
  # http://www.w3.org/TR/cors/#resource-implementation
59
78
  unless headers['Access-Control-Allow-Origin'] == '*'
@@ -61,16 +80,33 @@ module Rack
61
80
  headers['Vary'] = ((vary ? vary.split(/,\s*/) : []) + ['Origin']).uniq.join(', ')
62
81
  end
63
82
  end
83
+
84
+ if debug? && result = env[ENV_KEY]
85
+ result.append_header(headers)
86
+ end
87
+
64
88
  [status, headers, body]
65
89
  end
66
90
 
67
91
  protected
68
92
  def debug(env, message = nil, &block)
69
- if @debug_mode
70
- logger = @logger || env['rack.logger'] || begin
71
- @logger = ::Logger.new(STDOUT).tap {|logger| logger.level = ::Logger::Severity::INFO}
72
- end
73
- logger.debug(message, &block)
93
+ (@logger || select_logger(env)).debug(message, &block) if debug?
94
+ end
95
+
96
+ def select_logger(env)
97
+ @logger = if @logger_proc
98
+ logger_proc = @logger_proc
99
+ @logger_proc = nil
100
+ logger_proc.call
101
+
102
+ elsif defined?(Rails) && Rails.logger
103
+ Rails.logger
104
+
105
+ elsif env['rack.logger']
106
+ env['rack.logger']
107
+
108
+ else
109
+ ::Logger.new(STDOUT).tap { |logger| logger.level = ::Logger::Severity::DEBUG }
74
110
  end
75
111
  end
76
112
 
@@ -79,22 +115,104 @@ module Rack
79
115
  end
80
116
 
81
117
  def process_preflight(env)
82
- resource = find_resource(env['HTTP_ORIGIN'], env['PATH_INFO'],env)
83
- resource && resource.process_preflight(env)
118
+ resource, error = find_resource(env)
119
+ if resource
120
+ Result.preflight_hit(env)
121
+ preflight = resource.process_preflight(env)
122
+ preflight
123
+
124
+ else
125
+ Result.preflight_miss(env, error)
126
+ nil
127
+ end
84
128
  end
85
129
 
86
130
  def process_cors(env)
87
- resource = find_resource(env['HTTP_ORIGIN'], env['PATH_INFO'],env)
88
- resource.to_headers(env) if resource
131
+ resource, error = find_resource(env)
132
+ if resource
133
+ Result.hit(env)
134
+ cors = resource.to_headers(env)
135
+ cors
136
+
137
+ else
138
+ Result.miss(env, error)
139
+ nil
140
+ end
89
141
  end
90
142
 
91
- def find_resource(origin, path, env)
143
+ def find_resource(env)
144
+ path = env[PATH_INFO_HEADER_KEY]
145
+ origin = env[ORIGIN_HEADER_KEY]
146
+
147
+ origin_matched = false
92
148
  all_resources.each do |r|
93
- if r.allow_origin?(origin, env) and found = r.find_resource(path)
94
- return found
149
+ if r.allow_origin?(origin, env)
150
+ origin_matched = true
151
+ if found = r.find_resource(path)
152
+ return [found, nil]
153
+ end
154
+ end
155
+ end
156
+
157
+ [nil, origin_matched ? Result::MISS_NO_PATH : Result::MISS_NO_ORIGIN]
158
+ end
159
+
160
+ class Result
161
+ HEADER_KEY = 'X-Rack-CORS'.freeze
162
+
163
+ MISS_NO_ORIGIN = 'no-origin'.freeze
164
+ MISS_NO_PATH = 'no-path'.freeze
165
+
166
+ attr_accessor :preflight, :hit, :miss_reason
167
+
168
+ def hit?
169
+ !!hit
170
+ end
171
+
172
+ def preflight?
173
+ !!preflight
174
+ end
175
+
176
+ def self.hit(env)
177
+ r = Result.new
178
+ r.preflight = false
179
+ r.hit = true
180
+ env[ENV_KEY] = r
181
+ end
182
+
183
+ def self.miss(env, reason)
184
+ r = Result.new
185
+ r.preflight = false
186
+ r.hit = false
187
+ r.miss_reason = reason
188
+ env[ENV_KEY] = r
189
+ end
190
+
191
+ def self.preflight_hit(env)
192
+ r = Result.new
193
+ r.preflight = true
194
+ r.hit = true
195
+ env[ENV_KEY] = r
196
+ end
197
+
198
+ def self.preflight_miss(env, reason)
199
+ r = Result.new
200
+ r.preflight = true
201
+ r.hit = false
202
+ r.miss_reason = reason
203
+ env[ENV_KEY] = r
204
+ end
205
+
206
+ def append_header(headers)
207
+ headers[HEADER_KEY] = if hit?
208
+ preflight? ? 'preflight-hit' : 'hit'
209
+ else
210
+ [
211
+ (preflight? ? 'preflight-miss' : 'preflight-hit'),
212
+ miss_reason
213
+ ].join('; ')
95
214
  end
96
215
  end
97
- nil
98
216
  end
99
217
 
100
218
  class Resources
@@ -104,13 +222,14 @@ module Rack
104
222
  @public_resources = false
105
223
  end
106
224
 
107
- def origins(*args,&blk)
225
+ def origins(*args, &blk)
108
226
  @origins = args.flatten.collect do |n|
109
227
  case n
110
- when Regexp, /^https?:\/\// then n
111
- when 'file://' then n
112
- when '*' then @public_resources = true; n
113
- else ["http://#{n}", "https://#{n}"]
228
+ when Regexp,
229
+ /^https?:\/\//,
230
+ 'file://' then n
231
+ when '*' then @public_resources = true; n
232
+ else Regexp.compile("^[a-z][a-z0-9.+-]*:\\\/\\\/#{Regexp.quote(n)}")
114
233
  end
115
234
  end.flatten
116
235
  @origins.push(blk) if blk
@@ -126,11 +245,14 @@ module Rack
126
245
 
127
246
  def allow_origin?(source,env = {})
128
247
  return true if public_resources?
248
+
249
+ effective_source = (source == 'null' ? 'file://' : source)
250
+
129
251
  return !! @origins.detect do |origin|
130
252
  if origin.is_a?(Proc)
131
253
  origin.call(source,env)
132
254
  else
133
- origin === source
255
+ origin === effective_source
134
256
  end
135
257
  end
136
258
  end
@@ -145,7 +267,7 @@ module Rack
145
267
 
146
268
  def initialize(public_resource, path, opts={})
147
269
  self.path = path
148
- self.methods = ensure_enum(opts[:methods]) || [:get]
270
+ self.methods = prepare_and_validate_methods_option(opts[:methods]) || [:get]
149
271
  self.credentials = opts[:credentials].nil? ? true : opts[:credentials]
150
272
  self.max_age = opts[:max_age] || 1728000
151
273
  self.pattern = compile(path)
@@ -173,7 +295,7 @@ module Rack
173
295
  def to_headers(env)
174
296
  x_origin = env['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']
175
297
  h = {
176
- 'Access-Control-Allow-Origin' => origin_for_response_header(env['HTTP_ORIGIN']),
298
+ 'Access-Control-Allow-Origin' => origin_for_response_header(env[ORIGIN_HEADER_KEY]),
177
299
  'Access-Control-Allow-Methods' => methods.collect{|m| m.to_s.upcase}.join(', '),
178
300
  'Access-Control-Expose-Headers' => expose.nil? ? '' : expose.join(', '),
179
301
  'Access-Control-Max-Age' => max_age.to_s }
@@ -188,7 +310,7 @@ module Rack
188
310
 
189
311
  def origin_for_response_header(origin)
190
312
  return '*' if public_resource? && !credentials
191
- origin == 'file://' ? 'null' : origin
313
+ origin
192
314
  end
193
315
 
194
316
  def to_preflight_headers(env)
@@ -217,6 +339,11 @@ module Rack
217
339
  end
218
340
  end
219
341
 
342
+ def prepare_and_validate_methods_option setting
343
+ raise ArgumentError.new("rack-cors methods must be an single HTTP verb, or an array of verbs") if setting && setting == :any
344
+ ensure_enum setting
345
+ end
346
+
220
347
  def ensure_enum(v)
221
348
  return nil if v.nil?
222
349
  [v].flatten
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class Cors
3
- VERSION = "0.2.9"
3
+ VERSION = "0.3.0"
4
4
  end
5
5
  end
@@ -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. Read more here: http://blog.sourcebender.com/2010/06/09/introducin-rack-cors.html. 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/ }
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.3"
22
22
  spec.add_development_dependency "rake"
23
- spec.add_development_dependency "shoulda", ">= 0"
23
+ spec.add_development_dependency "minitest", ">= 5.3.0"
24
24
  spec.add_development_dependency "mocha", ">= 0.14.0"
25
25
  spec.add_development_dependency "rack-test", ">= 0"
26
26
  end
@@ -7,6 +7,26 @@ 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 HEAD access to dynamic resource', (done) ->
16
+ $.ajax("http://#{CORS_SERVER}/", type: 'HEAD').done (data, textStatus, jqXHR) ->
17
+ expect(jqXHR.status).to.eql(200)
18
+ done()
19
+
20
+ it 'should allow DELETE access to dynamic resource', (done) ->
21
+ $.ajax("http://#{CORS_SERVER}/", type: 'DELETE').done (data, textStatus, jqXHR) ->
22
+ expect(data).to.eql('Hello world')
23
+ done()
24
+
25
+ it 'should allow OPTIONS access to dynamic resource', (done) ->
26
+ $.ajax("http://#{CORS_SERVER}/", type: 'OPTIONS').done (data, textStatus, jqXHR) ->
27
+ expect(jqXHR.status).to.eql(200)
28
+ done()
29
+
10
30
  it 'should allow access to static resource', (done) ->
11
31
  $.get "http://#{CORS_SERVER}/static.txt", (data, status, xhr) ->
12
32
  expect($.trim(data)).to.eql("hello world")
@@ -20,4 +40,3 @@ describe 'CORS', ->
20
40
  success:(data, status, xhr) ->
21
41
  expect($.trim(data)).to.eql("OK!")
22
42
  done()
23
-
@@ -1,3 +1,4 @@
1
+ // Generated by CoffeeScript 1.7.1
1
2
  (function() {
2
3
  var CORS_SERVER;
3
4
 
@@ -10,6 +11,38 @@
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 HEAD access to dynamic resource', function(done) {
23
+ return $.ajax("http://" + CORS_SERVER + "/", {
24
+ type: 'HEAD'
25
+ }).done(function(data, textStatus, jqXHR) {
26
+ expect(jqXHR.status).to.eql(200);
27
+ return done();
28
+ });
29
+ });
30
+ it('should allow DELETE access to dynamic resource', function(done) {
31
+ return $.ajax("http://" + CORS_SERVER + "/", {
32
+ type: 'DELETE'
33
+ }).done(function(data, textStatus, jqXHR) {
34
+ expect(data).to.eql('Hello world');
35
+ return done();
36
+ });
37
+ });
38
+ it('should allow OPTIONS access to dynamic resource', function(done) {
39
+ return $.ajax("http://" + CORS_SERVER + "/", {
40
+ type: 'OPTIONS'
41
+ }).done(function(data, textStatus, jqXHR) {
42
+ expect(jqXHR.status).to.eql(200);
43
+ return done();
44
+ });
45
+ });
13
46
  it('should allow access to static resource', function(done) {
14
47
  return $.get("http://" + CORS_SERVER + "/static.txt", function(data, status, xhr) {
15
48
  expect($.trim(data)).to.eql("hello world");
@@ -1,9 +1,9 @@
1
1
  require 'rubygems'
2
- require 'test/unit'
2
+ require 'minitest/autorun'
3
3
  require 'rack/test'
4
- require 'shoulda'
5
4
  require 'mocha/setup'
6
5
  require 'rack/cors'
6
+ require 'ostruct'
7
7
 
8
8
  Rack::Test::Session.class_eval do
9
9
  def options(uri, params = {}, env = {}, &block)
@@ -16,149 +16,231 @@ Rack::Test::Methods.class_eval do
16
16
  def_delegator :current_session, :options
17
17
  end
18
18
 
19
- class CorsTest < Test::Unit::TestCase
19
+ describe Rack::Cors do
20
20
  include Rack::Test::Methods
21
21
 
22
- def app
23
- eval "Rack::Builder.new {( " + File.read(File.dirname(__FILE__) + '/test.ru') + "\n )}"
22
+ attr_accessor :cors_result
23
+
24
+ def load_app(name)
25
+ test = self
26
+ Rack::Builder.new do
27
+ eval File.read(File.dirname(__FILE__) + "/#{name}.ru")
28
+ map('/') do
29
+ run proc { |env|
30
+ test.cors_result = env[Rack::Cors::ENV_KEY]
31
+ [200, {'Content-Type' => 'text/html'}, ['success']]
32
+ }
33
+ end
34
+ end
24
35
  end
25
36
 
26
- should('support simple cors request') { cors_request }
37
+ let(:app) { load_app('test') }
27
38
 
28
- should 'support OPTIONS cors request' do
39
+ it 'should support simple CORS request' do
40
+ cors_request
41
+ cors_result.must_be :hit
42
+ end
43
+
44
+ it "should not return CORS headers if Origin header isn't present" do
45
+ get '/'
46
+ should_render_cors_failure
47
+ cors_result.wont_be :hit
48
+ end
49
+
50
+ it 'should support OPTIONS CORS request' do
29
51
  cors_request '/options', :method => :options
30
52
  end
31
53
 
32
- should 'support regex origins configuration' do
54
+ it 'should support regex origins configuration' do
33
55
  cors_request :origin => 'http://192.168.0.1:1234'
34
56
  end
35
57
 
36
- should 'support proc origins configuration' do
58
+ it 'should support proc origins configuration' do
37
59
  cors_request '/proc-origin', :origin => 'http://10.10.10.10:3000'
38
60
  end
39
61
 
40
- should 'support alternative X-Origin header' do
62
+ it 'should support alternative X-Origin header' do
41
63
  header 'X-Origin', 'http://localhost:3000'
42
64
  get '/'
43
- assert_cors_success
65
+ should_render_cors_success
44
66
  end
45
67
 
46
- should 'support expose header configuration' do
68
+ it 'should support expose header configuration' do
47
69
  cors_request '/expose_single_header'
48
- assert_equal 'expose-test', last_response.headers['Access-Control-Expose-Headers']
70
+ last_response.headers['Access-Control-Expose-Headers'].must_equal 'expose-test'
49
71
  end
50
72
 
51
- should 'support expose multiple header configuration' do
73
+ it 'should support expose multiple header configuration' do
52
74
  cors_request '/expose_multiple_headers'
53
- assert_equal 'expose-test-1, expose-test-2', last_response.headers['Access-Control-Expose-Headers']
75
+ last_response.headers['Access-Control-Expose-Headers'].must_equal 'expose-test-1, expose-test-2'
54
76
  end
55
77
 
56
- should 'add Vary header if Access-Control-Allow-Origin header was added and if it is specific' do
78
+ it 'should add Vary header if Access-Control-Allow-Origin header was added and if it is specific' do
57
79
  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'
80
+ last_response.headers['Access-Control-Allow-Origin'].must_equal 'http://192.168.0.3:8080'
81
+ last_response.headers['Vary'].wont_be_nil
61
82
  end
62
83
 
63
- should 'not add Vary header if Access-Control-Allow-Origin header was added and if it is generic (*)' do
84
+ it 'should not add Vary header if Access-Control-Allow-Origin header was added and if it is generic (*)' do
64
85
  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'
86
+ last_response.headers['Access-Control-Allow-Origin'].must_equal '*'
87
+ last_response.headers['Vary'].must_be_nil
68
88
  end
69
89
 
70
- should 'support multi allow configurations for the same resource' do
90
+ it 'should support multi allow configurations for the same resource' do
71
91
  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'
92
+ last_response.headers['Access-Control-Allow-Origin'].must_equal 'http://mucho-grande.com'
93
+ last_response.headers['Vary'].must_equal 'Origin'
75
94
 
76
95
  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'
96
+ last_response.headers['Access-Control-Allow-Origin'].must_equal '*'
97
+ last_response.headers['Vary'].must_be_nil
80
98
  end
81
99
 
82
- should 'not log debug messages if debug option is false' do
83
- app = mock
84
- app.stubs(:call).returns(200, {}, [''])
100
+ it "should not return CORS headers on OPTIONS request if Access-Control-Allow-Origin is not present" do
101
+ options '/get-only'
102
+ last_response.headers['Access-Control-Allow-Origin'].must_be_nil
103
+ end
85
104
 
86
- logger = mock
87
- logger.expects(:debug).never
105
+ describe 'logging' do
106
+ it 'should not log debug messages if debug option is false' do
107
+ app = mock
108
+ app.stubs(:call).returns(200, {}, [''])
88
109
 
89
- cors = Rack::Cors.new(app, :debug => false, :logger => logger) {}
90
- cors.send(:debug, {}, 'testing')
91
- end
110
+ logger = mock
111
+ logger.expects(:debug).never
112
+
113
+ cors = Rack::Cors.new(app, :debug => false, :logger => logger) {}
114
+ cors.send(:debug, {}, 'testing')
115
+ end
116
+
117
+ it 'should log debug messages if debug option is true' do
118
+ app = mock
119
+ app.stubs(:call).returns(200, {}, [''])
120
+
121
+ logger = mock
122
+ logger.expects(:debug)
123
+
124
+ cors = Rack::Cors.new(app, :debug => true, :logger => logger) {}
125
+ cors.send(:debug, {}, 'testing')
126
+ end
127
+
128
+ it 'should use rack.logger if available' do
129
+ app = mock
130
+ app.stubs(:call).returns([200, {}, ['']])
131
+
132
+ logger = mock
133
+ logger.expects(:debug).at_least_once
92
134
 
93
- should 'log debug messages if debug option is true' do
94
- app = mock
95
- app.stubs(:call).returns(200, {}, [''])
135
+ cors = Rack::Cors.new(app, :debug => true) {}
136
+ cors.call({'rack.logger' => logger, 'HTTP_ORIGIN' => 'test.com'})
137
+ end
138
+
139
+ it 'should use logger proc' do
140
+ app = mock
141
+ app.stubs(:call).returns([200, {}, ['']])
142
+
143
+ logger = mock
144
+ logger.expects(:debug)
145
+
146
+ cors = Rack::Cors.new(app, :debug => true, :logger => proc { logger }) {}
147
+ cors.call({'HTTP_ORIGIN' => 'test.com'})
148
+ end
149
+
150
+ describe 'with Rails setup' do
151
+ after do
152
+ ::Rails.logger = nil if defined?(::Rails)
153
+ end
154
+
155
+ it 'should use Rails.logger if available' do
156
+ app = mock
157
+ app.stubs(:call).returns([200, {}, ['']])
158
+
159
+ logger = mock
160
+ logger.expects(:debug)
96
161
 
97
- logger = mock
98
- logger.expects(:debug)
162
+ ::Rails = OpenStruct.new(:logger => logger)
99
163
 
100
- cors = Rack::Cors.new(app, :debug => true, :logger => logger) {}
101
- cors.send(:debug, {}, 'testing')
164
+ cors = Rack::Cors.new(app, :debug => true) {}
165
+ cors.call({'HTTP_ORIGIN' => 'test.com'})
166
+ end
167
+ end
102
168
  end
103
169
 
104
- context 'preflight requests' do
105
- should 'fail if origin is invalid' do
170
+ describe 'preflight requests' do
171
+ it 'should fail if origin is invalid' do
106
172
  preflight_request('http://allyourdataarebelongtous.com', '/')
107
- assert_cors_failure
173
+ should_render_cors_failure
174
+ cors_result.wont_be :hit
175
+ cors_result.must_be :preflight
108
176
  end
109
177
 
110
- should 'fail if Access-Control-Request-Method is not allowed' do
178
+ it 'should fail if Access-Control-Request-Method is not allowed' do
111
179
  preflight_request('http://localhost:3000', '/get-only', :method => :post)
112
- assert_cors_failure
180
+ should_render_cors_failure
113
181
  end
114
182
 
115
- should 'fail if header is not allowed' do
183
+ it 'should fail if header is not allowed' do
116
184
  preflight_request('http://localhost:3000', '/single_header', :headers => 'Fooey')
117
- assert_cors_failure
185
+ should_render_cors_failure
118
186
  end
119
187
 
120
- should 'allow any header if headers = :any' do
188
+ it 'should allow any header if headers = :any' do
121
189
  preflight_request('http://localhost:3000', '/', :headers => 'Fooey')
122
- assert_cors_success
190
+ should_render_cors_success
123
191
  end
124
192
 
125
- should 'allow header case insensitive match' do
193
+ it 'should allow header case insensitive match' do
126
194
  preflight_request('http://localhost:3000', '/single_header', :headers => 'X-Domain-Token')
127
- assert_cors_success
195
+ should_render_cors_success
128
196
  end
129
197
 
130
- should 'allow multiple headers match' do
198
+ it 'should allow multiple headers match' do
131
199
  # Webkit style
132
200
  preflight_request('http://localhost:3000', '/two_headers', :headers => 'X-Requested-With, X-Domain-Token')
133
- assert_cors_success
201
+ should_render_cors_success
134
202
 
135
203
  # Gecko style
136
204
  preflight_request('http://localhost:3000', '/two_headers', :headers => 'x-requested-with,x-domain-token')
137
- assert_cors_success
205
+ should_render_cors_success
138
206
  end
139
207
 
140
- should '* origin should allow any origin' do
208
+ it 'should * origin should allow any origin' do
141
209
  preflight_request('http://locohost:3000', '/public')
142
- assert_cors_success
143
- assert_equal 'http://locohost:3000', last_response.headers['Access-Control-Allow-Origin']
210
+ should_render_cors_success
211
+ last_response.headers['Access-Control-Allow-Origin'].must_equal 'http://locohost:3000'
144
212
  end
145
213
 
146
- should '* origin should allow any origin, and set * if no credentials required' do
214
+ it 'should * origin should allow any origin, and set * if no credentials required' do
147
215
  preflight_request('http://locohost:3000', '/public_without_credentials')
148
- assert_cors_success
149
- assert_equal '*', last_response.headers['Access-Control-Allow-Origin']
216
+ should_render_cors_success
217
+ last_response.headers['Access-Control-Allow-Origin'].must_equal '*'
150
218
  end
151
219
 
152
- should '"null" origin, allowed as "file://", returned as "null" in header' do
220
+ it 'should "null" origin, allowed as "file://", returned as "null" in header' do
153
221
  preflight_request('null', '/')
154
- assert_cors_success
155
- assert_equal 'null', last_response.headers['Access-Control-Allow-Origin']
222
+ should_render_cors_success
223
+ last_response.headers['Access-Control-Allow-Origin'].must_equal 'null'
224
+ end
225
+
226
+ it 'should return "file://" as header with "file://" as origin' do
227
+ preflight_request('file://', '/')
228
+ should_render_cors_success
229
+ last_response.headers['Access-Control-Allow-Origin'].must_equal 'file://'
156
230
  end
157
231
 
158
- should 'return a Content-Type' do
232
+ it 'should return a Content-Type' do
159
233
  preflight_request('http://localhost:3000', '/')
160
- assert_cors_success
161
- assert_not_nil last_response.headers['Content-Type']
234
+ should_render_cors_success
235
+ last_response.headers['Content-Type'].wont_be_nil
236
+ end
237
+ end
238
+
239
+ describe "with non HTTP config" do
240
+ let(:app) { load_app("non_http") }
241
+
242
+ it 'should support non http/https origins' do
243
+ cors_request '/public', origin: 'content://com.company.app'
162
244
  end
163
245
  end
164
246
 
@@ -170,8 +252,8 @@ class CorsTest < Test::Unit::TestCase
170
252
  opts.merge! args.last if args.last.is_a?(Hash)
171
253
 
172
254
  header 'Origin', opts[:origin]
173
- current_session.__send__ opts[:method], path
174
- assert_cors_success
255
+ current_session.__send__ opts[:method], path, {}, test: self
256
+ should_render_cors_success
175
257
  end
176
258
 
177
259
  def preflight_request(origin, path, opts = {})
@@ -185,11 +267,11 @@ class CorsTest < Test::Unit::TestCase
185
267
  options path
186
268
  end
187
269
 
188
- def assert_cors_success
189
- assert_not_nil last_response.headers['Access-Control-Allow-Origin'], 'missing Access-Control-Allow-Origin header'
270
+ def should_render_cors_success
271
+ last_response.headers['Access-Control-Allow-Origin'].wont_be_nil
190
272
  end
191
273
 
192
- def assert_cors_failure
193
- assert_nil last_response.headers['Access-Control-Allow-Origin'], 'no expecting Access-Control-Allow-Origin header'
274
+ def should_render_cors_failure
275
+ last_response.headers['Access-Control-Allow-Origin'].must_be_nil
194
276
  end
195
277
  end
@@ -1,11 +1,10 @@
1
1
  require 'rubygems'
2
- require 'test/unit'
2
+ require 'minitest/autorun'
3
3
  require 'rack/cors'
4
- require 'shoulda'
5
4
 
6
5
 
7
- class DSLTest < Test::Unit::TestCase
8
- should 'support explicit config object dsl mode' do
6
+ describe Rack::Cors, 'DSL' do
7
+ it 'should support explicit config object dsl mode' do
9
8
  cors = Rack::Cors.new(Proc.new {}) do |cfg|
10
9
  cfg.allow do |allow|
11
10
  allow.origins 'localhost:3000', '127.0.0.1:3000' do |source,env|
@@ -17,15 +16,15 @@ class DSLTest < Test::Unit::TestCase
17
16
  end
18
17
  end
19
18
  resources = cors.send :all_resources
20
- assert_equal 1, resources.length
21
- assert resources.first.allow_origin?('http://localhost:3000')
22
19
 
23
- assert resources.first.allow_origin?('http://10.10.10.10:3000',{"USER_AGENT" => "test-agent" })
24
- assert !resources.first.allow_origin?('http://10.10.10.10:3001',{"USER_AGENT" => "test-agent" })
25
- assert !resources.first.allow_origin?('http://10.10.10.10:3000',{"USER_AGENT" => "other-agent"})
20
+ resources.length.must_equal 1
21
+ resources.first.allow_origin?('http://localhost:3000').must_equal true
22
+ resources.first.allow_origin?('http://10.10.10.10:3000',{"USER_AGENT" => "test-agent" }).must_equal true
23
+ resources.first.allow_origin?('http://10.10.10.10:3001',{"USER_AGENT" => "test-agent" }).wont_equal true
24
+ resources.first.allow_origin?('http://10.10.10.10:3000',{"USER_AGENT" => "other-agent"}).wont_equal true
26
25
  end
27
26
 
28
- should 'support implicit config object dsl mode' do
27
+ it 'should support implicit config object dsl mode' do
29
28
  cors = Rack::Cors.new(Proc.new {}) do
30
29
  allow do
31
30
  origins 'localhost:3000', '127.0.0.1:3000' do |source,env|
@@ -37,15 +36,15 @@ class DSLTest < Test::Unit::TestCase
37
36
  end
38
37
  end
39
38
  resources = cors.send :all_resources
40
- assert_equal 1, resources.length
41
- assert resources.first.allow_origin?('http://localhost:3000')
42
39
 
43
- assert resources.first.allow_origin?('http://10.10.10.10:3000',{"USER_AGENT" => "test-agent" })
44
- assert !resources.first.allow_origin?('http://10.10.10.10:3001',{"USER_AGENT" => "test-agent" })
45
- assert !resources.first.allow_origin?('http://10.10.10.10:3000',{"USER_AGENT" => "other-agent"})
40
+ resources.length.must_equal 1
41
+ resources.first.allow_origin?('http://localhost:3000').must_equal true
42
+ resources.first.allow_origin?('http://10.10.10.10:3000',{"USER_AGENT" => "test-agent" }).must_equal true
43
+ resources.first.allow_origin?('http://10.10.10.10:3001',{"USER_AGENT" => "test-agent" }).wont_equal true
44
+ resources.first.allow_origin?('http://10.10.10.10:3000',{"USER_AGENT" => "other-agent"}).wont_equal true
46
45
  end
47
46
 
48
- should 'support "file://" origin' do
47
+ it 'should support "file://" origin' do
49
48
  cors = Rack::Cors.new(Proc.new {}) do
50
49
  allow do
51
50
  origins 'file://'
@@ -53,6 +52,7 @@ class DSLTest < Test::Unit::TestCase
53
52
  end
54
53
  end
55
54
  resources = cors.send :all_resources
56
- assert resources.first.allow_origin?('file://')
55
+
56
+ resources.first.allow_origin?('file://').must_equal true
57
57
  end
58
58
  end
@@ -0,0 +1,8 @@
1
+ require 'rack/cors'
2
+
3
+ use Rack::Cors do
4
+ allow do
5
+ origins 'com.company.app'
6
+ resource '/public'
7
+ end
8
+ end
@@ -41,7 +41,3 @@ use Rack::Cors do
41
41
  resource '/multi-allow-config', :max_age => 300, :credentials => false
42
42
  end
43
43
  end
44
-
45
- map '/' do
46
- run Proc.new { |env| [200, {'Content-Type' => 'text/html'}, ['success']] }
47
- end
metadata CHANGED
@@ -1,97 +1,98 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-cors
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.9
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Calvin Yu
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-12 00:00:00.000000000 Z
11
+ date: 2014-12-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.3'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.3'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ! '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ! '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: shoulda
42
+ name: minitest
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ! '>='
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: 5.3.0
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ! '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: 5.3.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: mocha
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ! '>='
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: 0.14.0
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ! '>='
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: 0.14.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rack-test
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ! '>='
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ! '>='
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
- description: ! 'Middleware that will make Rack-based apps CORS compatible. Read more
83
+ description: 'Middleware that will make Rack-based apps CORS compatible. Read more
84
84
  here: http://blog.sourcebender.com/2010/06/09/introducin-rack-cors.html. Fork the
85
- project here: http://github.com/cyu/rack-cors'
85
+ project here: https://github.com/cyu/rack-cors'
86
86
  email:
87
87
  - me@sourcebender.com
88
88
  executables: []
89
89
  extensions: []
90
90
  extra_rdoc_files: []
91
91
  files:
92
+ - CHANGELOG
92
93
  - Gemfile
93
94
  - LICENSE.txt
94
- - README.rdoc
95
+ - README.md
95
96
  - Rakefile
96
97
  - lib/rack/cors.rb
97
98
  - lib/rack/cors/version.rb
@@ -104,8 +105,9 @@ files:
104
105
  - test/cors/test.cors.js
105
106
  - test/unit/cors_test.rb
106
107
  - test/unit/dsl_test.rb
108
+ - test/unit/non_http.ru
107
109
  - test/unit/test.ru
108
- homepage: http://github.com/cyu/rack-cors
110
+ homepage: https://github.com/cyu/rack-cors
109
111
  licenses:
110
112
  - MIT
111
113
  metadata: {}
@@ -115,17 +117,17 @@ require_paths:
115
117
  - lib
116
118
  required_ruby_version: !ruby/object:Gem::Requirement
117
119
  requirements:
118
- - - ! '>='
120
+ - - ">="
119
121
  - !ruby/object:Gem::Version
120
122
  version: '0'
121
123
  required_rubygems_version: !ruby/object:Gem::Requirement
122
124
  requirements:
123
- - - ! '>='
125
+ - - ">="
124
126
  - !ruby/object:Gem::Version
125
127
  version: '0'
126
128
  requirements: []
127
129
  rubyforge_project:
128
- rubygems_version: 2.1.9
130
+ rubygems_version: 2.2.2
129
131
  signing_key:
130
132
  specification_version: 4
131
133
  summary: Middleware for enabling Cross-Origin Resource Sharing in Rack apps
@@ -138,4 +140,5 @@ test_files:
138
140
  - test/cors/test.cors.js
139
141
  - test/unit/cors_test.rb
140
142
  - test/unit/dsl_test.rb
143
+ - test/unit/non_http.ru
141
144
  - test/unit/test.ru
@@ -1,66 +0,0 @@
1
- = Rack CORS Middleware
2
-
3
- Rack::Cors provides support for Cross-Origin Resource Sharing (CORS) for Rack compatible web applications. The CORS spec allows web applications to make cross domain AJAX calls without
4
- using workarounds such as JSONP. For a thorough write up on CORS, see this blog post:
5
-
6
- http://www.nczonline.net/blog/2010/05/25/cross-domain-ajax-with-cross-origin-resource-sharing/
7
-
8
- Or for all the gory details, you can read the spec here:
9
-
10
- http://www.w3.org/TR/access-control/#simple-cross-origin-request-and-actual-r
11
-
12
-
13
- Install the gem:
14
-
15
- gem install rack-cors
16
-
17
- In your Gemfile:
18
-
19
- gem 'rack-cors', :require => 'rack/cors'
20
-
21
-
22
- == Configuration
23
-
24
- You configure Rack::Cors by passing a block to the <tt>use</tt> command:
25
-
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/*',
34
- :methods => [:get, :post, :put, :delete, :options],
35
- :headers => 'x-domain-token',
36
- :expose => ['Some-Custom-Response-Header'],
37
- :max_age => 600
38
- # headers to expose
39
- end
40
-
41
- allow do
42
- origins '*'
43
- resource '/public/*', :headers => :any, :methods => :get
44
- end
45
- end
46
-
47
- Put your code in "config/application.rb" on your rails application. For example, this will allow
48
- from any origins on any resource of your application, methods :get, :post and :options.
49
-
50
- module YourApp
51
- class Application < Rails::Application
52
-
53
- # ...
54
-
55
- config.middleware.use Rack::Cors do
56
- allow do
57
- origins '*'
58
- resource '*', :headers => :any, :methods => [:get, :post, :options]
59
- end
60
- end
61
-
62
- end
63
- end
64
-
65
- See http://guides.rubyonrails.org/rails_on_rack.html for more details on rack middlewares or
66
- http://railscasts.com/episodes/151-rack-middleware.