Skip to content

Commit 6aef89a

Browse files
authored
Merge pull request rails#43440 from ignacio-chiazzo/wildcard-routes
Allow multiline to be passed in routes when using wildcards.
2 parents 3272335 + 99986cc commit 6aef89a

File tree

7 files changed

+64
-16
lines changed

7 files changed

+64
-16
lines changed

actionpack/lib/action_dispatch/journey/nodes/node.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def visit_tree(formatted)
5353
if formatted != false
5454
# Add a constraint for wildcard route to make it non-greedy and
5555
# match the optional format part of the route by default.
56-
wildcard_options[node.name.to_sym] ||= /.+?/
56+
wildcard_options[node.name.to_sym] ||= /.+?/m
5757
end
5858
end
5959

@@ -166,7 +166,7 @@ def initialize(left)
166166
super(left)
167167

168168
# By default wildcard routes are non-greedy and must match something.
169-
@regexp = /.+?/
169+
@regexp = /.+?/m
170170
end
171171

172172
def star?; true; end

actionpack/lib/action_dispatch/journey/route.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def eager_load!
9191
# as requirements.
9292
def requirements
9393
@defaults.merge(path.requirements).delete_if { |_, v|
94-
/.+?/ == v
94+
/.+?/m == v
9595
}
9696
end
9797

