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

LocationHtml5Url search() returns undefined if server rewrites base URL #11223

Closed
akerfoot opened this issue Mar 3, 2015 · 2 comments
Closed

Comments

@akerfoot
Copy link

akerfoot commented Mar 3, 2015

Hi,

I encountered a problem where if the server rewrites part of the baseHref, the LocationHtml5Url $location.search() will return undefined, instead of an empty object.

For example, if I request: http://www.example.com/Username

and the server rewrites it as http://www.example.com/username

when the LocationProvider.$get calls $$parseLinkUrl, it doesn't call $$parse like it normally would, which would call parseAppUrl and set locationObj.$$search.

Since $$parseLinkUrl is comparing the rewritten appBase (http://www.example.com/username) against the requested initialUrl (http://www.example.com/Username), the beginsWith check fails and it doesn't follow the same code path as if the url hadn't been rewritten.

I can't say I'm familiar enough with the code to suggest a fix, but it seems like parseAppUrl sets up important state for the $location service, and not calling it during instantiation causes problems.

EDIT: forgot to mention, I encountered this running angular-1.3.6.js.

@akerfoot
Copy link
Author

Ok, I dug a little deeper, and I think I can answer my own question.

According to the RFC3986 standard, every part of a URL except the scheme and host are case-sensitive, including the part of the path that makes up the HTML5 Base URL.

from http://tools.ietf.org/html/rfc3986#section-6.2.2.1:

6.2.2.1.  Case Normalization

   <snip>

   When a URI uses components of the generic syntax, the component
   syntax equivalence rules always apply; namely, that the scheme and
   host are case-insensitive and therefore should be normalized to
   lowercase.  For example, the URI <HTTP://www.EXAMPLE.com/> is
   equivalent to <http://www.example.com/>.  The other generic syntax
   components are assumed to be case-sensitive unless specifically
   defined otherwise by the scheme (see Section 6.2.3).

Given this, if a non-normalized version of the base URL is requested, the best option is to have the server return a 301 redirect to the normalized version. That way you can ensure your href always matches the url requested, and the response will be cacheable.

If that's not an option, I think the best solution is to replace this line in $LocationProvider's $get method:

 $location.$$parseLinkUrl(initialUrl, initialUrl);

with this:

var url = initialUrl;
if (beginsWith(appBase.toLowerCase(), url.toLowerCase()) !== undefined) {
    url = appBase + url.slice(appBase.length);
}
$location.$$parseLinkUrl(url, url);

This handles the case where the base URL and the base part of the requested URL only vary by case, which seems like a common case.

If this is the canonical URL:

http://www.example.com/UserName/additional/path/data/?queryString=something#fragment/data

with a

<base href="/UserName/">

and the user requests

http://www.example.com/username/additional/path/data/?queryString=something#fragment/data

this this will replace the base part of the url with the normalized version, and pass that to $$parseLinkUrl, which will correctly identify that the url beginsWith appBase, and will set rewrittenUrl to the remaining portion additional/path/data/?queryString=something#fragment/data. Then this.$$parse and parseAppUrl will separate that into the proper $location.$$path, $location.$$search, and $location.$$hash that the rest of the framework expects to exist.

If this code is in the $LocationProvider's $get (rather than within $$parseLinkUrl) we know the url is the app's url (rather than an arbitrary href link in the body of the page).

It doesn't overwrite initialUrl directly, because of the check later on in the $get function that rewrites the browser URL

// rewrite hashbang url <> html5 url
if ($location.absUrl() != initialUrl) {
  $browser.url($location.absUrl(), true);
}

If initialUrl is changed before this, then the absUrl and initialUrl will already match, and it won't update the browser's URL bar to match.

Supporting anything more than a case-insensitive base URL seems prone to error, as there would be no way to determine where a non-normalized base URL ends and appURL begins.

@gkalpak
Copy link
Member

gkalpak commented Jan 3, 2017

Since the relevant parts of the URL are case-sensitive, I don't think it makes sense to threat them as case-insensitive. If the spec says something is case-sensitive, then different casing designates a different thing and servers should treat such changes accordingly. I wouldn't be surprised if treating the baseHref case-insensitively would even have security implications.

Closing as won't fix.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants