Fix double-encoding of spaces in query parameter keys in ->uri_for
Dagfinn Ilmari Mannsåker [Mon, 11 Sep 2017 14:19:44 +0000 (15:19 +0100)]
Commit 5c779e9841d052721162a48ad96fdbda2acd1035 moved half of the key-
encoding part into the map over the values, but left the `s/ /+/g` in
place, causing spaces in query parameter keys to first be converted to
+, then to %2B.

This moves the current key encoding block out of the inner map and
removes the premature `s/ /+g/`.

lib/Catalyst.pm
t/aggregate/unit_core_uri_for.t

index 77bd7c7..b92f01f 100644 (file)
@@ -1701,23 +1701,20 @@ sub uri_for {
       # somewhat lifted from URI::_query's query_form
       $query = '?'.join('&', map {
           my $val = $params->{$_};
-          #s/([;\/?:@&=+,\$\[\]%])/$URI::Escape::escapes{$1}/go; ## Commented out because seems to lead to double encoding - JNAP
-          s/ /+/g;
-          my $key = $_;
+          my $key = encode_utf8($_);
+          # using the URI::Escape pattern here so utf8 chars survive
+          $key =~ s/([^A-Za-z0-9\-_.!~*'() ])/$URI::Escape::escapes{$1}/go;
+          $key =~ s/ /+/g;
+
           $val = '' unless defined $val;
           (map {
-              my $param = "$_";
-              $param = encode_utf8($param);
+              my $param = encode_utf8($_);
               # using the URI::Escape pattern here so utf8 chars survive
               $param =~ s/([^A-Za-z0-9\-_.!~*'() ])/$URI::Escape::escapes{$1}/go;
               $param =~ s/ /+/g;
 
-              $key = encode_utf8($key);
-              # using the URI::Escape pattern here so utf8 chars survive
-              $key =~ s/([^A-Za-z0-9\-_.!~*'() ])/$URI::Escape::escapes{$1}/go;
-              $key =~ s/ /+/g;
-
-              "${key}=$param"; } ( ref $val eq 'ARRAY' ? @$val : $val ));
+              "${key}=$param";
+          } ( ref $val eq 'ARRAY' ? @$val : $val ));
       } @keys);
     }
 
index ab256f0..887aa4a 100644 (file)
@@ -60,6 +60,12 @@ is(
 );
 
 is(
+    Catalyst::uri_for( $context, '/bar', 'with space', { 'also with' => 'space here' })->as_string,
+    'http://127.0.0.1/foo/bar/with%20space?also+with=space+here',
+    'Spaces encoded correctly'
+);
+
+is(
     Catalyst::uri_for( $context, '/bar#fragment', { param1 => 'value1' } )->as_string,
     'http://127.0.0.1/foo/bar?param1=value1#fragment',
     'URI for path with fragment and query params 1'
@@ -127,6 +133,12 @@ is(
     'Plus is not encoded, called with only class name'
 );
 
+is(
+    Catalyst::uri_for( 'TestApp', '/bar', 'with space', { 'also with' => 'space here' })->as_string,
+    '/bar/with%20space?also+with=space+here',
+    'Spaces encoded correctly, called with only class name'
+);
+
 TODO: {
     local $TODO = 'broken by 5.7008';
     is(