class Rack::Cors::Resource

Constants

CORS_SIMPLE_HEADERS

All CORS routes need to accept CORS simple headers at all times {developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers}

Attributes

credentials[RW]
expose[RW]
headers[RW]
if_proc[RW]
max_age[RW]
methods[RW]
path[RW]
pattern[RW]
vary_headers[RW]

Public Class Methods

new(public_resource, path, opts = {}) click to toggle source
# File lib/rack/cors/resource.rb, line 12
def initialize(public_resource, path, opts = {})
  raise CorsMisconfigurationError if public_resource && opts[:credentials] == true

  self.path         = path
  self.credentials  = public_resource ? false : (opts[:credentials] == true)
  self.max_age      = opts[:max_age] || 7200
  self.pattern      = compile(path)
  self.if_proc      = opts[:if]
  self.vary_headers = opts[:vary] && [opts[:vary]].flatten
  @public_resource  = public_resource

  self.headers = case opts[:headers]
                 when :any then :any
                 when nil then nil
                 else
                   [opts[:headers]].flatten.collect(&:downcase)
                 end

  self.methods = case opts[:methods]
                 when :any then %i[get head post put patch delete options]
                 else
                   ensure_enum(opts[:methods]) || [:get]
                 end.map(&:to_s)

  self.expose = opts[:expose] ? [opts[:expose]].flatten : nil
end

Public Instance Methods

match?(path, env) click to toggle source
# File lib/rack/cors/resource.rb, line 43
def match?(path, env)
  matches_path?(path) && (if_proc.nil? || if_proc.call(env))
end
matches_path?(path) click to toggle source
# File lib/rack/cors/resource.rb, line 39
def matches_path?(path)
  pattern =~ path
end
process_preflight(env, result) click to toggle source
# File lib/rack/cors/resource.rb, line 47
def process_preflight(env, result)
  headers = {}

  request_method = env[Rack::Cors::HTTP_ACCESS_CONTROL_REQUEST_METHOD]
  result.miss(Result::MISS_NO_METHOD) && (return headers) if request_method.nil?
  result.miss(Result::MISS_DENY_METHOD) && (return headers) unless methods.include?(request_method.downcase)

  request_headers = env[Rack::Cors::HTTP_ACCESS_CONTROL_REQUEST_HEADERS]
  result.miss(Result::MISS_DENY_HEADER) && (return headers) if request_headers && !allow_headers?(request_headers)

  result.hit = true
  headers.merge(to_preflight_headers(env))
end
to_headers(env) click to toggle source
# File lib/rack/cors/resource.rb, line 61
def to_headers(env)
  h = {
    'access-control-allow-origin' => origin_for_response_header(env[Rack::Cors::HTTP_ORIGIN]),
    'access-control-allow-methods' => methods.collect { |m| m.to_s.upcase }.join(', '),
    'access-control-expose-headers' => expose.nil? ? '' : expose.join(', '),
    'access-control-max-age' => max_age.to_s
  }
  h['access-control-allow-credentials'] = 'true' if credentials
  header_proc.call(h)
end

Protected Instance Methods

allow_headers?(request_headers) click to toggle source
# File lib/rack/cors/resource.rb, line 90
def allow_headers?(request_headers)
  headers = self.headers || []
  return true if headers == :any

  request_headers = request_headers.split(/,\s*/) if request_headers.is_a?(String)
  request_headers.all? do |header|
    header = header.downcase
    CORS_SIMPLE_HEADERS.include?(header) || headers.include?(header)
  end
end
compile(path) click to toggle source
# File lib/rack/cors/resource.rb, line 107
def compile(path)
  if path.respond_to? :to_str
    special_chars = %w[. + ( )]
    pattern =
      path.to_str.gsub(%r{((:\w+)|/\*|[\*#{special_chars.join}])}) do |match|
        case match
        when '/*'
          '\\/?(.*?)'
        when '*'
          '(.*?)'
        when *special_chars
          Regexp.escape(match)
        else
          '([^/?&#]+)'
        end
      end
    /^#{pattern}$/
  elsif path.respond_to? :match
    path
  else
    raise TypeError, path
  end
end
ensure_enum(var) click to toggle source
# File lib/rack/cors/resource.rb, line 101
def ensure_enum(var)
  return nil if var.nil?

  [var].flatten
end
header_proc() click to toggle source
# File lib/rack/cors/resource.rb, line 131
def header_proc
  @header_proc ||= begin
    if defined?(Rack::Headers)
      ->(h) { h }
    else
      ->(h) { Rack::Utils::HeaderHash.new(h) }
    end
  end
end
origin_for_response_header(origin) click to toggle source
# File lib/rack/cors/resource.rb, line 78
def origin_for_response_header(origin)
  return '*' if public_resource?

  origin
end
public_resource?() click to toggle source
# File lib/rack/cors/resource.rb, line 74
def public_resource?
  @public_resource
end
to_preflight_headers(env) click to toggle source
# File lib/rack/cors/resource.rb, line 84
def to_preflight_headers(env)
  h = to_headers(env)
  h.merge!('access-control-allow-headers' => env[Rack::Cors::HTTP_ACCESS_CONTROL_REQUEST_HEADERS]) if env[Rack::Cors::HTTP_ACCESS_CONTROL_REQUEST_HEADERS]
  h
end