Changeset 6730
- Timestamp:
- 05/14/07 11:14:30 (1 year ago)
- Files:
-
- trunk/actionpack/CHANGELOG (modified) (1 diff)
- trunk/actionpack/lib/action_controller/routing.rb (modified) (8 diffs)
- trunk/actionpack/test/controller/routing_test.rb (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/actionpack/CHANGELOG
r6729 r6730 1 1 *SVN* 2 3 * Rationalize route path escaping according to RFC 2396 section 3.3. #7544, #8307. [Jeremy Kemper, chrisroos, begemot, jugend] 2 4 3 5 * Added record identification with polymorphic routes for ActionController::Base#url_for and ActionView::Base#url_for [DHH]. Examples: trunk/actionpack/lib/action_controller/routing.rb
r6729 r6730 249 249 # 250 250 module Routing 251 # TODO: , (comma) should be an allowed path character. 251 252 SEPARATORS = %w( / ; . , ? ) 252 253 … … 548 549 549 550 class Segment #:nodoc: 551 # TODO: , (comma) should be an allowed path character. 552 RESERVED_PCHAR = ':@&=+$' 553 UNSAFE_PCHAR = Regexp.new("[^#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}]", false, 'N').freeze 554 550 555 attr_accessor :is_optional 551 556 alias_method :optional?, :is_optional … … 568 573 end 569 574 end 570 575 576 def interpolation_chunk 577 URI.escape(value, UNSAFE_PCHAR) 578 end 579 571 580 # Return a string interpolation statement for this segment and those before it. 572 581 def interpolation_statement(prior_segments) … … 612 621 613 622 def interpolation_chunk 614 raw? ? value : URI.escape(value)623 raw? ? value : super 615 624 end 616 625 617 626 def regexp_chunk 618 chunk = Regexp.escape value627 chunk = Regexp.escape(value) 619 628 optional? ? Regexp.optionalize(chunk) : chunk 620 629 end … … 693 702 694 703 def interpolation_chunk 695 "\#{ CGI.escape(#{local_name}.to_s)}"704 "\#{URI.escape(#{local_name}.to_s, ActionController::Routing::Segment::UNSAFE_PCHAR)}" 696 705 end 697 706 … … 724 733 end 725 734 def match_extraction(next_capture) 726 # All non code-related keys (such as :id, :slug) have to be unescaped as other CGI params 735 # All non code-related keys (such as :id, :slug) are URI-unescaped as 736 # path parameters. 727 737 default_value = default ? default.inspect : nil 728 738 %[ 729 739 value = if (m = match[#{next_capture}]) 730 m = m.gsub('+', '%2B') 731 CGI.unescape(m) 740 URI.unescape(m) 732 741 else 733 742 #{default_value} … … 749 758 end 750 759 751 # Don't URI.escape the controller name, since it may have slashes in it, 752 # like admin/foo. 760 # Don't URI.escape the controller name since it may contain slashes. 753 761 def interpolation_chunk 754 762 "\#{#{local_name}.to_s}" … … 771 779 772 780 class PathSegment < DynamicSegment #:nodoc: 773 EscapedSlash = URI.escape("/") 781 RESERVED_PCHAR = "#{Segment::RESERVED_PCHAR}/" 782 UNSAFE_PCHAR = Regexp.new("[^#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}]", false, 'N').freeze 783 774 784 def interpolation_chunk 775 "\#{URI.escape(#{local_name}.to_s ).gsub(#{EscapedSlash.inspect}, '/')}"785 "\#{URI.escape(#{local_name}.to_s, ActionController::Routing::PathSegment::UNSAFE_PCHAR)}" 776 786 end 777 787 trunk/actionpack/test/controller/routing_test.rb
r6724 r6730 14 14 end 15 15 16 # See RFC 3986, section 3.3 for allowed path characters. 16 17 class UriReservedCharactersRoutingTest < Test::Unit::TestCase 17 # See RFC 3986, section 2.2 Reserved Characters18 19 18 def setup 20 19 ActionController::Routing.use_controllers! ['controller'] 21 20 @set = ActionController::Routing::RouteSet.new 22 21 @set.draw do |map| 23 map.connect ':controller/:action/:var' 24 end 25 end 26 27 def test_should_escape_reserved_uri_characters_within_individual_path_components 28 assert_equal '/controller/action/p1%3Ap2', @set.generate(:controller => 'controller', :action => 'action', :var => 'p1:p2') 29 assert_equal '/controller/action/p1%2Fp2', @set.generate(:controller => 'controller', :action => 'action', :var => 'p1/p2') 30 assert_equal '/controller/action/p1%3Fp2', @set.generate(:controller => 'controller', :action => 'action', :var => 'p1?p2') 31 assert_equal '/controller/action/p1%23p2', @set.generate(:controller => 'controller', :action => 'action', :var => 'p1#p2') 32 assert_equal '/controller/action/p1%5Bp2', @set.generate(:controller => 'controller', :action => 'action', :var => 'p1[p2') 33 assert_equal '/controller/action/p1%5Dp2', @set.generate(:controller => 'controller', :action => 'action', :var => 'p1]p2') 34 assert_equal '/controller/action/p1%40p2', @set.generate(:controller => 'controller', :action => 'action', :var => 'p1@p2') 35 end 36 37 def test_should_recognize_escaped_path_component_and_unescape 38 expected_options = {:var => "p1:p2", :controller => "controller", :action => "action"} 39 assert_equal expected_options, @set.recognize_path('/controller/action/p1%3Ap2') 40 expected_options = {:var => "p1/p2", :controller => "controller", :action => "action"} 41 assert_equal expected_options, @set.recognize_path('/controller/action/p1%2Fp2') 42 expected_options = {:var => "p1?p2", :controller => "controller", :action => "action"} 43 assert_equal expected_options, @set.recognize_path('/controller/action/p1%3Fp2') 44 expected_options = {:var => "p1#p2", :controller => "controller", :action => "action"} 45 assert_equal expected_options, @set.recognize_path('/controller/action/p1%23p2') 46 expected_options = {:var => "p1[p2", :controller => "controller", :action => "action"} 47 assert_equal expected_options, @set.recognize_path('/controller/action/p1%5Bp2') 48 expected_options = {:var => "p1]p2", :controller => "controller", :action => "action"} 49 assert_equal expected_options, @set.recognize_path('/controller/action/p1%5Dp2') 50 expected_options = {:var => "p1@p2", :controller => "controller", :action => "action"} 51 assert_equal expected_options, @set.recognize_path('/controller/action/p1%40p2') 52 end 53 22 map.connect ':controller/:action/:variable' 23 end 24 25 # TODO: perhaps , (comma) shouldn't be a route separator. 26 safe, unsafe = %w(: @ & = + $), %w(, ^ / ? # [ ] ;) 27 hex = unsafe.map { |char| '%' + char.unpack('H2').first.upcase } 28 29 @segment = "#{safe}#{unsafe}".freeze 30 @escaped = "#{safe}#{hex}".freeze 31 end 32 33 def test_route_generation_escapes_unsafe_path_characters 34 assert_equal "/contr#{@segment}oller/act#{@escaped}ion/var#{@escaped}iable", 35 @set.generate(:controller => "contr#{@segment}oller", 36 :action => "act#{@segment}ion", 37 :variable => "var#{@segment}iable") 38 end 39 40 def test_route_recognition_unescapes_path_components 41 options = { :controller => "controller", 42 :action => "act#{@segment}ion", 43 :variable => "var#{@segment}iable" } 44 assert_equal options, @set.recognize_path("/controller/act#{@escaped}ion/var#{@escaped}iable") 45 end 54 46 end 55 47 … … 925 917 end 926 918 927 def test_default_route_should_escape_pluses_in_id 928 expected = {:controller => 'accounts', :action => 'show', :id => 'hello world'} 919 def test_default_route_should_uri_escape_pluses 920 expected = { :controller => 'accounts', :action => 'show', :id => 'hello world' } 921 assert_equal expected, default_route.recognize('/accounts/show/hello world') 922 assert_equal expected, default_route.recognize('/accounts/show/hello%20world') 923 assert_equal '/accounts/show/hello%20world', default_route.generate(expected, expected, {}) 924 925 expected[:id] = 'hello+world' 929 926 assert_equal expected, default_route.recognize('/accounts/show/hello+world') 927 assert_equal expected, default_route.recognize('/accounts/show/hello%2Bworld') 928 assert_equal '/accounts/show/hello+world', default_route.generate(expected, expected, {}) 930 929 end 931 930