Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 3625803

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 #5019
1 parent ca0f59e commit 3625803

File tree

3 files changed

+48
-5
lines changed

3 files changed

+48
-5
lines changed

src/Angular.js

+1
Original file line numberDiff line numberDiff line change
@@ -1176,6 +1176,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
11761176
replace(/%3A/gi, ':').
11771177
replace(/%24/g, '$').
11781178
replace(/%2C/gi, ',').
1179+
replace(/%3B/gi, ';').
11791180
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
11801181
}
11811182

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

+42
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,48 @@ 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.addEventListener = angular.noop;
72+
win.removeEventListener = angular.noop;
73+
win.history = {
74+
replaceState: angular.noop,
75+
pushState: angular.noop
76+
};
77+
win.location = {
78+
href: 'http://localhost:9876/;jsessionid=foo',
79+
replace: function(val) {
80+
win.location.href = val;
81+
}
82+
};
83+
return win;
84+
};
85+
$browserProvider.$get = function($document, $window) {
86+
var sniffer = {history: true, hashchange: false};
87+
var logs = {log:[], warn:[], info:[], error:[]};
88+
var fakeLog = {log: function() { logs.log.push(slice.call(arguments)); },
89+
warn: function() { logs.warn.push(slice.call(arguments)); },
90+
info: function() { logs.info.push(slice.call(arguments)); },
91+
error: function() { logs.error.push(slice.call(arguments)); }};
92+
93+
/* global Browser: false */
94+
var b = new Browser($window, $document, fakeLog, sniffer);
95+
b.pollFns = [];
96+
return b;
97+
};
98+
});
99+
var self = this;
100+
inject(function($location, $browser, $rootScope) {
101+
expect(function() {
102+
$rootScope.$digest();
103+
}).not.toThrow();
104+
});
105+
});
106+
65107
describe('NewUrl', function() {
66108
beforeEach(function() {
67109
url = new LocationHtml5Url('http://www.domain.com:9877/');

0 commit comments

Comments
 (0)