Skip to content

Commit da26f40

Browse files
authored
Merge pull request rails#54550 from skipkayhil/hm-fix-recognize-mutating-request
Fix `#recognize` inconsistency with `#serve`
2 parents d0b6797 + e533a32 commit da26f40

File tree

4 files changed

+73
-74
lines changed

4 files changed

+73
-74
lines changed

actionpack/lib/action_dispatch/journey/router.rb

Lines changed: 56 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -27,44 +27,74 @@ def eager_load!
2727
end
2828

2929
def serve(req)
30-
find_routes(req) do |match, parameters, route|
31-
set_params = req.path_parameters
32-
path_info = req.path_info
33-
script_name = req.script_name
34-
35-
unless route.path.anchored
36-
req.script_name = (script_name.to_s + match.to_s).chomp("/")
37-
req.path_info = match.post_match
38-
req.path_info = "/" + req.path_info unless req.path_info.start_with? "/"
39-
end
40-
30+
recognize(req) do |route, parameters|
4131
req.path_parameters = parameters
4232
req.route = route
4333

4434
_, headers, _ = response = route.app.serve(req)
4535

46-
if headers[Constants::X_CASCADE] == "pass"
47-
req.script_name = script_name
48-
req.path_info = path_info
49-
req.path_parameters = set_params
50-
next
51-
end
52-
53-
return response
36+
return response unless headers[Constants::X_CASCADE] == "pass"
5437
end
5538

5639
[404, { Constants::X_CASCADE => "pass" }, ["Not Found"]]
5740
end
5841

59-
def recognize(rails_req)
60-
find_routes(rails_req) do |match, parameters, route|
61-
unless route.path.anchored
62-
rails_req.script_name = match.to_s
63-
rails_req.path_info = match.post_match
64-
rails_req.path_info = "/" + rails_req.path_info unless rails_req.path_info.start_with? "/"
42+
def recognize(req, &block)
43+
req_params = req.path_parameters
44+
path_info = req.path_info
45+
script_name = req.script_name
46+
47+
routes = filter_routes(path_info)
48+
49+
custom_routes.each { |r|
50+
routes << r if r.path.match?(path_info)
51+
}
52+
53+
if req.head?
54+
routes = match_head_routes(routes, req)
55+
else
56+
routes.select! { |r| r.matches?(req) }
57+
end
58+
59+
if routes.size > 1
60+
routes.sort! do |a, b|
61+
a.precedence <=> b.precedence
62+
end
63+
end
64+
65+
routes.each do |r|
66+
match_data = r.path.match(path_info)
67+
68+
path_parameters = req_params.merge r.defaults
69+
70+
index = 1
71+
match_data.names.each do |name|
72+
if val = match_data[index]
73+
val = if val.include?("%")
74+
CGI.unescapeURIComponent(val)
75+
else
76+
val
77+
end
78+
val.force_encoding(::Encoding::UTF_8)
79+
path_parameters[name.to_sym] = val
80+
end
81+
index += 1
82+
end
83+
84+
if r.path.anchored
85+
yield(r, path_parameters)
86+
else
87+
req.script_name = (script_name.to_s + match_data.to_s).chomp("/")
88+
req.path_info = match_data.post_match
89+
req.path_info = "/" + req.path_info unless req.path_info.start_with? "/"
90+
91+
yield(r, path_parameters)
92+
93+
req.script_name = script_name
94+
req.path_info = path_info
6595
end
6696

67-
yield(route, parameters)
97+
req.path_parameters = req_params
6898
end
6999
end
70100

@@ -99,48 +129,6 @@ def filter_routes(path)
99129
simulator.memos(path) { [] }
100130
end
101131

102-
def find_routes(req)
103-
path_info = req.path_info
104-
routes = filter_routes(path_info)
105-
106-
custom_routes.each { |r|
107-
routes << r if r.path.match?(path_info)
108-
}
109-
110-
if req.head?
111-
routes = match_head_routes(routes, req)
112-
else
113-
routes.select! { |r| r.matches?(req) }
114-
end
115-
116-
if routes.size > 1
117-
routes.sort! do |a, b|
118-
a.precedence <=> b.precedence
119-
end
120-
end
121-
122-
routes.each do |r|
123-
match_data = r.path.match(path_info)
124-
125-
path_parameters = req.path_parameters.merge r.defaults
126-
127-
index = 1
128-
match_data.names.each do |name|
129-
if val = match_data[index]
130-
val = if val.include?("%")
131-
CGI.unescapeURIComponent(val)
132-
else
133-
val
134-
end
135-
val.force_encoding(::Encoding::UTF_8)
136-
path_parameters[name.to_sym] = val
137-
end
138-
index += 1
139-
end
140-
yield [match_data, path_parameters, r]
141-
end
142-
end
143-
144132
def match_head_routes(routes, req)
145133
head_routes = routes.select { |r| r.requires_matching_verb? && r.matches?(req) }
146134
return head_routes unless head_routes.empty?

actionpack/lib/action_dispatch/routing/route_set.rb

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -932,7 +932,6 @@ def recognize_path_with_request(req, path, extras, raise_on_missing: true)
932932
params[key] = URI::RFC2396_PARSER.unescape(value)
933933
end
934934
end
935-
original_path_parameters = req.path_parameters
936935
req.path_parameters = params
937936
app = route.app
938937
if app.matches?(req) && app.dispatcher?
@@ -946,8 +945,6 @@ def recognize_path_with_request(req, path, extras, raise_on_missing: true)
946945
elsif app.matches?(req) && app.engine?
947946
path_parameters = app.rack_app.routes.recognize_path_with_request(req, path, extras, raise_on_missing: false)
948947
return path_parameters if path_parameters
949-
else
950-
req.path_parameters = original_path_parameters
951948
end
952949
end
953950

actionpack/test/dispatch/routing_assertions_test.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ def self.name
7171
mount root_engine => "/"
7272

7373
get "/shelf/foo", controller: "query_articles", action: "index"
74+
75+
get "*path", to: "symbols#index", constraints: ->(request) do
76+
request.fullpath == "/request_mutated"
77+
end
7478
end
7579
end
7680

@@ -181,6 +185,10 @@ def test_assert_recognizes_continue_to_recognize_after_it_tried_engines
181185
assert_recognizes({ controller: "query_articles", action: "index" }, "/shelf/foo")
182186
end
183187

188+
def test_assert_recognizes_doesnt_mutate_request
189+
assert_recognizes({ controller: "symbols", action: "index", path: "request_mutated" }, "/request_mutated")
190+
end
191+
184192
def test_assert_routing
185193
assert_routing("/articles", controller: "articles", action: "index")
186194
end

actionpack/test/journey/router_test.rb

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,16 @@ def test_recognize_with_unbound_regexp
147147

148148
env = rails_env "PATH_INFO" => "/foo/bar"
149149

150-
router.recognize(env) { |*_| }
150+
recognized = false
151+
152+
router.recognize(env) do |*_|
153+
assert_equal "/foo", env.env["SCRIPT_NAME"]
154+
assert_equal "/bar", env.env["PATH_INFO"]
155+
156+
recognized = true
157+
end
151158

152-
assert_equal "/foo", env.env["SCRIPT_NAME"]
153-
assert_equal "/bar", env.env["PATH_INFO"]
159+
assert recognized
154160
end
155161

156162
def test_bound_regexp_keeps_path_info

0 commit comments

Comments
 (0)