Skip to content

Commit 194d697

Browse files
authored
Merge pull request rails#54517 from byroot/opt-find-routes
Micro-Optimize Router#find_routes
2 parents 1142fd4 + d577019 commit 194d697

File tree

5 files changed

+36
-30
lines changed

5 files changed

+36
-30
lines changed

actionpack/lib/action_dispatch/http/parameters.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,14 @@ def parameters
6565
alias :params :parameters
6666

6767
def path_parameters=(parameters) # :nodoc:
68-
delete_header("action_dispatch.request.parameters")
68+
@env.delete("action_dispatch.request.parameters")
6969

7070
parameters = Request::Utils.set_binary_encoding(self, parameters, parameters[:controller], parameters[:action])
7171
# If any of the path parameters has an invalid encoding then raise since it's
7272
# likely to trigger errors further on.
7373
Request::Utils.check_param_encoding(parameters)
7474

75-
set_header PARAMETERS_KEY, parameters
75+
@env[PARAMETERS_KEY] = parameters
7676
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
7777
raise ActionController::BadRequest.new("Invalid path parameters: #{e.message}")
7878
end
@@ -82,7 +82,7 @@ def path_parameters=(parameters) # :nodoc:
8282
#
8383
# { action: "my_action", controller: "my_controller" }
8484
def path_parameters
85-
get_header(PARAMETERS_KEY) || set_header(PARAMETERS_KEY, {})
85+
@env[PARAMETERS_KEY] ||= {}
8686
end
8787

8888
private

actionpack/lib/action_dispatch/http/request.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ def route_uri_pattern
166166
end
167167

168168
def route=(route) # :nodoc:
169-
set_header("action_dispatch.route", route)
169+
@env["action_dispatch.route"] = route
170170
end
171171

172172
def routes # :nodoc:

actionpack/lib/action_dispatch/journey/router.rb

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
# :markup: markdown
44

5+
require "cgi"
56
require "action_dispatch/journey/router/utils"
67
require "action_dispatch/journey/routes"
78
require "action_dispatch/journey/formatter"
@@ -116,17 +117,28 @@ def find_routes(req)
116117
routes.select! { |r| r.matches?(req) }
117118
end
118119

119-
routes.sort_by!(&:precedence)
120+
if routes.size > 1
121+
routes.sort! do |a, b|
122+
a.precedence <=> b.precedence
123+
end
124+
end
120125

121-
routes.each { |r|
126+
routes.each do |r|
122127
match_data = r.path.match(path_info)
123128
path_parameters = {}
124-
match_data.names.each_with_index { |name, i|
125-
val = match_data[i + 1]
126-
path_parameters[name.to_sym] = Utils.unescape_uri(val) if val
127-
}
129+
index = 1
130+
match_data.names.each do |name|
131+
if val = match_data[index]
132+
path_parameters[name.to_sym] = if val.include?("%")
133+
CGI.unescapeURIComponent(val)
134+
else
135+
val
136+
end
137+
end
138+
index += 1
139+
end
128140
yield [match_data, path_parameters, r]
129-
}
141+
end
130142
end
131143

132144
def match_head_routes(routes, req)

actionpack/lib/action_dispatch/journey/router/utils.rb

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,14 @@ class Utils # :nodoc:
1717
# normalize_path("") # => "/"
1818
# normalize_path("/%ab") # => "/%AB"
1919
def self.normalize_path(path)
20-
path ||= ""
20+
return "/".dup unless path
21+
22+
# Fast path for the overwhelming majority of paths that don't need to be normalized
23+
if path == "/" || (path.start_with?("/") && !path.end_with?("/") && !path.match?(%r{%|//}))
24+
return path.dup
25+
end
26+
27+
# Slow path
2128
encoding = path.encoding
2229
path = +"/#{path}"
2330
path.squeeze!("/")
@@ -61,11 +68,6 @@ def escape_segment(segment)
6168
escape(segment, SEGMENT)
6269
end
6370

64-
def unescape_uri(uri)
65-
encoding = uri.encoding == US_ASCII ? UTF_8 : uri.encoding
66-
uri.gsub(ESCAPED) { |match| [match[1, 2].hex].pack("C") }.force_encoding(encoding)
67-
end
68-
6971
private
7072
def escape(component, pattern)
7173
component.gsub(pattern) { |unsafe| percent_encode(unsafe) }.force_encoding(US_ASCII)
@@ -91,14 +93,6 @@ def self.escape_segment(segment)
9193
def self.escape_fragment(fragment)
9294
ENCODER.escape_fragment(fragment.to_s)
9395
end
94-
95-
# Replaces any escaped sequences with their unescaped representations.
96-
#
97-
# uri = "/topics?title=Ruby%20on%20Rails"
98-
# unescape_uri(uri) #=> "/topics?title=Ruby on Rails"
99-
def self.unescape_uri(uri)
100-
ENCODER.unescape_uri(uri)
101-
end
10296
end
10397
end
10498
end

actionpack/test/journey/router/utils_test.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ def test_fragment_escape
1818
assert_equal "a/b%20c+d%25?e", Utils.escape_fragment("a/b c+d%?e")
1919
end
2020

21-
def test_uri_unescape
22-
assert_equal "a/b c+d", Utils.unescape_uri("a%2Fb%20c+d")
21+
def test_CGI_unescapeURIComponent
22+
assert_equal "a/b c+d", CGI.unescapeURIComponent("a%2Fb%20c+d")
2323
end
2424

25-
def test_uri_unescape_with_utf8_string
26-
assert_equal "Šašinková", Utils.unescape_uri((+"%C5%A0a%C5%A1inkov%C3%A1").force_encoding(Encoding::US_ASCII))
25+
def test_CGI_unescapeURIComponent_with_utf8_string
26+
assert_equal "Šašinková", CGI.unescapeURIComponent((+"%C5%A0a%C5%A1inkov%C3%A1").force_encoding(Encoding::US_ASCII))
2727
end
2828

2929
def test_normalize_path_not_greedy
@@ -36,7 +36,7 @@ def test_normalize_path_uppercase
3636

3737
def test_normalize_path_maintains_string_encoding
3838
path = "/foo%AAbar%AAbaz".b
39-
assert_equal Encoding::ASCII_8BIT, Utils.normalize_path(path).encoding
39+
assert_equal Encoding::BINARY, Utils.normalize_path(path).encoding
4040
end
4141

4242
def test_normalize_path_with_nil

0 commit comments

Comments
 (0)