mirror of https://github.com/lukechilds/node.git
isaacs
15 years ago
committed by
Ryan Dahl
2 changed files with 427 additions and 0 deletions
@ -0,0 +1,236 @@ |
|||
/** |
|||
* uri.js |
|||
* A URI parser, compliant with assorted RFCs, providing parsing and resolution utilities. |
|||
**/ |
|||
|
|||
exports.parse = uri_parse; |
|||
exports.format = uri_format; |
|||
exports.resolve = uri_resolve; |
|||
exports.resolveObject = uri_resolveObject; |
|||
|
|||
|
|||
/**** expressionKeys |
|||
members of a parsed URI object that you get |
|||
from evaluting the strict regular expression. |
|||
*/ |
|||
var expressionKeys = [ |
|||
"url", |
|||
"protocol", |
|||
"authorityRoot", |
|||
"authority", |
|||
"userInfo", |
|||
"user", |
|||
"password", |
|||
"domain", |
|||
"port", |
|||
"path", |
|||
"root", |
|||
"directory", |
|||
"file", |
|||
"query", |
|||
"anchor" |
|||
], |
|||
strictExpression = new RegExp( /* url */ |
|||
"^" + |
|||
"(?:" + |
|||
"([^:/?#]+):" + /* protocol */ |
|||
")?" + |
|||
"(?:" + |
|||
"(//)" + /* authorityRoot */ |
|||
"(" + /* authority */ |
|||
"(?:" + |
|||
"(" + /* userInfo */ |
|||
"([^:@/]*)" + /* user */ |
|||
":?" + |
|||
"([^@/]*)" + /* password */ |
|||
")?" + |
|||
"@" + |
|||
")?" + |
|||
"([^:/?#]*)" + /* domain */ |
|||
"(?::(\\d*))?" + /* port */ |
|||
")" + |
|||
")?" + |
|||
"(" + /* path */ |
|||
"(/?)" + /* root */ |
|||
"((?:[^?#/]*/)*)" + |
|||
"([^?#]*)" + /* file */ |
|||
")" + |
|||
"(?:\\?([^#]*))?" + /* query */ |
|||
"(?:#(.*))?" /*anchor */ |
|||
); |
|||
|
|||
/**** parse |
|||
a URI parser function that uses the `strictExpression` |
|||
and `expressionKeys` and returns an `Object` |
|||
mapping all `keys` to values. |
|||
*/ |
|||
function uri_parse (url) { |
|||
var items = {}, |
|||
parts = strictExpression.exec(url); |
|||
|
|||
for (var i = 0; i < parts.length; i++) { |
|||
items[expressionKeys[i]] = parts[i] ? parts[i] : ""; |
|||
} |
|||
|
|||
items.root = (items.root || items.authorityRoot) ? '/' : ''; |
|||
|
|||
items.directories = items.directory.split("/"); |
|||
if (items.directories[items.directories.length - 1] == "") { |
|||
items.directories.pop(); |
|||
} |
|||
|
|||
/* normalize */ |
|||
var directories = []; |
|||
for (var i = 0; i < items.directories.length; i++) { |
|||
var directory = items.directories[i]; |
|||
if (directory == '.') { |
|||
} else if (directory == '..') { |
|||
if (directories.length && directories[directories.length - 1] != '..') |
|||
directories.pop(); |
|||
else |
|||
directories.push('..'); |
|||
} else { |
|||
directories.push(directory); |
|||
} |
|||
} |
|||
items.directories = directories; |
|||
|
|||
items.domains = items.domain.split("."); |
|||
|
|||
return items; |
|||
}; |
|||
|
|||
|
|||
/**** format |
|||
accepts a parsed URI object and returns |
|||
the corresponding string. |
|||
*/ |
|||
function uri_format (object) { |
|||
if (typeof(object) == 'undefined') |
|||
throw new Error("UrlError: URL undefined for urls#format"); |
|||
if (object instanceof String || typeof(object) === 'string') |
|||
return object; |
|||
var domain = |
|||
object.domains ? |
|||
object.domains.join(".") : |
|||
object.domain; |
|||
var userInfo = ( |
|||
object.user || |
|||
object.password |
|||
) ? ( |
|||
(object.user || "") + |
|||
(object.password ? ":" + object.password : "") |
|||
) : |
|||
object.userInfo; |
|||
var authority = ( |
|||
userInfo || |
|||
domain || |
|||
object.port |
|||
) ? ( |
|||
(userInfo ? userInfo + "@" : "") + |
|||
(domain || "") + |
|||
(object.port ? ":" + object.port : "") |
|||
) : |
|||
object.authority || ""; |
|||
|
|||
var directory = |
|||
object.directories ? |
|||
object.directories.join("/") : |
|||
object.directory; |
|||
var path = |
|||
directory || object.file ? |
|||
( |
|||
(directory ? directory + "/" : "") + |
|||
(object.file || "") |
|||
) : |
|||
object.path; |
|||
var authorityRoot = |
|||
object.authorityRoot |
|||
|| authority ? "//" : ""; |
|||
|
|||
return object.url = (( |
|||
(object.protocol ? object.protocol + ":" : "") + |
|||
(authorityRoot) + |
|||
(authority) + |
|||
(object.root || (authority && path) ? "/" : "") + |
|||
(path ? path : "") + |
|||
(object.query ? "?" + object.query : "") + |
|||
(object.anchor ? "#" + object.anchor : "") |
|||
) || object.url || ""); |
|||
}; |
|||
|
|||
/**** resolveObject |
|||
returns an object representing a URL resolved from |
|||
a relative location and a source location. |
|||
*/ |
|||
function uri_resolveObject (source, relative) { |
|||
if (!source) |
|||
return relative; |
|||
|
|||
source = uri_parse(source); |
|||
relative = uri_parse(relative); |
|||
|
|||
if (relative.url == "") |
|||
return source; |
|||
|
|||
delete source.url; |
|||
delete source.authority; |
|||
delete source.domain; |
|||
delete source.userInfo; |
|||
delete source.path; |
|||
delete source.directory; |
|||
|
|||
if ( |
|||
relative.protocol && relative.protocol != source.protocol || |
|||
relative.authority && relative.authority != source.authority |
|||
) { |
|||
source = relative; |
|||
} else { |
|||
if (relative.root) { |
|||
source.directories = relative.directories; |
|||
} else { |
|||
|
|||
var directories = relative.directories; |
|||
for (var i = 0; i < directories.length; i++) { |
|||
var directory = directories[i]; |
|||
if (directory == ".") { |
|||
} else if (directory == "..") { |
|||
if (source.directories.length) { |
|||
source.directories.pop(); |
|||
} else { |
|||
source.directories.push('..'); |
|||
} |
|||
} else { |
|||
source.directories.push(directory); |
|||
} |
|||
} |
|||
|
|||
if (relative.file == ".") { |
|||
relative.file = ""; |
|||
} else if (relative.file == "..") { |
|||
source.directories.pop(); |
|||
relative.file = ""; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (relative.root) |
|||
source.root = relative.root; |
|||
if (relative.protcol) |
|||
source.protocol = relative.protocol; |
|||
if (!(!relative.path && relative.anchor)) |
|||
source.file = relative.file; |
|||
source.query = relative.query; |
|||
source.anchor = relative.anchor; |
|||
|
|||
return source; |
|||
}; |
|||
|
|||
|
|||
/**** resolve |
|||
returns a URL resovled to a relative URL from a source URL. |
|||
*/ |
|||
function uri_resolve (source, relative) { |
|||
return uri_format(uri_resolveObject(source, relative)); |
|||
}; |
|||
|
@ -0,0 +1,191 @@ |
|||
process.mixin(require("./common")); |
|||
|
|||
var uri = require("uri"), |
|||
sys = require("sys"); |
|||
|
|||
// URIs to parse, and expected data
|
|||
// { url : parsed }
|
|||
var parseTests = { |
|||
"http://www.narwhaljs.org/blog/categories?id=news" : { |
|||
"url": "http://www.narwhaljs.org/blog/categories?id=news", |
|||
"protocol": "http", |
|||
"authorityRoot": "//", |
|||
"authority": "www.narwhaljs.org", |
|||
"userInfo": "", |
|||
"user": "", |
|||
"password": "", |
|||
"domain": "www.narwhaljs.org", |
|||
"port": "", |
|||
"path": "/blog/categories", |
|||
"root": "/", |
|||
"directory": "blog/", |
|||
"file": "categories", |
|||
"query": "id=news", |
|||
"anchor": "", |
|||
"directories": [ |
|||
"blog" |
|||
], |
|||
"domains": [ |
|||
"www", |
|||
"narwhaljs", |
|||
"org" |
|||
] |
|||
}, |
|||
"http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=" : { |
|||
"url": "http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=", |
|||
"protocol": "http", |
|||
"authorityRoot": "//", |
|||
"authority": "mt0.google.com", |
|||
"userInfo": "", |
|||
"user": "", |
|||
"password": "", |
|||
"domain": "mt0.google.com", |
|||
"port": "", |
|||
"path": "/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=", |
|||
"root": "/", |
|||
"directory": "vt/", |
|||
"file": "lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=", |
|||
"query": "", |
|||
"anchor": "", |
|||
"directories": [ |
|||
"vt" |
|||
], |
|||
"domains": [ |
|||
"mt0", |
|||
"google", |
|||
"com" |
|||
] |
|||
}, |
|||
"http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=" : { |
|||
"url": "http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=", |
|||
"protocol": "http", |
|||
"authorityRoot": "//", |
|||
"authority": "mt0.google.com", |
|||
"userInfo": "", |
|||
"user": "", |
|||
"password": "", |
|||
"domain": "mt0.google.com", |
|||
"port": "", |
|||
"path": "/vt/lyrs=m@114", |
|||
"root": "/", |
|||
"directory": "vt/", |
|||
"file": "lyrs=m@114", |
|||
"query": "??&hl=en&src=api&x=2&y=2&z=3&s=", |
|||
"anchor": "", |
|||
"directories": [ |
|||
"vt" |
|||
], |
|||
"domains": [ |
|||
"mt0", |
|||
"google", |
|||
"com" |
|||
] |
|||
}, |
|||
"http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=" : { |
|||
"url": "http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=", |
|||
"protocol": "http", |
|||
"authorityRoot": "//", |
|||
"authority": "user:pass@mt0.google.com", |
|||
"userInfo": "user:pass", |
|||
"user": "user", |
|||
"password": "pass", |
|||
"domain": "mt0.google.com", |
|||
"port": "", |
|||
"path": "/vt/lyrs=m@114", |
|||
"root": "/", |
|||
"directory": "vt/", |
|||
"file": "lyrs=m@114", |
|||
"query": "??&hl=en&src=api&x=2&y=2&z=3&s=", |
|||
"anchor": "", |
|||
"directories": [ |
|||
"vt" |
|||
], |
|||
"domains": [ |
|||
"mt0", |
|||
"google", |
|||
"com" |
|||
] |
|||
}, |
|||
"file:///etc/passwd" : { |
|||
"url": "file:///etc/passwd", |
|||
"protocol": "file", |
|||
"authorityRoot": "//", |
|||
"authority": "", |
|||
"userInfo": "", |
|||
"user": "", |
|||
"password": "", |
|||
"domain": "", |
|||
"port": "", |
|||
"path": "/etc/passwd", |
|||
"root": "/", |
|||
"directory": "etc/", |
|||
"file": "passwd", |
|||
"query": "", |
|||
"anchor": "", |
|||
"directories": [ |
|||
"etc" |
|||
], |
|||
"domains": [ |
|||
"" |
|||
] |
|||
}, |
|||
"file:///etc/node/" : { |
|||
"url": "file:///etc/node/", |
|||
"protocol": "file", |
|||
"authorityRoot": "//", |
|||
"authority": "", |
|||
"userInfo": "", |
|||
"user": "", |
|||
"password": "", |
|||
"domain": "", |
|||
"port": "", |
|||
"path": "/etc/node/", |
|||
"root": "/", |
|||
"directory": "etc/node/", |
|||
"file": "", |
|||
"query": "", |
|||
"anchor": "", |
|||
"directories": [ |
|||
"etc", |
|||
"node" |
|||
], |
|||
"domains": [ |
|||
"" |
|||
] |
|||
} |
|||
}; |
|||
for (var url in parseTests) { |
|||
var actual = uri.parse(url), |
|||
expected = parseTests[url]; |
|||
for (var i in expected) { |
|||
// sys.debug(i);
|
|||
// sys.debug("expect: " + JSON.stringify(expected[i]));
|
|||
// sys.debug("actual: " + JSON.stringify(actual[i]));
|
|||
var e = JSON.stringify(expected[i]), |
|||
a = JSON.stringify(actual[i]); |
|||
assert.equal(e, a, "parse(" + url + ")."+i+" == "+e+"\nactual: "+a); |
|||
} |
|||
|
|||
var expected = url, |
|||
actual = uri.format(parseTests[url]); |
|||
assert.equal(expected, actual, "format("+url+") == "+url+"\nactual:"+actual); |
|||
} |
|||
|
|||
[ |
|||
// [from, path, expected]
|
|||
["/foo/bar/baz", "quux", "/foo/bar/quux"], |
|||
["/foo/bar/baz", "quux/asdf", "/foo/bar/quux/asdf"], |
|||
["/foo/bar/baz", "quux/baz", "/foo/bar/quux/baz"], //TODO: failing test
|
|||
["/foo/bar/baz", "../quux/baz", "/foo/quux/baz"], //TODO: failing test
|
|||
["/foo/bar/baz", "/bar", "/bar"], |
|||
["/foo/bar/baz/", "quux", "/foo/bar/baz/quux"], |
|||
["/foo/bar/baz/", "quux/baz", "/foo/bar/baz/quux/baz"], |
|||
["/foo/bar/baz", "../../../../../../../../quux/baz", "/quux/baz"] |
|||
].forEach(function (relativeTest) { |
|||
var a = uri.resolve(relativeTest[0], relativeTest[1]), |
|||
e = relativeTest[2]; |
|||
assert.equal(e, a, |
|||
"resolve("+[relativeTest[0], relativeTest[1]]+") == "+e+ |
|||
"\n actual="+a); |
|||
}); |
|||
|
Loading…
Reference in new issue