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