Class: Rack::URLMap

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

Overview

Rack::URLMap takes a hash mapping urls or paths to apps, and dispatches accordingly. Support for HTTP/1.1 host names exists if the URLs start with http:// or https://.

URLMap modifies the SCRIPT_NAME and PATH_INFO such that the part relevant for dispatch is in the SCRIPT_NAME, and the rest in the PATH_INFO. This should be taken care of when you need to reconstruct the URL in order to create links.

URLMap dispatches in such a way that the longest paths are tried first, since they are most specific.

Instance Method Summary collapse

Constructor Details

#initialize(map = {}) ⇒ URLMap

Returns a new instance of URLMap.



15
16
17
# File 'lib/rack/urlmap.rb', line 15

def initialize(map = {})
  remap(map)
end

Instance Method Details

#call(env) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/rack/urlmap.rb', line 37

def call(env)
  path = env["PATH_INFO"]
  script_name = env['SCRIPT_NAME']
  hHost, sName, sPort = env.values_at('HTTP_HOST','SERVER_NAME','SERVER_PORT')
  @mapping.each { |host, location, match, app|
    next unless (hHost == host || sName == host \
      || (host.nil? && (hHost == sName || hHost == sName+':'+sPort)))
    next unless path.to_s =~ match && rest = $1
    next unless rest.empty? || rest[0] == ?/
    env.merge!('SCRIPT_NAME' => (script_name + location), 'PATH_INFO' => rest)
    return app.call(env)
  }
  [404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found: #{path}"]]
ensure
  env.merge! 'PATH_INFO' => path, 'SCRIPT_NAME' => script_name
end

#remap(map) ⇒ Object



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/rack/urlmap.rb', line 19

def remap(map)
  @mapping = map.map { |location, app|
    if location =~ %r{\Ahttps?://(.*?)(/.*)}
      host, location = $1, $2
    else
      host = nil
    end

    unless location[0] == ?/
      raise ArgumentError, "paths need to start with /"
    end
    location = location.chomp('/')
    match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)", nil, 'n')

    [host, location, match, app]
  }.sort_by { |(h, l, m, a)| [h ? -h.size : (-1.0 / 0.0), -l.size] }  # Longest path first
end