2017-07-24 16:20:53 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2017-06-30 01:10:34 -04:00
|
|
|
require_relative "router/utils"
|
|
|
|
require_relative "routes"
|
|
|
|
require_relative "formatter"
|
2012-12-19 15:54:47 -05:00
|
|
|
|
|
|
|
before = $-w
|
|
|
|
$-w = false
|
2017-06-30 01:10:34 -04:00
|
|
|
require_relative "parser"
|
2012-12-19 15:54:47 -05:00
|
|
|
$-w = before
|
|
|
|
|
2017-06-30 01:10:34 -04:00
|
|
|
require_relative "route"
|
|
|
|
require_relative "path/pattern"
|
2012-12-19 15:54:47 -05:00
|
|
|
|
|
|
|
module ActionDispatch
|
2012-12-19 19:24:25 -05:00
|
|
|
module Journey # :nodoc:
|
|
|
|
class Router # :nodoc:
|
|
|
|
class RoutingError < ::StandardError # :nodoc:
|
2012-12-19 15:54:47 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
attr_accessor :routes
|
|
|
|
|
2014-05-23 13:57:15 -04:00
|
|
|
def initialize(routes)
|
|
|
|
@routes = routes
|
2012-12-19 15:54:47 -05:00
|
|
|
end
|
|
|
|
|
2017-01-11 08:50:42 -05:00
|
|
|
def eager_load!
|
|
|
|
# Eagerly trigger the simulator's initialization so
|
|
|
|
# it doesn't happen during a request cycle.
|
|
|
|
simulator
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
2014-05-23 13:25:26 -04:00
|
|
|
def serve(req)
|
2014-05-23 13:16:03 -04:00
|
|
|
find_routes(req).each do |match, parameters, route|
|
2014-05-23 12:56:33 -04:00
|
|
|
set_params = req.path_parameters
|
|
|
|
path_info = req.path_info
|
|
|
|
script_name = req.script_name
|
2012-12-19 15:54:47 -05:00
|
|
|
|
|
|
|
unless route.path.anchored
|
2016-08-06 12:51:43 -04:00
|
|
|
req.script_name = (script_name.to_s + match.to_s).chomp("/")
|
2014-05-23 12:56:33 -04:00
|
|
|
req.path_info = match.post_match
|
2014-06-12 02:15:29 -04:00
|
|
|
req.path_info = "/" + req.path_info unless req.path_info.start_with? "/"
|
2012-12-19 15:54:47 -05:00
|
|
|
end
|
|
|
|
|
2017-08-01 13:02:53 -04:00
|
|
|
parameters = route.defaults.merge parameters.transform_values { |val|
|
|
|
|
val.dup.force_encoding(::Encoding::UTF_8)
|
|
|
|
}
|
|
|
|
|
2014-05-22 18:57:24 -04:00
|
|
|
req.path_parameters = set_params.merge parameters
|
2012-12-19 15:54:47 -05:00
|
|
|
|
2014-05-25 17:26:48 -04:00
|
|
|
status, headers, body = route.app.serve(req)
|
2012-12-19 15:54:47 -05:00
|
|
|
|
2016-08-06 12:51:43 -04:00
|
|
|
if "pass" == headers["X-Cascade"]
|
2014-05-23 12:56:33 -04:00
|
|
|
req.script_name = script_name
|
|
|
|
req.path_info = path_info
|
2014-05-22 18:57:24 -04:00
|
|
|
req.path_parameters = set_params
|
2012-12-19 15:54:47 -05:00
|
|
|
next
|
|
|
|
end
|
|
|
|
|
|
|
|
return [status, headers, body]
|
|
|
|
end
|
|
|
|
|
2016-08-16 03:30:11 -04:00
|
|
|
return [404, { "X-Cascade" => "pass" }, ["Not Found"]]
|
2012-12-19 15:54:47 -05:00
|
|
|
end
|
|
|
|
|
2014-05-23 13:52:58 -04:00
|
|
|
def recognize(rails_req)
|
2014-05-23 13:16:03 -04:00
|
|
|
find_routes(rails_req).each do |match, parameters, route|
|
2012-12-19 15:54:47 -05:00
|
|
|
unless route.path.anchored
|
2014-05-23 13:17:57 -04:00
|
|
|
rails_req.script_name = match.to_s
|
|
|
|
rails_req.path_info = match.post_match.sub(/^([^\/])/, '/\1')
|
2012-12-19 15:54:47 -05:00
|
|
|
end
|
|
|
|
|
2017-08-01 13:02:53 -04:00
|
|
|
parameters = route.defaults.merge parameters
|
2014-05-21 19:12:49 -04:00
|
|
|
yield(route, parameters)
|
2012-12-19 15:54:47 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def visualizer
|
|
|
|
tt = GTG::Builder.new(ast).transition_table
|
2014-10-27 12:28:53 -04:00
|
|
|
groups = partitioned_routes.first.map(&:ast).group_by(&:to_s)
|
|
|
|
asts = groups.values.map(&:first)
|
2012-12-20 15:42:39 -05:00
|
|
|
tt.visualizer(asts)
|
2012-12-19 15:54:47 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
def partitioned_routes
|
2016-08-13 20:44:24 -04:00
|
|
|
routes.partition { |r|
|
|
|
|
r.path.anchored && r.ast.grep(Nodes::Symbol).all? { |n| n.default_regexp? }
|
|
|
|
}
|
2012-12-20 15:42:39 -05:00
|
|
|
end
|
2012-12-19 15:54:47 -05:00
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
def ast
|
|
|
|
routes.ast
|
|
|
|
end
|
2012-12-19 15:54:47 -05:00
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
def simulator
|
|
|
|
routes.simulator
|
|
|
|
end
|
2012-12-19 15:54:47 -05:00
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
def custom_routes
|
2014-06-18 22:16:30 -04:00
|
|
|
routes.custom_routes
|
2012-12-20 15:42:39 -05:00
|
|
|
end
|
2012-12-19 15:54:47 -05:00
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
def filter_routes(path)
|
|
|
|
return [] unless ast
|
2014-04-01 18:55:48 -04:00
|
|
|
simulator.memos(path) { [] }
|
2012-12-20 15:42:39 -05:00
|
|
|
end
|
2012-12-19 15:54:47 -05:00
|
|
|
|
2016-08-06 14:20:22 -04:00
|
|
|
def find_routes(req)
|
2012-12-20 15:42:39 -05:00
|
|
|
routes = filter_routes(req.path_info).concat custom_routes.find_all { |r|
|
|
|
|
r.path.match(req.path_info)
|
|
|
|
}
|
2014-05-25 16:48:30 -04:00
|
|
|
|
2014-07-04 00:32:14 -04:00
|
|
|
routes =
|
2015-08-14 13:20:28 -04:00
|
|
|
if req.head?
|
2014-07-04 00:32:14 -04:00
|
|
|
match_head_routes(routes, req)
|
|
|
|
else
|
|
|
|
match_routes(routes, req)
|
|
|
|
end
|
2012-12-19 15:54:47 -05:00
|
|
|
|
2014-06-20 10:16:11 -04:00
|
|
|
routes.sort_by!(&:precedence)
|
2012-12-19 15:54:47 -05:00
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
routes.map! { |r|
|
2016-10-28 23:05:58 -04:00
|
|
|
match_data = r.path.match(req.path_info)
|
2017-08-01 13:02:53 -04:00
|
|
|
path_parameters = {}
|
2016-10-28 23:05:58 -04:00
|
|
|
match_data.names.zip(match_data.captures) { |name, val|
|
2014-05-21 19:21:33 -04:00
|
|
|
path_parameters[name.to_sym] = Utils.unescape_uri(val) if val
|
|
|
|
}
|
2014-05-21 19:33:06 -04:00
|
|
|
[match_data, path_parameters, r]
|
2012-12-20 15:42:39 -05:00
|
|
|
}
|
|
|
|
end
|
2012-12-19 15:54:47 -05:00
|
|
|
|
2014-07-04 00:32:14 -04:00
|
|
|
def match_head_routes(routes, req)
|
2015-08-14 13:09:09 -04:00
|
|
|
verb_specific_routes = routes.select(&:requires_matching_verb?)
|
2015-03-20 14:13:59 -04:00
|
|
|
head_routes = match_routes(verb_specific_routes, req)
|
2014-07-04 00:32:14 -04:00
|
|
|
|
|
|
|
if head_routes.empty?
|
|
|
|
begin
|
|
|
|
req.request_method = "GET"
|
|
|
|
match_routes(routes, req)
|
|
|
|
ensure
|
|
|
|
req.request_method = "HEAD"
|
|
|
|
end
|
|
|
|
else
|
|
|
|
head_routes
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def match_routes(routes, req)
|
|
|
|
routes.select { |r| r.matches?(req) }
|
2012-12-20 15:42:39 -05:00
|
|
|
end
|
2012-12-19 15:54:47 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|