From: isaacs Date: Fri, 11 Dec 2009 23:25:35 +0000 (-0800) Subject: Pull in the uri.js from Narwhal and create tests, stripping out the cruft from a... X-Git-Tag: v0.1.22~11 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=2f9722cca0a72122aa03763c085f6b5aa7f0ada2;p=platform%2Fupstream%2Fnodejs.git Pull in the uri.js from Narwhal and create tests, stripping out the cruft from a previous code-surgery. --- diff --git a/lib/uri.js b/lib/uri.js new file mode 100644 index 0000000..0a530cb --- /dev/null +++ b/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)); +}; + diff --git a/test/mjsunit/test-uri.js b/test/mjsunit/test-uri.js new file mode 100644 index 0000000..13ac656 --- /dev/null +++ b/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); +}); +