Created
May 21, 2017 13:51
-
-
Save kakarukeys/5381f3975674d81ba37d3bee4cd415a1 to your computer and use it in GitHub Desktop.
solution2 - URI comparison
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function parseURI(uri) { | |
/* parse <uri> into invidual components (loosely, some errors allowed) */ | |
// see https://regex101.com/r/l96l7F/1 for explanation | |
var regex = /(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/\/?)?(?:(?:([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?((?:\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?[^?#\/]*)(?:\?([^#]*))?(?:#(.*))?/, | |
groupNames = [ | |
"fullMatch", | |
"scheme", | |
"username", | |
"password", | |
"host", | |
"port", | |
"path", | |
"query", | |
"fragment" | |
], | |
result = regex.exec(uri), | |
uriObj = {}; | |
for (var i = 0; i < result.length; i++) { | |
uriObj[groupNames[i]] = result[i] || ''; | |
} | |
return uriObj; | |
} | |
function parsePath(path) { | |
/* return an array of invidual components in <path> */ | |
var resolved = [], | |
pieces = path.split('/'), | |
token; | |
for (var i = 0; i < pieces.length; i++) { | |
token = decodeURIComponent(pieces[i]); | |
if (token && token !== '.' && token !== "..") { | |
resolved.push(token); | |
} else { | |
if (token === '..') { | |
resolved.pop(); | |
} | |
if (i === pieces.length - 1) { | |
// last token, push a blank string representing a trailing slash | |
resolved.push(''); | |
} | |
} | |
} | |
return resolved; | |
} | |
function parseQuery(query) { | |
/* return an array of name, values pair from <query> ordered by name */ | |
var resolvedMap = {}, | |
resolvedArr = [], | |
pieces = query.split('&'), | |
token, name, value; | |
for (var i = 0; i < pieces.length; i++) { | |
token = pieces[i].split('='); | |
name = decodeURIComponent(token[0]); | |
value = decodeURIComponent(token[1] || ''); | |
if (!resolvedMap[name]) { | |
resolvedMap[name] = []; | |
resolvedArr.push([name, resolvedMap[name]]); | |
} | |
resolvedMap[name].push(value); | |
} | |
return resolvedArr.sort(); | |
} | |
function checkURIs(uri1, uri2) { | |
/* return whether <uri1> and <uri2> are equivalent */ | |
if (uri1 === uri2) { | |
return true; | |
} | |
var uriObj1 = parseURI(uri1), | |
uriObj2 = parseURI(uri2), | |
protocol1 = uriObj1.scheme.toUpperCase() || "HTTP", | |
protocol2 = uriObj2.scheme.toUpperCase() || "HTTP"; | |
if (protocol1 != "HTTP" || protocol2 != "HTTP") { | |
throw new RangeError("Comparison involving non-http URI is not yet supported."); | |
} | |
return protocol1 === protocol2 && | |
decodeURIComponent(uriObj1.username) === decodeURIComponent(uriObj2.username) && | |
decodeURIComponent(uriObj1.password) === decodeURIComponent(uriObj2.password) && | |
uriObj1.host.toUpperCase() === uriObj2.host.toUpperCase() && | |
(uriObj1.port || "80") === (uriObj2.port || "80") && | |
decodeURIComponent(uriObj1.fragment) === decodeURIComponent(uriObj2.fragment) && | |
JSON.stringify(parsePath(uriObj1.path)) === JSON.stringify(parsePath(uriObj2.path)) && | |
JSON.stringify(parseQuery(uriObj1.query)) === JSON.stringify(parseQuery(uriObj2.query)); | |
} | |
/* unit tests */ | |
try { | |
checkURIs( | |
"http://username:passwd123@domain.com/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"ftp://username:passwd123@domain.com/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash") | |
checkURIs( | |
"https://username:passwd123@domain.com/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash") | |
console.assert(false, "compare with non-http URI") | |
} catch (e) { | |
if (!(e instanceof RangeError)) { | |
console.assert(false, "compare with non-http URI") | |
} | |
} | |
var testData = [ | |
["http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", "exactly equal"], | |
["hTtP://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"HtTp://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", "scheme name case-insensitive"], | |
["http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"//username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", "protocol-relative URI"], | |
["http://john:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"http://John:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", "username", false], | |
["http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"http://username:Passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", "password", false], | |
["http://username:passwd123@zendesk.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"http://username:passwd123@ZendesK.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", "host name case-insensitive"], | |
["http://username:passwd123@zendesk.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"http://username:passwd123@google.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", "host name", false], | |
["http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", "default port"], | |
["http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com:8000/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", "port", false], | |
["http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#Hash", "fragment", false], | |
["http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1", "fragment", false], | |
["http://username:passwd123@domain.com:80?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com:80/?a=3&b=4&a=1#hash", "path (root)"], | |
["http://username:passwd123@domain.com:80/?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com:80/abc?a=3&b=4&a=1#hash", "path", false], | |
["http://username:passwd123@domain.com:80/abc?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com:80/abc/?a=3&b=4&a=1#hash", "path (trailing slash)", false], | |
["http://username:passwd123@domain.com:80/abc/?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com:80/abc/def.html?a=3&b=4&a=1#hash", "path", false], | |
["http://username:passwd123@domain.com:80/abc/def.html?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com:80/abc/def.html/?a=3&b=4&a=1#hash", "path (trailing slash)", false], | |
["http://username:passwd123@domain.com:80/users/../../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com:80/temp/%7Esmith/home.html?a=3&b=4&a=1#hash", " path traversal"], | |
["http://username:passwd123@domain.com:80/abc/def/.?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com:80/abc/def/?a=3&b=4&a=1#hash", " path traversal (trailing dot)"], | |
["http://username:passwd123@domain.com:80/abc/def/..?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com:80/abc/?a=3&b=4&a=1#hash", " path traversal (trailing dot dot)"], | |
["http://username:passwd123@domain.com:80//users///..//temp////.//%7Esmith////home.html?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", "path (loose matching)"], | |
["http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&c=4&a=1#hash", "query name diff", false], | |
["http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=9&a=1#hash", "query value diff", false], | |
["http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1&b=5#hash", "query extra value", false], | |
["http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?b=4&a=3&a=1#hash", "query name ordering"], | |
["http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=1&b=4&a=3#hash", "query value ordering", false], | |
["http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=&a=1#hash", | |
"http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b&a=1#hash", "query missing value"], | |
["http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=&a=1#hash", | |
"http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=null&a=1#hash", "query missing value", false], | |
["http://user%3Aname@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"http://user:name@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", "URI encoding (username) (reserved char)", false], | |
["http://user%7Ename@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"http://user~name@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", "URI encoding (username) (unreserved char)"], | |
["http://username:passwd%7E123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"http://username:passwd~123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", "URI encoding (password)"], | |
["http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com:80/users/../temp/./%7Esmith%2Fhome.html?a=3&b=4&a=1#hash", "URI encoding (path) (reserved char)", false], | |
["http://username:passwd123@domain.com:80/users/../temp/./%7Esmith%40/home.html?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com:80/users/../temp/./~smith@/home.html?a=3&b=4&a=1#hash", "URI encoding (path)"], | |
["http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4%26a=1#hash", "URI encoding (query) (reserved char)", false], | |
["http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=foo?bar~#hash", | |
"http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=foo%3Fbar%7E#hash", "URI encoding (query)"], | |
["http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#hash", | |
"http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1%23hash", "URI encoding (fragment) (reserved char)", false], | |
["http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#:hash~", | |
"http://username:passwd123@domain.com:80/users/../temp/./%7Esmith/home.html?a=3&b=4&a=1#%3Ahash%7E", "URI encoding (fragment)"], | |
["http://www.zendesk.com/product/pricing/", | |
"http://www.zendesk.com/support/", "common URL without many components", false], | |
], d; | |
for (var i = 0; i < testData.length; i++) { | |
d = testData[i]; | |
if (d[3] === false) { | |
console.assert(!checkURIs(d[0], d[1]), d[2]); | |
console.assert(!checkURIs(d[1], d[0]), d[2] + " (args reversed)"); | |
} else { | |
console.assert(checkURIs(d[0], d[1]), d[2]); | |
console.assert(checkURIs(d[1], d[0]), d[2] + " (args reversed)"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment