1 # frozen_string_literal: true
5 require_relative 'constants'
8 # Rack::URLMap takes a hash mapping urls or paths to apps, and
9 # dispatches accordingly. Support for HTTP/1.1 host names exists if
10 # the URLs start with <tt>http://</tt> or <tt>https://</tt>.
12 # URLMap modifies the SCRIPT_NAME and PATH_INFO such that the part
13 # relevant for dispatch is in the SCRIPT_NAME, and the rest in the
14 # PATH_INFO. This should be taken care of when you need to
15 # reconstruct the URL in order to create links.
17 # URLMap dispatches in such a way that the longest paths are tried
18 # first, since they are most specific.
21 def initialize(map = {})
27 @mapping = map.map { |location, app|
28 if location =~ %r{\Ahttps?://(.*?)(/.*)}
29 host, location = $1, $2
35 unless location[0] == ?/
36 raise ArgumentError, "paths need to start with /"
39 location = location.chomp('/')
40 match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)", Regexp::NOENCODING)
42 [host, location, match, app]
43 }.sort_by do |(host, location, _, _)|
44 [host ? -host.size : Float::INFINITY, -location.size]
50 script_name = env[SCRIPT_NAME]
51 http_host = env[HTTP_HOST]
52 server_name = env[SERVER_NAME]
53 server_port = env[SERVER_PORT]
55 is_same_server = casecmp?(http_host, server_name) ||
56 casecmp?(http_host, "#{server_name}:#{server_port}")
58 is_host_known = @known_hosts.include? http_host
60 @mapping.each do |host, location, match, app|
61 unless casecmp?(http_host, host) \
62 || casecmp?(server_name, host) \
63 || (!host && is_same_server) \
64 || (!host && !is_host_known) # If we don't have a matching host, default to the first without a specified host
68 next unless m = match.match(path.to_s)
71 next unless !rest || rest.empty? || rest[0] == ?/
73 env[SCRIPT_NAME] = (script_name + location)
79 [404, { CONTENT_TYPE => "text/plain", "x-cascade" => "pass" }, ["Not Found: #{path}"]]
83 env[SCRIPT_NAME] = script_name
88 # if both nil, or they're the same string
89 return true if v1 == v2
91 # if either are nil... (but they're not the same)
92 return false if v1.nil?
93 return false if v2.nil?
95 # otherwise check they're not case-insensitive the same