Browse Source

Pull in the uri.js from Narwhal and create tests, stripping out the cruft from a previous code-surgery.

v0.7.4-release
isaacs 15 years ago
committed by Ryan Dahl
parent
commit
2f9722cca0
  1. 236
      lib/uri.js
  2. 191
      test/mjsunit/test-uri.js

236
lib/uri.js

@ -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));
};

191
test/mjsunit/test-uri.js

@ -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…
Cancel
Save