Skip to content

Commit

Permalink
feat($location): parse query parameters delimited by ; or & angular#6167
Browse files Browse the repository at this point in the history


According to RFC (http://www.w3.org/TR/1999/REC-html401-19991224/appendix/notes.html#h-B.2.2)
location should match either ';' OR '&', but not both at the same time.

Closes angular#6140
  • Loading branch information
Sebastien Armand - sa250111 committed Feb 19, 2014
1 parent 1933802 commit 1073967
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 13 deletions.
5 changes: 3 additions & 2 deletions src/Angular.js
Expand Up @@ -1055,9 +1055,10 @@ function tryDecodeURIComponent(value) {
* Parses an escaped url query string into key-value pairs.
* @returns Object.<(string|boolean)>
*/
function parseKeyValue(/**string*/keyValue) {
function parseKeyValue(/**string*/keyValue, delimiter) {
delimiter = delimiter === ';' ? delimiter : '&';
var obj = {}, key_value, key;
forEach((keyValue || "").split(/[&;]/), function(keyValue){
forEach((keyValue || "").split(delimiter), function(keyValue){
if ( keyValue ) {
key_value = keyValue.split('=');
key = tryDecodeURIComponent(key_value[0]);
Expand Down
16 changes: 12 additions & 4 deletions src/ng/location.js
Expand Up @@ -39,7 +39,7 @@ function parseAppUrl(relativeUrl, locationObj, appBase) {
var match = urlResolve(relativeUrl, appBase);
locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
match.pathname.substring(1) : match.pathname);
locationObj.$$search = parseKeyValue(match.search);
locationObj.$$search = parseKeyValue(match.search, locationObj.$$queryDelimiter);
locationObj.$$hash = decodeURIComponent(match.hash);

// make sure path starts with '/';
Expand Down Expand Up @@ -90,6 +90,7 @@ function serverBase(url) {
function LocationHtml5Url(appBase, basePrefix, queryDelimiter) {
this.$$html5 = true;
basePrefix = basePrefix || '';
this.$$queryDelimiter = queryDelimiter;
var appBaseNoFile = stripFile(appBase);
parseAbsoluteUrl(appBase, this, appBase);

Expand Down Expand Up @@ -120,7 +121,7 @@ function LocationHtml5Url(appBase, basePrefix, queryDelimiter) {
* @private
*/
this.$$compose = function() {
var search = toKeyValue(this.$$search, queryDelimiter),
var search = toKeyValue(this.$$search, this.$$queryDelimiter),
hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';

this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
Expand Down Expand Up @@ -157,6 +158,7 @@ function LocationHtml5Url(appBase, basePrefix, queryDelimiter) {
*/
function LocationHashbangUrl(appBase, hashPrefix, queryDelimiter) {
var appBaseNoFile = stripFile(appBase);
this.$$queryDelimiter = queryDelimiter;

parseAbsoluteUrl(appBase, this, appBase);

Expand Down Expand Up @@ -227,7 +229,7 @@ function LocationHashbangUrl(appBase, hashPrefix, queryDelimiter) {
* @private
*/
this.$$compose = function() {
var search = toKeyValue(this.$$search, queryDelimiter),
var search = toKeyValue(this.$$search, this.$$queryDelimiter),
hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';

this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
Expand Down Expand Up @@ -287,6 +289,12 @@ LocationHashbangInHtml5Url.prototype =
*/
$$replace: false,

/**
* Allows using ";" instead of "&" to separate query string arguments
* @private
*/
$$queryDelimiter: '&',

/**
* @ngdoc method
* @name $location#absUrl
Expand Down Expand Up @@ -415,7 +423,7 @@ LocationHashbangInHtml5Url.prototype =
return this.$$search;
case 1:
if (isString(search)) {
this.$$search = parseKeyValue(search);
this.$$search = parseKeyValue(search, this.$$queryDelimiter);
} else if (isObject(search)) {
this.$$search = search;
} else {
Expand Down
26 changes: 19 additions & 7 deletions test/ng/locationSpec.js
Expand Up @@ -316,12 +316,18 @@ describe('$location', function() {


it('should decode query params delimited interchangeably by & and ;', function() {
var url = new LocationHtml5Url('http://host.com/');
url.$$parse('http://host.com/?foo=1&bar=2;baz=3');
var url = new LocationHashbangUrl('http://host.com/', '#');
url.$$parse('http://host.com/#?foo=1&bar=2;baz');
expect(url.search()).toEqual({
'foo': '1',
'bar': '2',
'baz': '3'
'bar': '2;baz'
});

url = new LocationHashbangUrl('http://host.com/', '#', ';');
url.$$parse('http://host.com/#?foo=1&bar=2;baz');
expect(url.search()).toEqual({
'foo': '1&bar',
'baz': true
});
});

Expand Down Expand Up @@ -469,11 +475,17 @@ describe('$location', function() {

it('should decode query params delimited interchangeably by & and ;', function() {
var url = new LocationHashbangUrl('http://host.com/', '#');
url.$$parse('http://host.com/#?foo=1&bar=2;baz=3');
url.$$parse('http://host.com/#?foo=1&bar=2;baz');
expect(url.search()).toEqual({
'foo': '1',
'bar': '2',
'baz': '3'
'bar': '2;baz'
});

url = new LocationHashbangUrl('http://host.com/', '#', ';');
url.$$parse('http://host.com/#?foo=1&bar=2;baz');
expect(url.search()).toEqual({
'foo': '1&bar',
'baz': true
});
});

Expand Down

0 comments on commit 1073967

Please sign in to comment.