Skip to content

Commit 2699101

Browse files
committed
fix($location): add semicolon to whitelist of delimiters to unencode
Some servers require characters within path segments to contain semicolons, such as `/;jsessionid=foo` in order to work correctly. RFC-3986 includes semicolons as acceptable sub-delimiters inside of path and query, but $location currently encodes semicolons. This can cause an infinite digest to occur since $location is comparing the internal semicolon-encoded url with the semicolon-unencoded url returned from window.location.href, causing Angular to believe the url is changing with each digest loop. This fix adds ";" to the list of characters to unencode after encoding queries or path segments. Closes angular#5019
1 parent f684c21 commit 2699101

File tree

3 files changed

+40
-5
lines changed

3 files changed

+40
-5
lines changed

src/Angular.js

+2
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,7 @@ function toKeyValue(obj) {
11541154
function encodeUriSegment(val) {
11551155
return encodeUriQuery(val, true).
11561156
replace(/%26/gi, '&').
1157+
replace(/%3B/gi, ';').
11571158
replace(/%3D/gi, '=').
11581159
replace(/%2B/gi, '+');
11591160
}
@@ -1176,6 +1177,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
11761177
replace(/%3A/gi, ':').
11771178
replace(/%24/g, '$').
11781179
replace(/%2C/gi, ',').
1180+
replace(/%3B/gi, ';').
11791181
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
11801182
}
11811183

test/AngularSpec.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -668,16 +668,16 @@ describe('angular', function() {
668668
toEqual('asdf1234asdf');
669669

670670
//don't encode unreserved'
671-
expect(encodeUriSegment("-_.!~*'() -_.!~*'()")).
672-
toEqual("-_.!~*'()%20-_.!~*'()");
671+
expect(encodeUriSegment("-_.!~*'(); -_.!~*'();")).
672+
toEqual("-_.!~*'();%20-_.!~*'();");
673673

674674
//don't encode the rest of pchar'
675675
expect(encodeUriSegment(':@&=+$, :@&=+$,')).
676676
toEqual(':@&=+$,%20:@&=+$,');
677677

678-
//encode '/', ';' and ' ''
678+
//encode '/' and ' ''
679679
expect(encodeUriSegment('/; /;')).
680-
toEqual('%2F%3B%20%2F%3B');
680+
toEqual('%2F;%20%2F;');
681681
});
682682
});
683683

@@ -699,7 +699,7 @@ describe('angular', function() {
699699

700700
//encode '&', ';', '=', '+', and '#'
701701
expect(encodeUriQuery('&;=+# &;=+#')).
702-
toEqual('%26%3B%3D%2B%23+%26%3B%3D%2B%23');
702+
toEqual('%26;%3D%2B%23+%26;%3D%2B%23');
703703

704704
//encode ' ' as '+'
705705
expect(encodeUriQuery(' ')).

test/ng/locationSpec.js

+33
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,39 @@ describe('$location', function() {
6262
});
6363

6464

65+
it('should not infinitely digest when using a semicolon in initial path', function() {
66+
module(function($windowProvider, $locationProvider, $browserProvider) {
67+
$locationProvider.html5Mode(true);
68+
$windowProvider.$get = function() {
69+
var win = {};
70+
angular.extend(win, window);
71+
win.location = {
72+
href: 'http://localhost:9876/;jsessionid=foo'
73+
};
74+
return win;
75+
};
76+
$browserProvider.$get = function($document, $window) {
77+
var sniffer = {history: false, hashchange: true};
78+
var logs = {log:[], warn:[], info:[], error:[]};
79+
var fakeLog = {log: function() { logs.log.push(slice.call(arguments)); },
80+
warn: function() { logs.warn.push(slice.call(arguments)); },
81+
info: function() { logs.info.push(slice.call(arguments)); },
82+
error: function() { logs.error.push(slice.call(arguments)); }};
83+
84+
/* global Browser: false */
85+
var b = new Browser($window, $document, fakeLog, sniffer);
86+
b.pollFns = [];
87+
return b;
88+
};
89+
});
90+
var self = this;
91+
inject(function($location, $browser, $rootScope) {
92+
expect(function() {
93+
$rootScope.$digest();
94+
}).not.toThrow();
95+
});
96+
});
97+
6598
describe('NewUrl', function() {
6699
beforeEach(function() {
67100
url = new LocationHtml5Url('http://www.domain.com:9877/');

0 commit comments

Comments
 (0)