Class: Rack::Rewrite::Rule

Inherits:
Object
  • Object
show all
Defined in:
lib/rack/rewrite/rule.rb

Overview

:nodoc:

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(rule_type, from, to, options = {}) ⇒ Rule

Returns a new instance of Rule.

Parameters:

  • rule_type (Symbol)

    rule type (e.g. :rewrite, :r301)

  • from (Regexp, String)

    matcher for the incoming path

  • to (String)

    target path or body

  • options (Hash) (defaults to: {})

    rule options

Options Hash (options):

  • method (Array<String>)

    restrict matching to these HTTP methods

  • if (Proc)

    guard evaluated against the rack env

  • headers (Hash)

    response headers for send rules



151
152
153
154
155
156
# File 'lib/rack/rewrite/rule.rb', line 151

def initialize(rule_type, from, to, options = {}) # :nodoc:
  @rule_type = rule_type
  @from = from
  @to = to
  @options = normalize_options(options)
end

Instance Attribute Details

#optionsObject (readonly)

Returns the value of attribute options.



142
143
144
# File 'lib/rack/rewrite/rule.rb', line 142

def options
  @options
end

#rule_typeObject (readonly)

Returns the value of attribute rule_type.



142
143
144
# File 'lib/rack/rewrite/rule.rb', line 142

def rule_type
  @rule_type
end

#toObject (readonly)

Returns the value of attribute to.



142
143
144
# File 'lib/rack/rewrite/rule.rb', line 142

def to
  @to
end

Instance Method Details

#apply!(env) ⇒ Array, Boolean

Either (a) return a Rack response (short-circuiting the Rack stack), or
(b) alter env as necessary and return true

Returns:

  • (Array, Boolean)

    a Rack response triplet, or true when the env was rewritten



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/rack/rewrite/rule.rb', line 176

def apply!(env) # :nodoc:
  interpreted_to = interpret_to(env)
  additional_headers = {}
  if @options[:headers]
    additional_headers = if @options[:headers].respond_to?(:call)
                           @options[:headers].call || {}
                         else
                           @options[:headers] || {}
                         end
  end
  status = @options[:status] || 200
  case rule_type
  when :r301
    [301, { 'Location' => interpreted_to, 'Content-Type' => Rack::Mime.mime_type(::File.extname(interpreted_to)) }.merge!(additional_headers), [redirect_message(interpreted_to)]]
  when :r302
    [302, { 'Location' => interpreted_to, 'Content-Type' => Rack::Mime.mime_type(::File.extname(interpreted_to)) }.merge!(additional_headers), [redirect_message(interpreted_to)]]
  when :r303
    [303, { 'Location' => interpreted_to, 'Content-Type' => Rack::Mime.mime_type(::File.extname(interpreted_to)) }.merge!(additional_headers), [redirect_message(interpreted_to)]]
  when :r307
    [307, { 'Location' => interpreted_to, 'Content-Type' => Rack::Mime.mime_type(::File.extname(interpreted_to)) }.merge!(additional_headers), [redirect_message(interpreted_to)]]
  when :rewrite
    # return [200, {}, {:content => env.inspect}]
    env['REQUEST_URI'] = interpreted_to
    if (q_index = interpreted_to.index('?'))
      env['PATH_INFO'] = interpreted_to[0..(q_index - 1)]
      env['QUERY_STRING'] = interpreted_to[(q_index + 1)..-1]
    else
      env['PATH_INFO'] = interpreted_to
      env['QUERY_STRING'] = ''
    end
    true
  when :send_file
    [status, {
      'Content-Length' => ::File.size(interpreted_to).to_s,
      'Content-Type'   => Rack::Mime.mime_type(::File.extname(interpreted_to))
      }.merge!(additional_headers), [::File.read(interpreted_to)]]
  when :x_send_file
    [status, {
      'X-Sendfile'     => interpreted_to,
      'Content-Length' => ::File.size(interpreted_to).to_s,
      'Content-Type'   => Rack::Mime.mime_type(::File.extname(interpreted_to))
      }.merge!(additional_headers), []]
  when :send_data
    [status, {
      'Content-Length' => interpreted_to.bytesize,
      'Content-Type' => 'text/html'
    }.merge!(additional_headers), [interpreted_to]]
  else
    raise Exception, "Unsupported rule: #{rule_type}"
  end
end

#fromString, ...

Returns the resolved from matcher.

Returns:

  • (String, Regexp, Proc)

    the resolved from matcher



167
168
169
170
171
# File 'lib/rack/rewrite/rule.rb', line 167

def from
  return @static_from if @static_from

  @from.respond_to?(:call) ? @from.call : @static_from = @from
end

#interpret_to(env) ⇒ Object (protected)

Returns the interpreted destination (String or Proc result).

Returns:

  • (Object)

    the interpreted destination (String or Proc result)



231
232
233
234
235
236
237
# File 'lib/rack/rewrite/rule.rb', line 231

def interpret_to(env) # :nodoc:
  path = build_path_from_env(env)
  return interpret_to_proc(path, env) if to.is_a?(Proc)
  return computed_to(path) if compute_to?(path)

  to
end

#is_a_regexp?(obj) ⇒ Boolean (protected)

Returns:

  • (Boolean)


239
240
241
# File 'lib/rack/rewrite/rule.rb', line 239

def is_a_regexp?(obj)
  obj.is_a?(Regexp) || (Object.const_defined?(:Oniguruma) && obj.is_a?(Oniguruma::ORegexp))
end

#match_options?(env, path = build_path_from_env(env)) ⇒ Boolean (protected)

Returns:

  • (Boolean)


243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/rack/rewrite/rule.rb', line 243

def match_options?(env, path = build_path_from_env(env))
  matches = []
  request = Rack::Request.new(env)

  # negative matches
  matches << !string_matches?(path, options[:not]) if options[:not]

  # positive matches
  matches << string_matches?(env['REQUEST_METHOD'], options[:method]) if options[:method]
  matches << string_matches?(request.host, options[:host]) if options[:host]
  matches << string_matches?(request.scheme, options[:scheme]) if options[:scheme]

  if options[:country]
    # US, CA, etc.  HTTP_CF_IPCOUNTRY is provided by cloudflare
    country = env['HTTP_CF_IPCOUNTRY'] || (request.respond_to?(:location) && request.location&.country)
    if country && country != 'XX'
      country = 'CA' if country.upcase == 'CANADA' # Make ISO
      matches << string_matches?(country, options[:country])
    end
  end

  matches.all?
end

#matches?(rack_env) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


158
159
160
161
162
163
164
# File 'lib/rack/rewrite/rule.rb', line 158

def matches?(rack_env) # :nodoc:
  return false if options[:if].respond_to?(:call) && !options[:if].call(rack_env)

  path = build_path_from_env(rack_env)

  match_options?(rack_env) && string_matches?(path, from)
end