Skip to content

Commit

Permalink
Merge pull request #614 from zaki/fix-routing-regex
Browse files Browse the repository at this point in the history
Fix routing regex
  • Loading branch information
rkh committed Jan 26, 2013
2 parents 3f97416 + d983c72 commit 5a346b7
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 9 deletions.
32 changes: 30 additions & 2 deletions lib/sinatra/base.rb
Expand Up @@ -1326,15 +1326,20 @@ def compile(path)
ignore = "" ignore = ""
pattern = path.to_str.gsub(/[^\?\%\\\/\:\*\w]/) do |c| pattern = path.to_str.gsub(/[^\?\%\\\/\:\*\w]/) do |c|
ignore << escaped(c).join if c.match(/[\.@]/) ignore << escaped(c).join if c.match(/[\.@]/)
encoded(c) patt = encoded(c)
patt.gsub(/%[\da-fA-F]{2}/) do |match|
match.split(//).map {|char| char =~ /[A-Z]/ ? "[#{char}#{char.tr('A-Z', 'a-z')}]" : char}.join
end
end end
pattern.gsub!(/((:\w+)|\*)/) do |match| pattern.gsub!(/((:\w+)|\*)/) do |match|
if match == "*" if match == "*"
keys << 'splat' keys << 'splat'
"(.*?)" "(.*?)"
else else
keys << $2[1..-1] keys << $2[1..-1]
"([^#{ignore}/?#]+)" ignore_pattern = safe_ignore(ignore)

ignore_pattern
end end
end end
[/\A#{pattern}\z/, keys] [/\A#{pattern}\z/, keys]
Expand Down Expand Up @@ -1362,6 +1367,29 @@ def escaped(char, enc = URI.escape(char))
[Regexp.escape(enc), URI.escape(char, /./)] [Regexp.escape(enc), URI.escape(char, /./)]
end end


def safe_ignore(ignore)
unsafe_ignore = []
ignore = ignore.gsub(/%[\da-fA-F]{2}/) do |hex|
unsafe_ignore << hex[1..2]
''
end
unsafe_patterns = unsafe_ignore.map do |unsafe|
chars = unsafe.split(//).map do |char|
if char =~ /[A-Z]/
char <<= char.tr('A-Z', 'a-z')
end
char
end

"|(?:%[^#{chars[0]}].|%[#{chars[0]}][^#{chars[1]}])"
end
if unsafe_patterns.length > 0
"((?:[^#{ignore}/?#%]#{unsafe_patterns.join()})+)"
else
"([^#{ignore}/?#]+)"
end
end

public public
# Makes the methods defined in the block and in the Modules given # Makes the methods defined in the block and in the Modules given
# in `extensions` available to the handlers and templates # in `extensions` available to the handlers and templates
Expand Down
28 changes: 21 additions & 7 deletions test/compile_test.rb
Expand Up @@ -58,7 +58,7 @@ def compiled pattern
fails "/:foo", "/" fails "/:foo", "/"
fails "/:foo", "/foo/" fails "/:foo", "/foo/"


converts "/föö", %r{\A/f%C3%B6%C3%B6\z} converts "/föö", %r{\A/f%[Cc]3%[Bb]6%[Cc]3%[Bb]6\z}
parses "/föö", "/f%C3%B6%C3%B6", {} parses "/föö", "/f%C3%B6%C3%B6", {}


converts "/:foo/:bar", %r{\A/([^/?#]+)/([^/?#]+)\z} converts "/:foo/:bar", %r{\A/([^/?#]+)/([^/?#]+)\z}
Expand Down Expand Up @@ -87,15 +87,15 @@ def compiled pattern
converts "/test$/", %r{\A/test(?:\$|%24)/\z} converts "/test$/", %r{\A/test(?:\$|%24)/\z}
parses "/test$/", "/test$/", {} parses "/test$/", "/test$/", {}


converts "/te+st/", %r{\A/te(?:\+|%2B)st/\z} converts "/te+st/", %r{\A/te(?:\+|%2[Bb])st/\z}
parses "/te+st/", "/te+st/", {} parses "/te+st/", "/te+st/", {}
fails "/te+st/", "/test/" fails "/te+st/", "/test/"
fails "/te+st/", "/teeest/" fails "/te+st/", "/teeest/"


converts "/test(bar)/", %r{\A/test(?:\(|%28)bar(?:\)|%29)/\z} converts "/test(bar)/", %r{\A/test(?:\(|%28)bar(?:\)|%29)/\z}
parses "/test(bar)/", "/test(bar)/", {} parses "/test(bar)/", "/test(bar)/", {}


converts "/path with spaces", %r{\A/path(?:%20|(?:\+|%2B))with(?:%20|(?:\+|%2B))spaces\z} converts "/path with spaces", %r{\A/path(?:%20|(?:\+|%2[Bb]))with(?:%20|(?:\+|%2[Bb]))spaces\z}
parses "/path with spaces", "/path%20with%20spaces", {} parses "/path with spaces", "/path%20with%20spaces", {}
parses "/path with spaces", "/path%2Bwith%2Bspaces", {} parses "/path with spaces", "/path%2Bwith%2Bspaces", {}
parses "/path with spaces", "/path+with+spaces", {} parses "/path with spaces", "/path+with+spaces", {}
Expand All @@ -110,22 +110,22 @@ def compiled pattern
parses "/*/foo/*/*", "/bar/foo/bling/baz/boom", "splat" => ["bar", "bling", "baz/boom"] parses "/*/foo/*/*", "/bar/foo/bling/baz/boom", "splat" => ["bar", "bling", "baz/boom"]
fails "/*/foo/*/*", "/bar/foo/baz" fails "/*/foo/*/*", "/bar/foo/baz"


converts "/test.bar", %r{\A/test(?:\.|%2E)bar\z} converts "/test.bar", %r{\A/test(?:\.|%2[Ee])bar\z}
parses "/test.bar", "/test.bar", {} parses "/test.bar", "/test.bar", {}
fails "/test.bar", "/test0bar" fails "/test.bar", "/test0bar"


converts "/:file.:ext", %r{\A/([^\.%2E/?#]+)(?:\.|%2E)([^\.%2E/?#]+)\z} converts "/:file.:ext", %r{\A/((?:[^\./?#%]|(?:%[^2].|%[2][^Ee]))+)(?:\.|%2[Ee])((?:[^\./?#%]|(?:%[^2].|%[2][^Ee]))+)\z}
parses "/:file.:ext", "/pony.jpg", "file" => "pony", "ext" => "jpg" parses "/:file.:ext", "/pony.jpg", "file" => "pony", "ext" => "jpg"
parses "/:file.:ext", "/pony%2Ejpg", "file" => "pony", "ext" => "jpg" parses "/:file.:ext", "/pony%2Ejpg", "file" => "pony", "ext" => "jpg"
fails "/:file.:ext", "/.jpg" fails "/:file.:ext", "/.jpg"


converts "/:name.?:format?", %r{\A/([^\.%2E/?#]+)(?:\.|%2E)?([^\.%2E/?#]+)?\z} converts "/:name.?:format?", %r{\A/((?:[^\./?#%]|(?:%[^2].|%[2][^Ee]))+)(?:\.|%2[Ee])?((?:[^\./?#%]|(?:%[^2].|%[2][^Ee]))+)?\z}
parses "/:name.?:format?", "/foo", "name" => "foo", "format" => nil parses "/:name.?:format?", "/foo", "name" => "foo", "format" => nil
parses "/:name.?:format?", "/foo.bar", "name" => "foo", "format" => "bar" parses "/:name.?:format?", "/foo.bar", "name" => "foo", "format" => "bar"
parses "/:name.?:format?", "/foo%2Ebar", "name" => "foo", "format" => "bar" parses "/:name.?:format?", "/foo%2Ebar", "name" => "foo", "format" => "bar"
fails "/:name.?:format?", "/.bar" fails "/:name.?:format?", "/.bar"


converts "/:user@?:host?", %r{\A/([^@%40/?#]+)(?:@|%40)?([^@%40/?#]+)?\z} converts "/:user@?:host?", %r{\A/((?:[^@/?#%]|(?:%[^4].|%[4][^0]))+)(?:@|%40)?((?:[^@/?#%]|(?:%[^4].|%[4][^0]))+)?\z}
parses "/:user@?:host?", "/foo@bar", "user" => "foo", "host" => "bar" parses "/:user@?:host?", "/foo@bar", "user" => "foo", "host" => "bar"
parses "/:user@?:host?", "/foo.foo@bar", "user" => "foo.foo", "host" => "bar" parses "/:user@?:host?", "/foo.foo@bar", "user" => "foo.foo", "host" => "bar"
parses "/:user@?:host?", "/foo@bar.bar", "user" => "foo", "host" => "bar.bar" parses "/:user@?:host?", "/foo@bar.bar", "user" => "foo", "host" => "bar.bar"
Expand All @@ -136,4 +136,18 @@ def compiled pattern
# parses "/:name(.:format)?", "/foo", "name" => "foo", "format" => nil # parses "/:name(.:format)?", "/foo", "name" => "foo", "format" => nil
# parses "/:name(.:format)?", "/foo.bar", "name" => "foo", "format" => "bar" # parses "/:name(.:format)?", "/foo.bar", "name" => "foo", "format" => "bar"
fails "/:name(.:format)?", "/foo." fails "/:name(.:format)?", "/foo."

parses "/:id/test.bar", "/3/test.bar", {"id" => "3"}
parses "/:id/test.bar", "/2/test.bar", {"id" => "2"}
parses "/:id/test.bar", "/2E/test.bar", {"id" => "2E"}
parses "/:id/test.bar", "/2e/test.bar", {"id" => "2e"}
fails "/:id/test.bar", "/%2E/test.bar"

parses "/:file.:ext", "/pony%2ejpg", "file" => "pony", "ext" => "jpg"
parses "/:file.:ext", "/pony%E6%AD%A3%2Ejpg", "file" => "pony%E6%AD%A3", "ext" => "jpg"
parses "/:file.:ext", "/pony%e6%ad%a3%2ejpg", "file" => "pony%e6%ad%a3", "ext" => "jpg"
parses "/:file.:ext", "/pony正%2Ejpg", "file" => "pony正", "ext" => "jpg"
parses "/:file.:ext", "/pony正%2ejpg", "file" => "pony正", "ext" => "jpg"
fails "/:file.:ext", "/pony正..jpg"
fails "/:file.:ext", "/pony正.%2ejpg"
end end

0 comments on commit 5a346b7

Please sign in to comment.