actionpack/lib/action_dispatch/routing/mapper.rb

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ def initialize(set:, ast:, controller:, default_action:, to:, formatted:, via:,
146146
end
147147

148148
requirements, conditions = split_constraints ast.path_params, constraints
149-
verify_regexp_requirements requirements.map(&:last).grep(Regexp)
149+
verify_regexp_requirements requirements, ast.wildcard_options
150150

151151
formats = normalize_format(formatted)
152152

@@ -246,14 +246,18 @@ def normalize_format(formatted)
246246
end
247247
end
248248

249-
def verify_regexp_requirements(requirements)
250-
requirements.each do |requirement|
251-
if ANCHOR_CHARACTERS_REGEX.match?(requirement.source)
249+
def verify_regexp_requirements(requirements, wildcard_options)
250+
requirements.each do |requirement, regex|
251+
next unless regex.is_a? Regexp
252+
253+
if ANCHOR_CHARACTERS_REGEX.match?(regex.source)
252254
raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
253255
end
254256

255-
if requirement.multiline?
256-
raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{requirement.inspect}"
257+
if regex.multiline?
258+
next if wildcard_options.key?(requirement)
259+
260+
raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{regex.inspect}"
257261
end
258262
end
259263
end

actionpack/test/controller/routing_test.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,34 @@ def test_dynamic_path_allowed
624624
url_for(rs, controller: "content", action: "show_file", path: %w(pages boo))
625625
end
626626

627+
def test_escapes_newline_character_for_dynamic_path
628+
rs.draw do
629+
get "/dynamic/:dynamic_segment" => "subpath_books#show", as: :dynamic
630+
631+
ActiveSupport::Deprecation.silence do
632+
get ":controller/:action/:id"
633+
end
634+
end
635+
636+
results = rs.recognize_path("/dynamic/a%0Anewline")
637+
assert(results, "Recognition should have succeeded")
638+
assert_equal("a\nnewline", results[:dynamic_segment])
639+
end
640+
641+
def test_escapes_newline_character_for_wildcard_path
642+
rs.draw do
643+
get "/wildcard/*wildcard_segment" => "subpath_books#show", as: :wildcard
644+
645+
ActiveSupport::Deprecation.silence do
646+
get ":controller/:action/:id"
647+
end
648+
end
649+
650+
results = rs.recognize_path("/wildcard/a%0Anewline")
651+
assert(results, "Recognition should have succeeded")
652+
assert_equal("a\nnewline", results[:wildcard_segment])
653+
end
654+
627655
def test_dynamic_recall_paths_allowed
628656
rs.draw do
629657
get "*path" => "content#show_file"

actionpack/test/dispatch/mapper_test.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def test_mapping_requirements
9292
scope = Mapper::Scope.new({})
9393
ast = Journey::Parser.parse "/store/:name(*rest)"
9494
m = Mapper::Mapping.build(scope, FakeSet.new, ast, "foo", "bar", nil, [:get], nil, {}, true, options)
95-
assert_equal(/.+?/, m.requirements[:rest])
95+
assert_equal(/.+?/m, m.requirements[:rest])
9696
end
9797

9898
def test_via_scope
@@ -137,24 +137,24 @@ def test_map_wildcard
137137
mapper = Mapper.new fakeset
138138
mapper.get "/*path", to: "pages#show"
139139
assert_equal "/*path(.:format)", fakeset.asts.first.to_s
140-
assert_equal(/.+?/, fakeset.requirements.first[:path])
140+
assert_equal(/.+?/m, fakeset.requirements.first[:path])
141141
end
142142

143143
def test_map_wildcard_with_other_element
144144
fakeset = FakeSet.new
145145
mapper = Mapper.new fakeset
146146
mapper.get "/*path/foo/:bar", to: "pages#show"
147147
assert_equal "/*path/foo/:bar(.:format)", fakeset.asts.first.to_s
148-
assert_equal(/.+?/, fakeset.requirements.first[:path])
148+
assert_equal(/.+?/m, fakeset.requirements.first[:path])
149149
end
150150

151151
def test_map_wildcard_with_multiple_wildcard
152152
fakeset = FakeSet.new
153153
mapper = Mapper.new fakeset
154154
mapper.get "/*foo/*bar", to: "pages#show"
155155
assert_equal "/*foo/*bar(.:format)", fakeset.asts.first.to_s
156-
assert_equal(/.+?/, fakeset.requirements.first[:foo])
157-
assert_equal(/.+?/, fakeset.requirements.first[:bar])
156+
assert_equal(/.+?/m, fakeset.requirements.first[:foo])
157+
assert_equal(/.+?/m, fakeset.requirements.first[:bar])
158158
end
159159

160160
def test_map_wildcard_with_format_false

actionpack/test/dispatch/routing/route_set_test.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,22 @@ def call(env)
149149
assert_equal "/users/1.json", url_helpers.user_path(1, :json)
150150
end
151151

152+
test "escape new line for dynamic params" do
153+
draw do
154+
get "/dynamic/:dynamic_segment", to: SimpleApp.new("foo#index"), as: :dynamic
155+
end
156+
157+
assert_equal "/dynamic/a%0Anewline", url_helpers.dynamic_path(dynamic_segment: "a\nnewline")
158+
end
159+
160+
test "escape new line for wildcard params" do
161+
draw do
162+
get "/wildcard/*wildcard_segment", to: SimpleApp.new("foo#index"), as: :wildcard
163+
end
164+
165+
assert_equal "/wildcard/a%0Anewline", url_helpers.wildcard_path(wildcard_segment: "a\nnewline")
166+
end
167+
152168
private
153169
def draw(&block)
154170
@set.draw(&block)

actionpack/test/journey/nodes/ast_test.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def test_wildcard_options_when_formatted
6767
ast = Ast.new(tree, true)
6868

6969
wildcard_options = ast.wildcard_options
70-
assert_equal %r{.+?}, wildcard_options[:glob]
70+
assert_equal %r{.+?}m, wildcard_options[:glob]
7171
end
7272

7373
def test_wildcard_options_when_false
@@ -83,7 +83,7 @@ def test_wildcard_options_when_nil
8383
ast = Ast.new(tree, nil)
8484

8585
wildcard_options = ast.wildcard_options
86-
assert_equal %r{.+?}, wildcard_options[:glob]
86+
assert_equal %r{.+?}m, wildcard_options[:glob]
8787
end
8888
end
8989
end

0 commit comments

Comments
 (0)