Skip to content
This repository has been archived by the owner on Sep 13, 2017. It is now read-only.

Commit

Permalink
Merge remote-tracking branch 'rails/master'
Browse files Browse the repository at this point in the history
* rails/master:
  Symbol captures may generate multiple path segments, so don't escape /. Test splat escaping. Segregate URI path and fragment escaping utils.
  Generate escaped path parameters; unescape recognized path parameters.
  Adding lib/journey.rb to use require 'journey'

Conflicts:
	lib/journey.rb
	lib/journey/router.rb
  • Loading branch information
tenderlove committed Oct 19, 2011
2 parents 1347a42 + 24bf0bc commit 6c92401
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 18 deletions.
5 changes: 5 additions & 0 deletions lib/journey/path/pattern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ def ast
node.regexp = re if re
end

@spec.grep(Nodes::Star).each do |node|
node = node.left
node.regexp = @requirements[node.to_sym] || /(.+)/
end

@spec
end

Expand Down
3 changes: 2 additions & 1 deletion lib/journey/router.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ def find_routes env
}.reject { |r| addr && !(r.ip === addr) }.map { |r|
match_data = r.path.match(env['PATH_INFO'])
match_names = match_data.names.map { |n| n.to_sym }
info = Hash[match_names.zip(match_data.captures).find_all { |_,y| y }]
match_values = match_data.captures.map { |v| v && Utils.unescape_uri(v) }
info = Hash[match_names.zip(match_values).find_all { |_,y| y }]

[match_data, r.defaults.merge(info), r]
}
Expand Down
36 changes: 28 additions & 8 deletions lib/journey/router/utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,38 @@ def self.normalize_path(path)
path
end

RESERVED_PCHAR = ':@&=+$,;%'
SAFE_PCHAR = "#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}"
if RUBY_VERSION >= '1.9'
UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false).freeze
else
UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false, 'N').freeze
# URI path and fragment escaping
# http://tools.ietf.org/html/rfc3986
module UriEscape
# Symbol captures can generate multiple path segments, so include /.
reserved_segment = '/'
reserved_fragment = '/?'
reserved_pchar = ':@&=+$,;%'

safe_pchar = "#{URI::REGEXP::PATTERN::UNRESERVED}#{reserved_pchar}"
safe_segment = "#{safe_pchar}#{reserved_segment}"
safe_fragment = "#{safe_pchar}#{reserved_fragment}"
if RUBY_VERSION >= '1.9'
UNSAFE_SEGMENT = Regexp.new("[^#{safe_segment}]", false).freeze
UNSAFE_FRAGMENT = Regexp.new("[^#{safe_fragment}]", false).freeze
else
UNSAFE_SEGMENT = Regexp.new("[^#{safe_segment}]", false, 'N').freeze
UNSAFE_FRAGMENT = Regexp.new("[^#{safe_fragment}]", false, 'N').freeze
end
end

Parser = URI.const_defined?(:Parser) ? URI::Parser.new : URI

def self.escape_uri(uri)
Parser.escape(uri.to_s, UNSAFE_PCHAR)
def self.escape_path(path)
Parser.escape(path.to_s, UriEscape::UNSAFE_SEGMENT)
end

def self.escape_fragment(fragment)
Parser.escape(fragment.to_s, UriEscape::UNSAFE_FRAGMENT)
end

def self.unescape_uri(uri)
Parser.unescape(uri)
end
end
end
Expand Down
4 changes: 3 additions & 1 deletion lib/journey/visitors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,9 @@ def visit_SYMBOL node
key = node.to_sym

if options.key? key
consumed[key] = options[key]
value = options[key]
consumed[key] = value
Router::Utils.escape_path(value)
else
"\0"
end
Expand Down
12 changes: 10 additions & 2 deletions test/router/test_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,16 @@
module Journey
class Router
class TestUtils < MiniTest::Unit::TestCase
def test_cgi_escape
assert_equal "a%2Fb", Utils.escape_uri("a/b")
def test_path_escape
assert_equal "a/b%20c+d", Utils.escape_path("a/b c+d")
end

def test_fragment_escape
assert_equal "a/b%20c+d?e", Utils.escape_fragment("a/b c+d?e")
end

def test_uri_unescape
assert_equal "a/b c+d", Utils.unescape_uri("a%2Fb%20c+d")
end
end
end
Expand Down
33 changes: 27 additions & 6 deletions test/test_router.rb
Original file line number Diff line number Diff line change
Expand Up @@ -254,16 +254,15 @@ def test_generate_id
assert_equal({:id => 1}, params)
end

# FIXME I *guess* this isn't required??
def test_generate_escapes
path = Path::Pattern.new '/:controller(/:action)'
@router.routes.add_route nil, path, {}, {}, {}

path, _ = @formatter.generate(:path_info,
nil, { :controller => "tasks",
:action => "show me",
:action => "a/b c+d",
}, {})
assert_equal '/tasks/show me', path
assert_equal '/tasks/a/b%20c+d', path
end

def test_generate_extra_params
Expand Down Expand Up @@ -305,9 +304,9 @@ def test_generate_with_name
end

{
'/content' => { :controller => 'content' },
'/content/list' => { :controller => 'content', :action => 'list' },
'/content/show/10' => { :controller => 'content', :action => 'show', :id => "10" },
'/content' => { :controller => 'content' },
'/content/list' => { :controller => 'content', :action => 'list' },
'/content/show/10' => { :controller => 'content', :action => 'show', :id => "10" },
}.each do |request_path, expected|
define_method("test_recognize_#{expected.keys.map(&:to_s).join('_')}") do
path = Path::Pattern.new "/:controller(/:action(/:id))"
Expand All @@ -327,6 +326,28 @@ def test_generate_with_name
end
end

{
:segment => ['/a%2Fb%20c+d/splat', { :segment => 'a/b c+d', :splat => 'splat' }],
:splat => ['/segment/a/b%20c+d', { :segment => 'segment', :splat => 'a/b c+d' }]
}.each do |name, (request_path, expected)|
define_method("test_recognize_#{name}") do
path = Path::Pattern.new '/:segment/*splat'
app = Object.new
route = @router.routes.add_route(app, path, {}, {}, {})

env = rails_env 'PATH_INFO' => request_path
called = false

@router.recognize(env) do |r, _, params|
assert_equal route, r
assert_equal(expected, params)
called = true
end

assert called
end
end

def test_namespaced_controller
strexp = Router::Strexp.new(
"/:controller(/:action(/:id))",
Expand Down

0 comments on commit 6c92401

Please sign in to comment.