diff options
author | Florian Dold <florian.dold@gmail.com> | 2017-05-03 15:35:00 +0200 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2017-05-03 15:35:00 +0200 |
commit | de98e0b232509d5f40c135d540a70e415272ff85 (patch) | |
tree | a79222a5b58484ab3b80d18efcaaa7ccc4769b33 /node_modules/url | |
parent | e0c9d480a73fa629c1e4a47d3e721f1d2d345406 (diff) | |
download | wallet-core-de98e0b232509d5f40c135d540a70e415272ff85.tar.xz |
node_modules
Diffstat (limited to 'node_modules/url')
-rw-r--r-- | node_modules/url/.npmignore | 1 | ||||
-rw-r--r-- | node_modules/url/.travis.yml | 7 | ||||
-rw-r--r-- | node_modules/url/.zuul.yml | 16 | ||||
-rw-r--r-- | node_modules/url/LICENSE | 21 | ||||
-rw-r--r-- | node_modules/url/README.md | 108 | ||||
-rw-r--r-- | node_modules/url/node_modules/punycode/LICENSE-MIT.txt | 20 | ||||
-rw-r--r-- | node_modules/url/node_modules/punycode/README.md | 176 | ||||
-rw-r--r-- | node_modules/url/node_modules/punycode/package.json | 53 | ||||
-rw-r--r-- | node_modules/url/node_modules/punycode/punycode.js | 530 | ||||
-rw-r--r-- | node_modules/url/package.json | 24 | ||||
-rw-r--r-- | node_modules/url/test.js | 1599 | ||||
-rw-r--r-- | node_modules/url/url.js | 732 | ||||
-rw-r--r-- | node_modules/url/util.js | 16 |
13 files changed, 3303 insertions, 0 deletions
diff --git a/node_modules/url/.npmignore b/node_modules/url/.npmignore new file mode 100644 index 000000000..ba114713b --- /dev/null +++ b/node_modules/url/.npmignore @@ -0,0 +1 @@ +test-url.js diff --git a/node_modules/url/.travis.yml b/node_modules/url/.travis.yml new file mode 100644 index 000000000..16ed30155 --- /dev/null +++ b/node_modules/url/.travis.yml @@ -0,0 +1,7 @@ +language: node_js +node_js: + - "0.10" +env: + global: + - secure: OgPRLCzHFh5WbjHEKlghHFW1oOreSF2JVUr3CMaFDi03ngTS2WONSw8mRn8SA6FTldiGGBx1n8orDzUw6cdkB7+tkU3G5B0M0V3vl823NaUFKgxsCM3UGDYfJb3yfAG5cj72rVZoX/ABd1fVuG4vBIlDLxsSlKQFMzUCFoyttr8= + - secure: AiZP8GHbyx83ZBhOvOxxtpNcgNHoP+vo5G1a1OYU78EHCgHg8NRyHKyCdrBnPvw6mV2BI/8frZaXAEicsHMtHMofBYn7nibNlaajBPI8AkHtYfNSc+zO+71Kwv7VOTOKKnkMEIkqhHlc6njFoH3QaBNHsgNlzzplPxaIt8vdUVk= diff --git a/node_modules/url/.zuul.yml b/node_modules/url/.zuul.yml new file mode 100644 index 000000000..feea8b60d --- /dev/null +++ b/node_modules/url/.zuul.yml @@ -0,0 +1,16 @@ +ui: mocha-tdd +browsers: + - name: chrome + version: latest + - name: firefox + version: 24..latest + - name: safari + version: latest + - name: ie + version: 9..latest + - name: iphone + version: oldest..latest + - name: ipad + version: oldest..latest + - name: android + version: oldest..latest diff --git a/node_modules/url/LICENSE b/node_modules/url/LICENSE new file mode 100644 index 000000000..f45bc1186 --- /dev/null +++ b/node_modules/url/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright Joyent, Inc. and other Node contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/url/README.md b/node_modules/url/README.md new file mode 100644 index 000000000..8b3546028 --- /dev/null +++ b/node_modules/url/README.md @@ -0,0 +1,108 @@ +# node-url + +[![Build Status](https://travis-ci.org/defunctzombie/node-url.svg?branch=master)](https://travis-ci.org/defunctzombie/node-url) + +This module has utilities for URL resolution and parsing meant to have feature parity with node.js core [url](http://nodejs.org/api/url.html) module. + +```js +var url = require('url'); +``` + +## api + +Parsed URL objects have some or all of the following fields, depending on +whether or not they exist in the URL string. Any parts that are not in the URL +string will not be in the parsed object. Examples are shown for the URL + +`'http://user:pass@host.com:8080/p/a/t/h?query=string#hash'` + +* `href`: The full URL that was originally parsed. Both the protocol and host are lowercased. + + Example: `'http://user:pass@host.com:8080/p/a/t/h?query=string#hash'` + +* `protocol`: The request protocol, lowercased. + + Example: `'http:'` + +* `host`: The full lowercased host portion of the URL, including port + information. + + Example: `'host.com:8080'` + +* `auth`: The authentication information portion of a URL. + + Example: `'user:pass'` + +* `hostname`: Just the lowercased hostname portion of the host. + + Example: `'host.com'` + +* `port`: The port number portion of the host. + + Example: `'8080'` + +* `pathname`: The path section of the URL, that comes after the host and + before the query, including the initial slash if present. + + Example: `'/p/a/t/h'` + +* `search`: The 'query string' portion of the URL, including the leading + question mark. + + Example: `'?query=string'` + +* `path`: Concatenation of `pathname` and `search`. + + Example: `'/p/a/t/h?query=string'` + +* `query`: Either the 'params' portion of the query string, or a + querystring-parsed object. + + Example: `'query=string'` or `{'query':'string'}` + +* `hash`: The 'fragment' portion of the URL including the pound-sign. + + Example: `'#hash'` + +The following methods are provided by the URL module: + +### url.parse(urlStr, [parseQueryString], [slashesDenoteHost]) + +Take a URL string, and return an object. + +Pass `true` as the second argument to also parse +the query string using the `querystring` module. +Defaults to `false`. + +Pass `true` as the third argument to treat `//foo/bar` as +`{ host: 'foo', pathname: '/bar' }` rather than +`{ pathname: '//foo/bar' }`. Defaults to `false`. + +### url.format(urlObj) + +Take a parsed URL object, and return a formatted URL string. + +* `href` will be ignored. +* `protocol` is treated the same with or without the trailing `:` (colon). + * The protocols `http`, `https`, `ftp`, `gopher`, `file` will be + postfixed with `://` (colon-slash-slash). + * All other protocols `mailto`, `xmpp`, `aim`, `sftp`, `foo`, etc will + be postfixed with `:` (colon) +* `auth` will be used if present. +* `hostname` will only be used if `host` is absent. +* `port` will only be used if `host` is absent. +* `host` will be used in place of `hostname` and `port` +* `pathname` is treated the same with or without the leading `/` (slash) +* `search` will be used in place of `query` +* `query` (object; see `querystring`) will only be used if `search` is absent. +* `search` is treated the same with or without the leading `?` (question mark) +* `hash` is treated the same with or without the leading `#` (pound sign, anchor) + +### url.resolve(from, to) + +Take a base URL, and a href URL, and resolve them as a browser would for +an anchor tag. Examples: + + url.resolve('/one/two/three', 'four') // '/one/two/four' + url.resolve('http://example.com/', '/one') // 'http://example.com/one' + url.resolve('http://example.com/one', '/two') // 'http://example.com/two' diff --git a/node_modules/url/node_modules/punycode/LICENSE-MIT.txt b/node_modules/url/node_modules/punycode/LICENSE-MIT.txt new file mode 100644 index 000000000..a41e0a7ef --- /dev/null +++ b/node_modules/url/node_modules/punycode/LICENSE-MIT.txt @@ -0,0 +1,20 @@ +Copyright Mathias Bynens <https://mathiasbynens.be/> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/url/node_modules/punycode/README.md b/node_modules/url/node_modules/punycode/README.md new file mode 100644 index 000000000..831e6379b --- /dev/null +++ b/node_modules/url/node_modules/punycode/README.md @@ -0,0 +1,176 @@ +# Punycode.js [![Build status](https://travis-ci.org/bestiejs/punycode.js.svg?branch=master)](https://travis-ci.org/bestiejs/punycode.js) [![Code coverage status](http://img.shields.io/coveralls/bestiejs/punycode.js/master.svg)](https://coveralls.io/r/bestiejs/punycode.js) [![Dependency status](https://gemnasium.com/bestiejs/punycode.js.svg)](https://gemnasium.com/bestiejs/punycode.js) + +A robust Punycode converter that fully complies to [RFC 3492](http://tools.ietf.org/html/rfc3492) and [RFC 5891](http://tools.ietf.org/html/rfc5891), and works on nearly all JavaScript platforms. + +This JavaScript library is the result of comparing, optimizing and documenting different open-source implementations of the Punycode algorithm: + +* [The C example code from RFC 3492](http://tools.ietf.org/html/rfc3492#appendix-C) +* [`punycode.c` by _Markus W. Scherer_ (IBM)](http://opensource.apple.com/source/ICU/ICU-400.42/icuSources/common/punycode.c) +* [`punycode.c` by _Ben Noordhuis_](https://github.com/bnoordhuis/punycode/blob/master/punycode.c) +* [JavaScript implementation by _some_](http://stackoverflow.com/questions/183485/can-anyone-recommend-a-good-free-javascript-for-punycode-to-unicode-conversion/301287#301287) +* [`punycode.js` by _Ben Noordhuis_](https://github.com/joyent/node/blob/426298c8c1c0d5b5224ac3658c41e7c2a3fe9377/lib/punycode.js) (note: [not fully compliant](https://github.com/joyent/node/issues/2072)) + +This project is [bundled](https://github.com/joyent/node/blob/master/lib/punycode.js) with [Node.js v0.6.2+](https://github.com/joyent/node/compare/975f1930b1...61e796decc). + +## Installation + +Via [npm](http://npmjs.org/) (only required for Node.js releases older than v0.6.2): + +```bash +npm install punycode +``` + +Via [Bower](http://bower.io/): + +```bash +bower install punycode +``` + +Via [Component](https://github.com/component/component): + +```bash +component install bestiejs/punycode.js +``` + +In a browser: + +```html +<script src="punycode.js"></script> +``` + +In [Narwhal](http://narwhaljs.org/), [Node.js](http://nodejs.org/), and [RingoJS](http://ringojs.org/): + +```js +var punycode = require('punycode'); +``` + +In [Rhino](http://www.mozilla.org/rhino/): + +```js +load('punycode.js'); +``` + +Using an AMD loader like [RequireJS](http://requirejs.org/): + +```js +require( + { + 'paths': { + 'punycode': 'path/to/punycode' + } + }, + ['punycode'], + function(punycode) { + console.log(punycode); + } +); +``` + +## API + +### `punycode.decode(string)` + +Converts a Punycode string of ASCII symbols to a string of Unicode symbols. + +```js +// decode domain name parts +punycode.decode('maana-pta'); // 'mañana' +punycode.decode('--dqo34k'); // '☃-⌘' +``` + +### `punycode.encode(string)` + +Converts a string of Unicode symbols to a Punycode string of ASCII symbols. + +```js +// encode domain name parts +punycode.encode('mañana'); // 'maana-pta' +punycode.encode('☃-⌘'); // '--dqo34k' +``` + +### `punycode.toUnicode(input)` + +Converts a Punycode string representing a domain name or an email address to Unicode. Only the Punycoded parts of the input will be converted, i.e. it doesn’t matter if you call it on a string that has already been converted to Unicode. + +```js +// decode domain names +punycode.toUnicode('xn--maana-pta.com'); +// → 'mañana.com' +punycode.toUnicode('xn----dqo34k.com'); +// → '☃-⌘.com' + +// decode email addresses +punycode.toUnicode('джумла@xn--p-8sbkgc5ag7bhce.xn--ba-lmcq'); +// → 'джумла@джpумлатест.bрфa' +``` + +### `punycode.toASCII(input)` + +Converts a Unicode string representing a domain name or an email address to Punycode. Only the non-ASCII parts of the input will be converted, i.e. it doesn’t matter if you call it with a domain that's already in ASCII. + +```js +// encode domain names +punycode.toASCII('mañana.com'); +// → 'xn--maana-pta.com' +punycode.toASCII('☃-⌘.com'); +// → 'xn----dqo34k.com' + +// encode email addresses +punycode.toASCII('джумла@джpумлатест.bрфa'); +// → 'джумла@xn--p-8sbkgc5ag7bhce.xn--ba-lmcq' +``` + +### `punycode.ucs2` + +#### `punycode.ucs2.decode(string)` + +Creates an array containing the numeric code point values of each Unicode symbol in the string. While [JavaScript uses UCS-2 internally](https://mathiasbynens.be/notes/javascript-encoding), this function will convert a pair of surrogate halves (each of which UCS-2 exposes as separate characters) into a single code point, matching UTF-16. + +```js +punycode.ucs2.decode('abc'); +// → [0x61, 0x62, 0x63] +// surrogate pair for U+1D306 TETRAGRAM FOR CENTRE: +punycode.ucs2.decode('\uD834\uDF06'); +// → [0x1D306] +``` + +#### `punycode.ucs2.encode(codePoints)` + +Creates a string based on an array of numeric code point values. + +```js +punycode.ucs2.encode([0x61, 0x62, 0x63]); +// → 'abc' +punycode.ucs2.encode([0x1D306]); +// → '\uD834\uDF06' +``` + +### `punycode.version` + +A string representing the current Punycode.js version number. + +## Unit tests & code coverage + +After cloning this repository, run `npm install --dev` to install the dependencies needed for Punycode.js development and testing. You may want to install Istanbul _globally_ using `npm install istanbul -g`. + +Once that’s done, you can run the unit tests in Node using `npm test` or `node tests/tests.js`. To run the tests in Rhino, Ringo, Narwhal, PhantomJS, and web browsers as well, use `grunt test`. + +To generate the code coverage report, use `grunt cover`. + +Feel free to fork if you see possible improvements! + +## Author + +| [![twitter/mathias](https://gravatar.com/avatar/24e08a9ea84deb17ae121074d0f17125?s=70)](https://twitter.com/mathias "Follow @mathias on Twitter") | +|---| +| [Mathias Bynens](https://mathiasbynens.be/) | + +## Contributors + +| [![twitter/jdalton](https://gravatar.com/avatar/299a3d891ff1920b69c364d061007043?s=70)](https://twitter.com/jdalton "Follow @jdalton on Twitter") | +|---| +| [John-David Dalton](http://allyoucanleet.com/) | + +## License + +Punycode.js is available under the [MIT](https://mths.be/mit) license. diff --git a/node_modules/url/node_modules/punycode/package.json b/node_modules/url/node_modules/punycode/package.json new file mode 100644 index 000000000..35f046aec --- /dev/null +++ b/node_modules/url/node_modules/punycode/package.json @@ -0,0 +1,53 @@ +{ + "name": "punycode", + "version": "1.3.2", + "description": "A robust Punycode converter that fully complies to RFC 3492 and RFC 5891, and works on nearly all JavaScript platforms.", + "homepage": "https://mths.be/punycode", + "main": "punycode.js", + "keywords": [ + "punycode", + "unicode", + "idn", + "idna", + "dns", + "url", + "domain" + ], + "license": "MIT", + "author": { + "name": "Mathias Bynens", + "url": "https://mathiasbynens.be/" + }, + "contributors": [ + { + "name": "Mathias Bynens", + "url": "https://mathiasbynens.be/" + }, + { + "name": "John-David Dalton", + "url": "http://allyoucanleet.com/" + } + ], + "repository": { + "type": "git", + "url": "https://github.com/bestiejs/punycode.js.git" + }, + "bugs": "https://github.com/bestiejs/punycode.js/issues", + "files": [ + "LICENSE-MIT.txt", + "punycode.js" + ], + "scripts": { + "test": "node tests/tests.js" + }, + "devDependencies": { + "coveralls": "^2.10.1", + "grunt": "^0.4.5", + "grunt-contrib-uglify": "^0.5.0", + "grunt-shell": "^0.7.0", + "istanbul": "^0.2.13", + "qunit-extras": "^1.2.0", + "qunitjs": "~1.11.0", + "requirejs": "^2.1.14" + } +} diff --git a/node_modules/url/node_modules/punycode/punycode.js b/node_modules/url/node_modules/punycode/punycode.js new file mode 100644 index 000000000..ac6859738 --- /dev/null +++ b/node_modules/url/node_modules/punycode/punycode.js @@ -0,0 +1,530 @@ +/*! https://mths.be/punycode v1.3.2 by @mathias */ +;(function(root) { + + /** Detect free variables */ + var freeExports = typeof exports == 'object' && exports && + !exports.nodeType && exports; + var freeModule = typeof module == 'object' && module && + !module.nodeType && module; + var freeGlobal = typeof global == 'object' && global; + if ( + freeGlobal.global === freeGlobal || + freeGlobal.window === freeGlobal || + freeGlobal.self === freeGlobal + ) { + root = freeGlobal; + } + + /** + * The `punycode` object. + * @name punycode + * @type Object + */ + var punycode, + + /** Highest positive signed 32-bit float value */ + maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1 + + /** Bootstring parameters */ + base = 36, + tMin = 1, + tMax = 26, + skew = 38, + damp = 700, + initialBias = 72, + initialN = 128, // 0x80 + delimiter = '-', // '\x2D' + + /** Regular expressions */ + regexPunycode = /^xn--/, + regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars + regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators + + /** Error messages */ + errors = { + 'overflow': 'Overflow: input needs wider integers to process', + 'not-basic': 'Illegal input >= 0x80 (not a basic code point)', + 'invalid-input': 'Invalid input' + }, + + /** Convenience shortcuts */ + baseMinusTMin = base - tMin, + floor = Math.floor, + stringFromCharCode = String.fromCharCode, + + /** Temporary variable */ + key; + + /*--------------------------------------------------------------------------*/ + + /** + * A generic error utility function. + * @private + * @param {String} type The error type. + * @returns {Error} Throws a `RangeError` with the applicable error message. + */ + function error(type) { + throw RangeError(errors[type]); + } + + /** + * A generic `Array#map` utility function. + * @private + * @param {Array} array The array to iterate over. + * @param {Function} callback The function that gets called for every array + * item. + * @returns {Array} A new array of values returned by the callback function. + */ + function map(array, fn) { + var length = array.length; + var result = []; + while (length--) { + result[length] = fn(array[length]); + } + return result; + } + + /** + * A simple `Array#map`-like wrapper to work with domain name strings or email + * addresses. + * @private + * @param {String} domain The domain name or email address. + * @param {Function} callback The function that gets called for every + * character. + * @returns {Array} A new string of characters returned by the callback + * function. + */ + function mapDomain(string, fn) { + var parts = string.split('@'); + var result = ''; + if (parts.length > 1) { + // In email addresses, only the domain name should be punycoded. Leave + // the local part (i.e. everything up to `@`) intact. + result = parts[0] + '@'; + string = parts[1]; + } + // Avoid `split(regex)` for IE8 compatibility. See #17. + string = string.replace(regexSeparators, '\x2E'); + var labels = string.split('.'); + var encoded = map(labels, fn).join('.'); + return result + encoded; + } + + /** + * Creates an array containing the numeric code points of each Unicode + * character in the string. While JavaScript uses UCS-2 internally, + * this function will convert a pair of surrogate halves (each of which + * UCS-2 exposes as separate characters) into a single code point, + * matching UTF-16. + * @see `punycode.ucs2.encode` + * @see <https://mathiasbynens.be/notes/javascript-encoding> + * @memberOf punycode.ucs2 + * @name decode + * @param {String} string The Unicode input string (UCS-2). + * @returns {Array} The new array of code points. + */ + function ucs2decode(string) { + var output = [], + counter = 0, + length = string.length, + value, + extra; + while (counter < length) { + value = string.charCodeAt(counter++); + if (value >= 0xD800 && value <= 0xDBFF && counter < length) { + // high surrogate, and there is a next character + extra = string.charCodeAt(counter++); + if ((extra & 0xFC00) == 0xDC00) { // low surrogate + output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); + } else { + // unmatched surrogate; only append this code unit, in case the next + // code unit is the high surrogate of a surrogate pair + output.push(value); + counter--; + } + } else { + output.push(value); + } + } + return output; + } + + /** + * Creates a string based on an array of numeric code points. + * @see `punycode.ucs2.decode` + * @memberOf punycode.ucs2 + * @name encode + * @param {Array} codePoints The array of numeric code points. + * @returns {String} The new Unicode string (UCS-2). + */ + function ucs2encode(array) { + return map(array, function(value) { + var output = ''; + if (value > 0xFFFF) { + value -= 0x10000; + output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); + value = 0xDC00 | value & 0x3FF; + } + output += stringFromCharCode(value); + return output; + }).join(''); + } + + /** + * Converts a basic code point into a digit/integer. + * @see `digitToBasic()` + * @private + * @param {Number} codePoint The basic numeric code point value. + * @returns {Number} The numeric value of a basic code point (for use in + * representing integers) in the range `0` to `base - 1`, or `base` if + * the code point does not represent a value. + */ + function basicToDigit(codePoint) { + if (codePoint - 48 < 10) { + return codePoint - 22; + } + if (codePoint - 65 < 26) { + return codePoint - 65; + } + if (codePoint - 97 < 26) { + return codePoint - 97; + } + return base; + } + + /** + * Converts a digit/integer into a basic code point. + * @see `basicToDigit()` + * @private + * @param {Number} digit The numeric value of a basic code point. + * @returns {Number} The basic code point whose value (when used for + * representing integers) is `digit`, which needs to be in the range + * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is + * used; else, the lowercase form is used. The behavior is undefined + * if `flag` is non-zero and `digit` has no uppercase form. + */ + function digitToBasic(digit, flag) { + // 0..25 map to ASCII a..z or A..Z + // 26..35 map to ASCII 0..9 + return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5); + } + + /** + * Bias adaptation function as per section 3.4 of RFC 3492. + * http://tools.ietf.org/html/rfc3492#section-3.4 + * @private + */ + function adapt(delta, numPoints, firstTime) { + var k = 0; + delta = firstTime ? floor(delta / damp) : delta >> 1; + delta += floor(delta / numPoints); + for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) { + delta = floor(delta / baseMinusTMin); + } + return floor(k + (baseMinusTMin + 1) * delta / (delta + skew)); + } + + /** + * Converts a Punycode string of ASCII-only symbols to a string of Unicode + * symbols. + * @memberOf punycode + * @param {String} input The Punycode string of ASCII-only symbols. + * @returns {String} The resulting string of Unicode symbols. + */ + function decode(input) { + // Don't use UCS-2 + var output = [], + inputLength = input.length, + out, + i = 0, + n = initialN, + bias = initialBias, + basic, + j, + index, + oldi, + w, + k, + digit, + t, + /** Cached calculation results */ + baseMinusT; + + // Handle the basic code points: let `basic` be the number of input code + // points before the last delimiter, or `0` if there is none, then copy + // the first basic code points to the output. + + basic = input.lastIndexOf(delimiter); + if (basic < 0) { + basic = 0; + } + + for (j = 0; j < basic; ++j) { + // if it's not a basic code point + if (input.charCodeAt(j) >= 0x80) { + error('not-basic'); + } + output.push(input.charCodeAt(j)); + } + + // Main decoding loop: start just after the last delimiter if any basic code + // points were copied; start at the beginning otherwise. + + for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) { + + // `index` is the index of the next character to be consumed. + // Decode a generalized variable-length integer into `delta`, + // which gets added to `i`. The overflow checking is easier + // if we increase `i` as we go, then subtract off its starting + // value at the end to obtain `delta`. + for (oldi = i, w = 1, k = base; /* no condition */; k += base) { + + if (index >= inputLength) { + error('invalid-input'); + } + + digit = basicToDigit(input.charCodeAt(index++)); + + if (digit >= base || digit > floor((maxInt - i) / w)) { + error('overflow'); + } + + i += digit * w; + t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); + + if (digit < t) { + break; + } + + baseMinusT = base - t; + if (w > floor(maxInt / baseMinusT)) { + error('overflow'); + } + + w *= baseMinusT; + + } + + out = output.length + 1; + bias = adapt(i - oldi, out, oldi == 0); + + // `i` was supposed to wrap around from `out` to `0`, + // incrementing `n` each time, so we'll fix that now: + if (floor(i / out) > maxInt - n) { + error('overflow'); + } + + n += floor(i / out); + i %= out; + + // Insert `n` at position `i` of the output + output.splice(i++, 0, n); + + } + + return ucs2encode(output); + } + + /** + * Converts a string of Unicode symbols (e.g. a domain name label) to a + * Punycode string of ASCII-only symbols. + * @memberOf punycode + * @param {String} input The string of Unicode symbols. + * @returns {String} The resulting Punycode string of ASCII-only symbols. + */ + function encode(input) { + var n, + delta, + handledCPCount, + basicLength, + bias, + j, + m, + q, + k, + t, + currentValue, + output = [], + /** `inputLength` will hold the number of code points in `input`. */ + inputLength, + /** Cached calculation results */ + handledCPCountPlusOne, + baseMinusT, + qMinusT; + + // Convert the input in UCS-2 to Unicode + input = ucs2decode(input); + + // Cache the length + inputLength = input.length; + + // Initialize the state + n = initialN; + delta = 0; + bias = initialBias; + + // Handle the basic code points + for (j = 0; j < inputLength; ++j) { + currentValue = input[j]; + if (currentValue < 0x80) { + output.push(stringFromCharCode(currentValue)); + } + } + + handledCPCount = basicLength = output.length; + + // `handledCPCount` is the number of code points that have been handled; + // `basicLength` is the number of basic code points. + + // Finish the basic string - if it is not empty - with a delimiter + if (basicLength) { + output.push(delimiter); + } + + // Main encoding loop: + while (handledCPCount < inputLength) { + + // All non-basic code points < n have been handled already. Find the next + // larger one: + for (m = maxInt, j = 0; j < inputLength; ++j) { + currentValue = input[j]; + if (currentValue >= n && currentValue < m) { + m = currentValue; + } + } + + // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>, + // but guard against overflow + handledCPCountPlusOne = handledCPCount + 1; + if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) { + error('overflow'); + } + + delta += (m - n) * handledCPCountPlusOne; + n = m; + + for (j = 0; j < inputLength; ++j) { + currentValue = input[j]; + + if (currentValue < n && ++delta > maxInt) { + error('overflow'); + } + + if (currentValue == n) { + // Represent delta as a generalized variable-length integer + for (q = delta, k = base; /* no condition */; k += base) { + t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); + if (q < t) { + break; + } + qMinusT = q - t; + baseMinusT = base - t; + output.push( + stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0)) + ); + q = floor(qMinusT / baseMinusT); + } + + output.push(stringFromCharCode(digitToBasic(q, 0))); + bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength); + delta = 0; + ++handledCPCount; + } + } + + ++delta; + ++n; + + } + return output.join(''); + } + + /** + * Converts a Punycode string representing a domain name or an email address + * to Unicode. Only the Punycoded parts of the input will be converted, i.e. + * it doesn't matter if you call it on a string that has already been + * converted to Unicode. + * @memberOf punycode + * @param {String} input The Punycoded domain name or email address to + * convert to Unicode. + * @returns {String} The Unicode representation of the given Punycode + * string. + */ + function toUnicode(input) { + return mapDomain(input, function(string) { + return regexPunycode.test(string) + ? decode(string.slice(4).toLowerCase()) + : string; + }); + } + + /** + * Converts a Unicode string representing a domain name or an email address to + * Punycode. Only the non-ASCII parts of the domain name will be converted, + * i.e. it doesn't matter if you call it with a domain that's already in + * ASCII. + * @memberOf punycode + * @param {String} input The domain name or email address to convert, as a + * Unicode string. + * @returns {String} The Punycode representation of the given domain name or + * email address. + */ + function toASCII(input) { + return mapDomain(input, function(string) { + return regexNonASCII.test(string) + ? 'xn--' + encode(string) + : string; + }); + } + + /*--------------------------------------------------------------------------*/ + + /** Define the public API */ + punycode = { + /** + * A string representing the current Punycode.js version number. + * @memberOf punycode + * @type String + */ + 'version': '1.3.2', + /** + * An object of methods to convert from JavaScript's internal character + * representation (UCS-2) to Unicode code points, and back. + * @see <https://mathiasbynens.be/notes/javascript-encoding> + * @memberOf punycode + * @type Object + */ + 'ucs2': { + 'decode': ucs2decode, + 'encode': ucs2encode + }, + 'decode': decode, + 'encode': encode, + 'toASCII': toASCII, + 'toUnicode': toUnicode + }; + + /** Expose `punycode` */ + // Some AMD build optimizers, like r.js, check for specific condition patterns + // like the following: + if ( + typeof define == 'function' && + typeof define.amd == 'object' && + define.amd + ) { + define('punycode', function() { + return punycode; + }); + } else if (freeExports && freeModule) { + if (module.exports == freeExports) { // in Node.js or RingoJS v0.8.0+ + freeModule.exports = punycode; + } else { // in Narwhal or RingoJS v0.7.0- + for (key in punycode) { + punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]); + } + } + } else { // in Rhino or a web browser + root.punycode = punycode; + } + +}(this)); diff --git a/node_modules/url/package.json b/node_modules/url/package.json new file mode 100644 index 000000000..e3024abe7 --- /dev/null +++ b/node_modules/url/package.json @@ -0,0 +1,24 @@ +{ + "name": "url", + "description": "The core `url` packaged standalone for use with Browserify.", + "version": "0.11.0", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "main": "./url.js", + "devDependencies": { + "assert": "1.1.1", + "mocha": "1.18.2", + "zuul": "3.3.0" + }, + "scripts": { + "test": "mocha --ui qunit test.js && zuul -- test.js", + "test-local": "zuul --local -- test.js" + }, + "repository": { + "type": "git", + "url": "https://github.com/defunctzombie/node-url.git" + }, + "license": "MIT" +} diff --git a/node_modules/url/test.js b/node_modules/url/test.js new file mode 100644 index 000000000..3b7d3358c --- /dev/null +++ b/node_modules/url/test.js @@ -0,0 +1,1599 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var assert = require('assert'); + +var url = require('./url'); + +// URLs to parse, and expected data +// { url : parsed } +var parseTests = { + '//some_path' : { + 'href': '//some_path', + 'pathname': '//some_path', + 'path': '//some_path' + }, + + 'http:\\\\evil-phisher\\foo.html#h\\a\\s\\h': { + protocol: 'http:', + slashes: true, + host: 'evil-phisher', + hostname: 'evil-phisher', + pathname: '/foo.html', + path: '/foo.html', + hash: '#h%5Ca%5Cs%5Ch', + href: 'http://evil-phisher/foo.html#h%5Ca%5Cs%5Ch' + }, + + 'http:\\\\evil-phisher\\foo.html?json="\\"foo\\""#h\\a\\s\\h': { + protocol: 'http:', + slashes: true, + host: 'evil-phisher', + hostname: 'evil-phisher', + pathname: '/foo.html', + search: '?json=%22%5C%22foo%5C%22%22', + query: 'json=%22%5C%22foo%5C%22%22', + path: '/foo.html?json=%22%5C%22foo%5C%22%22', + hash: '#h%5Ca%5Cs%5Ch', + href: 'http://evil-phisher/foo.html?json=%22%5C%22foo%5C%22%22#h%5Ca%5Cs%5Ch' + }, + + 'http:\\\\evil-phisher\\foo.html#h\\a\\s\\h?blarg': { + protocol: 'http:', + slashes: true, + host: 'evil-phisher', + hostname: 'evil-phisher', + pathname: '/foo.html', + path: '/foo.html', + hash: '#h%5Ca%5Cs%5Ch?blarg', + href: 'http://evil-phisher/foo.html#h%5Ca%5Cs%5Ch?blarg' + }, + + + 'http:\\\\evil-phisher\\foo.html': { + protocol: 'http:', + slashes: true, + host: 'evil-phisher', + hostname: 'evil-phisher', + pathname: '/foo.html', + path: '/foo.html', + href: 'http://evil-phisher/foo.html' + }, + + 'HTTP://www.example.com/' : { + 'href': 'http://www.example.com/', + 'protocol': 'http:', + 'slashes': true, + 'host': 'www.example.com', + 'hostname': 'www.example.com', + 'pathname': '/', + 'path': '/' + }, + + 'HTTP://www.example.com' : { + 'href': 'http://www.example.com/', + 'protocol': 'http:', + 'slashes': true, + 'host': 'www.example.com', + 'hostname': 'www.example.com', + 'pathname': '/', + 'path': '/' + }, + + 'http://www.ExAmPlE.com/' : { + 'href': 'http://www.example.com/', + 'protocol': 'http:', + 'slashes': true, + 'host': 'www.example.com', + 'hostname': 'www.example.com', + 'pathname': '/', + 'path': '/' + }, + + 'http://user:pw@www.ExAmPlE.com/' : { + 'href': 'http://user:pw@www.example.com/', + 'protocol': 'http:', + 'slashes': true, + 'auth': 'user:pw', + 'host': 'www.example.com', + 'hostname': 'www.example.com', + 'pathname': '/', + 'path': '/' + }, + + 'http://USER:PW@www.ExAmPlE.com/' : { + 'href': 'http://USER:PW@www.example.com/', + 'protocol': 'http:', + 'slashes': true, + 'auth': 'USER:PW', + 'host': 'www.example.com', + 'hostname': 'www.example.com', + 'pathname': '/', + 'path': '/' + }, + + 'http://user@www.example.com/' : { + 'href': 'http://user@www.example.com/', + 'protocol': 'http:', + 'slashes': true, + 'auth': 'user', + 'host': 'www.example.com', + 'hostname': 'www.example.com', + 'pathname': '/', + 'path': '/' + }, + + 'http://user%3Apw@www.example.com/' : { + 'href': 'http://user:pw@www.example.com/', + 'protocol': 'http:', + 'slashes': true, + 'auth': 'user:pw', + 'host': 'www.example.com', + 'hostname': 'www.example.com', + 'pathname': '/', + 'path': '/' + }, + + 'http://x.com/path?that\'s#all, folks' : { + 'href': 'http://x.com/path?that%27s#all,%20folks', + 'protocol': 'http:', + 'slashes': true, + 'host': 'x.com', + 'hostname': 'x.com', + 'search': '?that%27s', + 'query': 'that%27s', + 'pathname': '/path', + 'hash': '#all,%20folks', + 'path': '/path?that%27s' + }, + + 'HTTP://X.COM/Y' : { + 'href': 'http://x.com/Y', + 'protocol': 'http:', + 'slashes': true, + 'host': 'x.com', + 'hostname': 'x.com', + 'pathname': '/Y', + 'path': '/Y' + }, + + // + not an invalid host character + // per https://url.spec.whatwg.org/#host-parsing + 'http://x.y.com+a/b/c' : { + 'href': 'http://x.y.com+a/b/c', + 'protocol': 'http:', + 'slashes': true, + 'host': 'x.y.com+a', + 'hostname': 'x.y.com+a', + 'pathname': '/b/c', + 'path': '/b/c' + }, + + // an unexpected invalid char in the hostname. + 'HtTp://x.y.cOm;a/b/c?d=e#f g<h>i' : { + 'href': 'http://x.y.com/;a/b/c?d=e#f%20g%3Ch%3Ei', + 'protocol': 'http:', + 'slashes': true, + 'host': 'x.y.com', + 'hostname': 'x.y.com', + 'pathname': ';a/b/c', + 'search': '?d=e', + 'query': 'd=e', + 'hash': '#f%20g%3Ch%3Ei', + 'path': ';a/b/c?d=e' + }, + + // make sure that we don't accidentally lcast the path parts. + 'HtTp://x.y.cOm;A/b/c?d=e#f g<h>i' : { + 'href': 'http://x.y.com/;A/b/c?d=e#f%20g%3Ch%3Ei', + 'protocol': 'http:', + 'slashes': true, + 'host': 'x.y.com', + 'hostname': 'x.y.com', + 'pathname': ';A/b/c', + 'search': '?d=e', + 'query': 'd=e', + 'hash': '#f%20g%3Ch%3Ei', + 'path': ';A/b/c?d=e' + }, + + 'http://x...y...#p': { + 'href': 'http://x...y.../#p', + 'protocol': 'http:', + 'slashes': true, + 'host': 'x...y...', + 'hostname': 'x...y...', + 'hash': '#p', + 'pathname': '/', + 'path': '/' + }, + + 'http://x/p/"quoted"': { + 'href': 'http://x/p/%22quoted%22', + 'protocol': 'http:', + 'slashes': true, + 'host': 'x', + 'hostname': 'x', + 'pathname': '/p/%22quoted%22', + 'path': '/p/%22quoted%22' + }, + + '<http://goo.corn/bread> Is a URL!': { + 'href': '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!', + 'pathname': '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!', + 'path': '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!' + }, + + 'http://www.narwhaljs.org/blog/categories?id=news' : { + 'href': 'http://www.narwhaljs.org/blog/categories?id=news', + 'protocol': 'http:', + 'slashes': true, + 'host': 'www.narwhaljs.org', + 'hostname': 'www.narwhaljs.org', + 'search': '?id=news', + 'query': 'id=news', + 'pathname': '/blog/categories', + 'path': '/blog/categories?id=news' + }, + + 'http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=' : { + 'href': 'http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=', + 'protocol': 'http:', + 'slashes': true, + 'host': 'mt0.google.com', + 'hostname': 'mt0.google.com', + 'pathname': '/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=', + 'path': '/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=' + }, + + 'http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=' : { + 'href': 'http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api' + + '&x=2&y=2&z=3&s=', + 'protocol': 'http:', + 'slashes': true, + 'host': 'mt0.google.com', + 'hostname': 'mt0.google.com', + 'search': '???&hl=en&src=api&x=2&y=2&z=3&s=', + 'query': '??&hl=en&src=api&x=2&y=2&z=3&s=', + 'pathname': '/vt/lyrs=m@114', + 'path': '/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=' + }, + + 'http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=': + { + 'href': 'http://user:pass@mt0.google.com/vt/lyrs=m@114???' + + '&hl=en&src=api&x=2&y=2&z=3&s=', + 'protocol': 'http:', + 'slashes': true, + 'host': 'mt0.google.com', + 'auth': 'user:pass', + 'hostname': 'mt0.google.com', + 'search': '???&hl=en&src=api&x=2&y=2&z=3&s=', + 'query': '??&hl=en&src=api&x=2&y=2&z=3&s=', + 'pathname': '/vt/lyrs=m@114', + 'path': '/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=' + }, + + 'file:///etc/passwd' : { + 'href': 'file:///etc/passwd', + 'slashes': true, + 'protocol': 'file:', + 'pathname': '/etc/passwd', + 'hostname': '', + 'host': '', + 'path': '/etc/passwd' + }, + + 'file://localhost/etc/passwd' : { + 'href': 'file://localhost/etc/passwd', + 'protocol': 'file:', + 'slashes': true, + 'pathname': '/etc/passwd', + 'hostname': 'localhost', + 'host': 'localhost', + 'path': '/etc/passwd' + }, + + 'file://foo/etc/passwd' : { + 'href': 'file://foo/etc/passwd', + 'protocol': 'file:', + 'slashes': true, + 'pathname': '/etc/passwd', + 'hostname': 'foo', + 'host': 'foo', + 'path': '/etc/passwd' + }, + + 'file:///etc/node/' : { + 'href': 'file:///etc/node/', + 'slashes': true, + 'protocol': 'file:', + 'pathname': '/etc/node/', + 'hostname': '', + 'host': '', + 'path': '/etc/node/' + }, + + 'file://localhost/etc/node/' : { + 'href': 'file://localhost/etc/node/', + 'protocol': 'file:', + 'slashes': true, + 'pathname': '/etc/node/', + 'hostname': 'localhost', + 'host': 'localhost', + 'path': '/etc/node/' + }, + + 'file://foo/etc/node/' : { + 'href': 'file://foo/etc/node/', + 'protocol': 'file:', + 'slashes': true, + 'pathname': '/etc/node/', + 'hostname': 'foo', + 'host': 'foo', + 'path': '/etc/node/' + }, + + 'http:/baz/../foo/bar' : { + 'href': 'http:/baz/../foo/bar', + 'protocol': 'http:', + 'pathname': '/baz/../foo/bar', + 'path': '/baz/../foo/bar' + }, + + 'http://user:pass@example.com:8000/foo/bar?baz=quux#frag' : { + 'href': 'http://user:pass@example.com:8000/foo/bar?baz=quux#frag', + 'protocol': 'http:', + 'slashes': true, + 'host': 'example.com:8000', + 'auth': 'user:pass', + 'port': '8000', + 'hostname': 'example.com', + 'hash': '#frag', + 'search': '?baz=quux', + 'query': 'baz=quux', + 'pathname': '/foo/bar', + 'path': '/foo/bar?baz=quux' + }, + + '//user:pass@example.com:8000/foo/bar?baz=quux#frag' : { + 'href': '//user:pass@example.com:8000/foo/bar?baz=quux#frag', + 'slashes': true, + 'host': 'example.com:8000', + 'auth': 'user:pass', + 'port': '8000', + 'hostname': 'example.com', + 'hash': '#frag', + 'search': '?baz=quux', + 'query': 'baz=quux', + 'pathname': '/foo/bar', + 'path': '/foo/bar?baz=quux' + }, + + '/foo/bar?baz=quux#frag' : { + 'href': '/foo/bar?baz=quux#frag', + 'hash': '#frag', + 'search': '?baz=quux', + 'query': 'baz=quux', + 'pathname': '/foo/bar', + 'path': '/foo/bar?baz=quux' + }, + + 'http:/foo/bar?baz=quux#frag' : { + 'href': 'http:/foo/bar?baz=quux#frag', + 'protocol': 'http:', + 'hash': '#frag', + 'search': '?baz=quux', + 'query': 'baz=quux', + 'pathname': '/foo/bar', + 'path': '/foo/bar?baz=quux' + }, + + 'mailto:foo@bar.com?subject=hello' : { + 'href': 'mailto:foo@bar.com?subject=hello', + 'protocol': 'mailto:', + 'host': 'bar.com', + 'auth' : 'foo', + 'hostname' : 'bar.com', + 'search': '?subject=hello', + 'query': 'subject=hello', + 'path': '?subject=hello' + }, + + 'javascript:alert(\'hello\');' : { + 'href': 'javascript:alert(\'hello\');', + 'protocol': 'javascript:', + 'pathname': 'alert(\'hello\');', + 'path': 'alert(\'hello\');' + }, + + 'xmpp:isaacschlueter@jabber.org' : { + 'href': 'xmpp:isaacschlueter@jabber.org', + 'protocol': 'xmpp:', + 'host': 'jabber.org', + 'auth': 'isaacschlueter', + 'hostname': 'jabber.org' + }, + + 'http://atpass:foo%40bar@127.0.0.1:8080/path?search=foo#bar' : { + 'href' : 'http://atpass:foo%40bar@127.0.0.1:8080/path?search=foo#bar', + 'protocol' : 'http:', + 'slashes': true, + 'host' : '127.0.0.1:8080', + 'auth' : 'atpass:foo@bar', + 'hostname' : '127.0.0.1', + 'port' : '8080', + 'pathname': '/path', + 'search' : '?search=foo', + 'query' : 'search=foo', + 'hash' : '#bar', + 'path': '/path?search=foo' + }, + + 'svn+ssh://foo/bar': { + 'href': 'svn+ssh://foo/bar', + 'host': 'foo', + 'hostname': 'foo', + 'protocol': 'svn+ssh:', + 'pathname': '/bar', + 'path': '/bar', + 'slashes': true + }, + + 'dash-test://foo/bar': { + 'href': 'dash-test://foo/bar', + 'host': 'foo', + 'hostname': 'foo', + 'protocol': 'dash-test:', + 'pathname': '/bar', + 'path': '/bar', + 'slashes': true + }, + + 'dash-test:foo/bar': { + 'href': 'dash-test:foo/bar', + 'host': 'foo', + 'hostname': 'foo', + 'protocol': 'dash-test:', + 'pathname': '/bar', + 'path': '/bar' + }, + + 'dot.test://foo/bar': { + 'href': 'dot.test://foo/bar', + 'host': 'foo', + 'hostname': 'foo', + 'protocol': 'dot.test:', + 'pathname': '/bar', + 'path': '/bar', + 'slashes': true + }, + + 'dot.test:foo/bar': { + 'href': 'dot.test:foo/bar', + 'host': 'foo', + 'hostname': 'foo', + 'protocol': 'dot.test:', + 'pathname': '/bar', + 'path': '/bar' + }, + + // IDNA tests + 'http://www.日本語.com/' : { + 'href': 'http://www.xn--wgv71a119e.com/', + 'protocol': 'http:', + 'slashes': true, + 'host': 'www.xn--wgv71a119e.com', + 'hostname': 'www.xn--wgv71a119e.com', + 'pathname': '/', + 'path': '/' + }, + + 'http://example.Bücher.com/' : { + 'href': 'http://example.xn--bcher-kva.com/', + 'protocol': 'http:', + 'slashes': true, + 'host': 'example.xn--bcher-kva.com', + 'hostname': 'example.xn--bcher-kva.com', + 'pathname': '/', + 'path': '/' + }, + + 'http://www.Äffchen.com/' : { + 'href': 'http://www.xn--ffchen-9ta.com/', + 'protocol': 'http:', + 'slashes': true, + 'host': 'www.xn--ffchen-9ta.com', + 'hostname': 'www.xn--ffchen-9ta.com', + 'pathname': '/', + 'path': '/' + }, + + 'http://www.Äffchen.cOm;A/b/c?d=e#f g<h>i' : { + 'href': 'http://www.xn--ffchen-9ta.com/;A/b/c?d=e#f%20g%3Ch%3Ei', + 'protocol': 'http:', + 'slashes': true, + 'host': 'www.xn--ffchen-9ta.com', + 'hostname': 'www.xn--ffchen-9ta.com', + 'pathname': ';A/b/c', + 'search': '?d=e', + 'query': 'd=e', + 'hash': '#f%20g%3Ch%3Ei', + 'path': ';A/b/c?d=e' + }, + + 'http://SÉLIER.COM/' : { + 'href': 'http://xn--slier-bsa.com/', + 'protocol': 'http:', + 'slashes': true, + 'host': 'xn--slier-bsa.com', + 'hostname': 'xn--slier-bsa.com', + 'pathname': '/', + 'path': '/' + }, + + 'http://ليهمابتكلموشعربي؟.ي؟/' : { + 'href': 'http://xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f/', + 'protocol': 'http:', + 'slashes': true, + 'host': 'xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f', + 'hostname': 'xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f', + 'pathname': '/', + 'path': '/' + }, + + 'http://➡.ws/➡' : { + 'href': 'http://xn--hgi.ws/➡', + 'protocol': 'http:', + 'slashes': true, + 'host': 'xn--hgi.ws', + 'hostname': 'xn--hgi.ws', + 'pathname': '/➡', + 'path': '/➡' + }, + + 'http://bucket_name.s3.amazonaws.com/image.jpg': { + protocol: 'http:', + 'slashes': true, + slashes: true, + host: 'bucket_name.s3.amazonaws.com', + hostname: 'bucket_name.s3.amazonaws.com', + pathname: '/image.jpg', + href: 'http://bucket_name.s3.amazonaws.com/image.jpg', + 'path': '/image.jpg' + }, + + 'git+http://github.com/joyent/node.git': { + protocol: 'git+http:', + slashes: true, + host: 'github.com', + hostname: 'github.com', + pathname: '/joyent/node.git', + path: '/joyent/node.git', + href: 'git+http://github.com/joyent/node.git' + }, + + //if local1@domain1 is uses as a relative URL it may + //be parse into auth@hostname, but here there is no + //way to make it work in url.parse, I add the test to be explicit + 'local1@domain1': { + 'pathname': 'local1@domain1', + 'path': 'local1@domain1', + 'href': 'local1@domain1' + }, + + //While this may seem counter-intuitive, a browser will parse + //<a href='www.google.com'> as a path. + 'www.example.com' : { + 'href': 'www.example.com', + 'pathname': 'www.example.com', + 'path': 'www.example.com' + }, + + // ipv6 support + '[fe80::1]': { + 'href': '[fe80::1]', + 'pathname': '[fe80::1]', + 'path': '[fe80::1]' + }, + + 'coap://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]': { + 'protocol': 'coap:', + 'slashes': true, + 'host': '[fedc:ba98:7654:3210:fedc:ba98:7654:3210]', + 'hostname': 'fedc:ba98:7654:3210:fedc:ba98:7654:3210', + 'href': 'coap://[fedc:ba98:7654:3210:fedc:ba98:7654:3210]/', + 'pathname': '/', + 'path': '/' + }, + + 'coap://[1080:0:0:0:8:800:200C:417A]:61616/': { + 'protocol': 'coap:', + 'slashes': true, + 'host': '[1080:0:0:0:8:800:200c:417a]:61616', + 'port': '61616', + 'hostname': '1080:0:0:0:8:800:200c:417a', + 'href': 'coap://[1080:0:0:0:8:800:200c:417a]:61616/', + 'pathname': '/', + 'path': '/' + }, + + 'http://user:password@[3ffe:2a00:100:7031::1]:8080': { + 'protocol': 'http:', + 'slashes': true, + 'auth': 'user:password', + 'host': '[3ffe:2a00:100:7031::1]:8080', + 'port': '8080', + 'hostname': '3ffe:2a00:100:7031::1', + 'href': 'http://user:password@[3ffe:2a00:100:7031::1]:8080/', + 'pathname': '/', + 'path': '/' + }, + + 'coap://u:p@[::192.9.5.5]:61616/.well-known/r?n=Temperature': { + 'protocol': 'coap:', + 'slashes': true, + 'auth': 'u:p', + 'host': '[::192.9.5.5]:61616', + 'port': '61616', + 'hostname': '::192.9.5.5', + 'href': 'coap://u:p@[::192.9.5.5]:61616/.well-known/r?n=Temperature', + 'search': '?n=Temperature', + 'query': 'n=Temperature', + 'pathname': '/.well-known/r', + 'path': '/.well-known/r?n=Temperature' + }, + + // empty port + 'http://example.com:': { + 'protocol': 'http:', + 'slashes': true, + 'host': 'example.com', + 'hostname': 'example.com', + 'href': 'http://example.com/', + 'pathname': '/', + 'path': '/' + }, + + 'http://example.com:/a/b.html': { + 'protocol': 'http:', + 'slashes': true, + 'host': 'example.com', + 'hostname': 'example.com', + 'href': 'http://example.com/a/b.html', + 'pathname': '/a/b.html', + 'path': '/a/b.html' + }, + + 'http://example.com:?a=b': { + 'protocol': 'http:', + 'slashes': true, + 'host': 'example.com', + 'hostname': 'example.com', + 'href': 'http://example.com/?a=b', + 'search': '?a=b', + 'query': 'a=b', + 'pathname': '/', + 'path': '/?a=b' + }, + + 'http://example.com:#abc': { + 'protocol': 'http:', + 'slashes': true, + 'host': 'example.com', + 'hostname': 'example.com', + 'href': 'http://example.com/#abc', + 'hash': '#abc', + 'pathname': '/', + 'path': '/' + }, + + 'http://[fe80::1]:/a/b?a=b#abc': { + 'protocol': 'http:', + 'slashes': true, + 'host': '[fe80::1]', + 'hostname': 'fe80::1', + 'href': 'http://[fe80::1]/a/b?a=b#abc', + 'search': '?a=b', + 'query': 'a=b', + 'hash': '#abc', + 'pathname': '/a/b', + 'path': '/a/b?a=b' + }, + + 'http://-lovemonsterz.tumblr.com/rss': { + 'protocol': 'http:', + 'slashes': true, + 'host': '-lovemonsterz.tumblr.com', + 'hostname': '-lovemonsterz.tumblr.com', + 'href': 'http://-lovemonsterz.tumblr.com/rss', + 'pathname': '/rss', + 'path': '/rss', + }, + + 'http://-lovemonsterz.tumblr.com:80/rss': { + 'protocol': 'http:', + 'slashes': true, + 'port': '80', + 'host': '-lovemonsterz.tumblr.com:80', + 'hostname': '-lovemonsterz.tumblr.com', + 'href': 'http://-lovemonsterz.tumblr.com:80/rss', + 'pathname': '/rss', + 'path': '/rss', + }, + + 'http://user:pass@-lovemonsterz.tumblr.com/rss': { + 'protocol': 'http:', + 'slashes': true, + 'auth': 'user:pass', + 'host': '-lovemonsterz.tumblr.com', + 'hostname': '-lovemonsterz.tumblr.com', + 'href': 'http://user:pass@-lovemonsterz.tumblr.com/rss', + 'pathname': '/rss', + 'path': '/rss', + }, + + 'http://user:pass@-lovemonsterz.tumblr.com:80/rss': { + 'protocol': 'http:', + 'slashes': true, + 'auth': 'user:pass', + 'port': '80', + 'host': '-lovemonsterz.tumblr.com:80', + 'hostname': '-lovemonsterz.tumblr.com', + 'href': 'http://user:pass@-lovemonsterz.tumblr.com:80/rss', + 'pathname': '/rss', + 'path': '/rss', + }, + + 'http://_jabber._tcp.google.com/test': { + 'protocol': 'http:', + 'slashes': true, + 'host': '_jabber._tcp.google.com', + 'hostname': '_jabber._tcp.google.com', + 'href': 'http://_jabber._tcp.google.com/test', + 'pathname': '/test', + 'path': '/test', + }, + + 'http://user:pass@_jabber._tcp.google.com/test': { + 'protocol': 'http:', + 'slashes': true, + 'auth': 'user:pass', + 'host': '_jabber._tcp.google.com', + 'hostname': '_jabber._tcp.google.com', + 'href': 'http://user:pass@_jabber._tcp.google.com/test', + 'pathname': '/test', + 'path': '/test', + }, + + 'http://_jabber._tcp.google.com:80/test': { + 'protocol': 'http:', + 'slashes': true, + 'port': '80', + 'host': '_jabber._tcp.google.com:80', + 'hostname': '_jabber._tcp.google.com', + 'href': 'http://_jabber._tcp.google.com:80/test', + 'pathname': '/test', + 'path': '/test', + }, + + 'http://user:pass@_jabber._tcp.google.com:80/test': { + 'protocol': 'http:', + 'slashes': true, + 'auth': 'user:pass', + 'port': '80', + 'host': '_jabber._tcp.google.com:80', + 'hostname': '_jabber._tcp.google.com', + 'href': 'http://user:pass@_jabber._tcp.google.com:80/test', + 'pathname': '/test', + 'path': '/test', + }, + + 'http://x:1/\' <>"`/{}|\\^~`/': { + protocol: 'http:', + slashes: true, + host: 'x:1', + port: '1', + hostname: 'x', + pathname: '/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/', + path: '/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/', + href: 'http://x:1/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/' + }, + + 'http://a@b@c/': { + protocol: 'http:', + slashes: true, + auth: 'a@b', + host: 'c', + hostname: 'c', + href: 'http://a%40b@c/', + path: '/', + pathname: '/' + }, + + 'http://a@b?@c': { + protocol: 'http:', + slashes: true, + auth: 'a', + host: 'b', + hostname: 'b', + href: 'http://a@b/?@c', + path: '/?@c', + pathname: '/', + search: '?@c', + query: '@c' + }, + + 'http://a\r" \t\n<\'b:b@c\r\nd/e?f':{ + protocol: 'http:', + slashes: true, + auth: 'a\r" \t\n<\'b:b', + host: 'c', + port: null, + hostname: 'c', + hash: null, + search: '?f', + query: 'f', + pathname: '%0D%0Ad/e', + path: '%0D%0Ad/e?f', + href: 'http://a%0D%22%20%09%0A%3C\'b:b@c/%0D%0Ad/e?f' + }, + + // git urls used by npm + 'git+ssh://git@github.com:npm/npm': { + protocol: 'git+ssh:', + slashes: true, + auth: 'git', + host: 'github.com', + port: null, + hostname: 'github.com', + hash: null, + search: null, + query: null, + pathname: '/:npm/npm', + path: '/:npm/npm', + href: 'git+ssh://git@github.com/:npm/npm' + } + +}; + +Object.keys(parseTests).forEach(function(u) { + test('parse(' + u + ')', function() { + var actual = url.parse(u), + spaced = url.parse(' \t ' + u + '\n\t'); + expected = parseTests[u]; + + Object.keys(actual).forEach(function (i) { + if (expected[i] === undefined && actual[i] === null) { + expected[i] = null; + } + }); + + assert.deepEqual(actual, expected); + assert.deepEqual(spaced, expected); + + var expected = parseTests[u].href, + actual = url.format(parseTests[u]); + + assert.equal(actual, expected, + 'format(' + u + ') == ' + u + '\nactual:' + actual); + }); +}); + +var parseTestsWithQueryString = { + '/foo/bar?baz=quux#frag' : { + 'href': '/foo/bar?baz=quux#frag', + 'hash': '#frag', + 'search': '?baz=quux', + 'query': { + 'baz': 'quux' + }, + 'pathname': '/foo/bar', + 'path': '/foo/bar?baz=quux' + }, + 'http://example.com' : { + 'href': 'http://example.com/', + 'protocol': 'http:', + 'slashes': true, + 'host': 'example.com', + 'hostname': 'example.com', + 'query': {}, + 'search': '', + 'pathname': '/', + 'path': '/' + }, + '/example': { + protocol: null, + slashes: null, + auth: null, + host: null, + port: null, + hostname: null, + hash: null, + search: '', + query: {}, + pathname: '/example', + path: '/example', + href: '/example' + }, + '/example?query=value':{ + protocol: null, + slashes: null, + auth: null, + host: null, + port: null, + hostname: null, + hash: null, + search: '?query=value', + query: { query: 'value' }, + pathname: '/example', + path: '/example?query=value', + href: '/example?query=value' + } +}; + +Object.keys(parseTestsWithQueryString).forEach(function(u) { + test('parse(' + u + ')', function() { + var actual = url.parse(u, true); + var expected = parseTestsWithQueryString[u]; + for (var i in actual) { + if (actual[i] === null && expected[i] === undefined) { + expected[i] = null; + } + } + + assert.deepEqual(actual, expected); + }); +}); + +// some extra formatting tests, just to verify +// that it'll format slightly wonky content to a valid url. +var formatTests = { + 'http://example.com?' : { + 'href': 'http://example.com/?', + 'protocol': 'http:', + 'slashes': true, + 'host': 'example.com', + 'hostname': 'example.com', + 'search': '?', + 'query': {}, + 'pathname': '/' + }, + 'http://example.com?foo=bar#frag' : { + 'href': 'http://example.com/?foo=bar#frag', + 'protocol': 'http:', + 'host': 'example.com', + 'hostname': 'example.com', + 'hash': '#frag', + 'search': '?foo=bar', + 'query': 'foo=bar', + 'pathname': '/' + }, + 'http://example.com?foo=@bar#frag' : { + 'href': 'http://example.com/?foo=@bar#frag', + 'protocol': 'http:', + 'host': 'example.com', + 'hostname': 'example.com', + 'hash': '#frag', + 'search': '?foo=@bar', + 'query': 'foo=@bar', + 'pathname': '/' + }, + 'http://example.com?foo=/bar/#frag' : { + 'href': 'http://example.com/?foo=/bar/#frag', + 'protocol': 'http:', + 'host': 'example.com', + 'hostname': 'example.com', + 'hash': '#frag', + 'search': '?foo=/bar/', + 'query': 'foo=/bar/', + 'pathname': '/' + }, + 'http://example.com?foo=?bar/#frag' : { + 'href': 'http://example.com/?foo=?bar/#frag', + 'protocol': 'http:', + 'host': 'example.com', + 'hostname': 'example.com', + 'hash': '#frag', + 'search': '?foo=?bar/', + 'query': 'foo=?bar/', + 'pathname': '/' + }, + 'http://example.com#frag=?bar/#frag' : { + 'href': 'http://example.com/#frag=?bar/#frag', + 'protocol': 'http:', + 'host': 'example.com', + 'hostname': 'example.com', + 'hash': '#frag=?bar/#frag', + 'pathname': '/' + }, + 'http://google.com" onload="alert(42)/' : { + 'href': 'http://google.com/%22%20onload=%22alert(42)/', + 'protocol': 'http:', + 'host': 'google.com', + 'pathname': '/%22%20onload=%22alert(42)/' + }, + 'http://a.com/a/b/c?s#h' : { + 'href': 'http://a.com/a/b/c?s#h', + 'protocol': 'http', + 'host': 'a.com', + 'pathname': 'a/b/c', + 'hash': 'h', + 'search': 's' + }, + 'xmpp:isaacschlueter@jabber.org' : { + 'href': 'xmpp:isaacschlueter@jabber.org', + 'protocol': 'xmpp:', + 'host': 'jabber.org', + 'auth': 'isaacschlueter', + 'hostname': 'jabber.org' + }, + 'http://atpass:foo%40bar@127.0.0.1/' : { + 'href': 'http://atpass:foo%40bar@127.0.0.1/', + 'auth': 'atpass:foo@bar', + 'hostname': '127.0.0.1', + 'protocol': 'http:', + 'pathname': '/' + }, + 'http://atslash%2F%40:%2F%40@foo/' : { + 'href': 'http://atslash%2F%40:%2F%40@foo/', + 'auth': 'atslash/@:/@', + 'hostname': 'foo', + 'protocol': 'http:', + 'pathname': '/' + }, + 'svn+ssh://foo/bar': { + 'href': 'svn+ssh://foo/bar', + 'hostname': 'foo', + 'protocol': 'svn+ssh:', + 'pathname': '/bar', + 'slashes': true + }, + 'dash-test://foo/bar': { + 'href': 'dash-test://foo/bar', + 'hostname': 'foo', + 'protocol': 'dash-test:', + 'pathname': '/bar', + 'slashes': true + }, + 'dash-test:foo/bar': { + 'href': 'dash-test:foo/bar', + 'hostname': 'foo', + 'protocol': 'dash-test:', + 'pathname': '/bar' + }, + 'dot.test://foo/bar': { + 'href': 'dot.test://foo/bar', + 'hostname': 'foo', + 'protocol': 'dot.test:', + 'pathname': '/bar', + 'slashes': true + }, + 'dot.test:foo/bar': { + 'href': 'dot.test:foo/bar', + 'hostname': 'foo', + 'protocol': 'dot.test:', + 'pathname': '/bar' + }, + // ipv6 support + 'coap:u:p@[::1]:61616/.well-known/r?n=Temperature': { + 'href': 'coap:u:p@[::1]:61616/.well-known/r?n=Temperature', + 'protocol': 'coap:', + 'auth': 'u:p', + 'hostname': '::1', + 'port': '61616', + 'pathname': '/.well-known/r', + 'search': 'n=Temperature' + }, + 'coap:[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616/s/stopButton': { + 'href': 'coap:[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616/s/stopButton', + 'protocol': 'coap', + 'host': '[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616', + 'pathname': '/s/stopButton' + }, + + // encode context-specific delimiters in path and query, but do not touch + // other non-delimiter chars like `%`. + // <https://github.com/joyent/node/issues/4082> + + // `#`,`?` in path + '/path/to/%%23%3F+=&.txt?foo=theA1#bar' : { + href : '/path/to/%%23%3F+=&.txt?foo=theA1#bar', + pathname: '/path/to/%#?+=&.txt', + query: { + foo: 'theA1' + }, + hash: "#bar" + }, + + // `#`,`?` in path + `#` in query + '/path/to/%%23%3F+=&.txt?foo=the%231#bar' : { + href : '/path/to/%%23%3F+=&.txt?foo=the%231#bar', + pathname: '/path/to/%#?+=&.txt', + query: { + foo: 'the#1' + }, + hash: "#bar" + }, + + // `?` and `#` in path and search + 'http://ex.com/foo%3F100%m%23r?abc=the%231?&foo=bar#frag': { + href: 'http://ex.com/foo%3F100%m%23r?abc=the%231?&foo=bar#frag', + protocol: 'http:', + hostname: 'ex.com', + hash: '#frag', + search: '?abc=the#1?&foo=bar', + pathname: '/foo?100%m#r', + }, + + // `?` and `#` in search only + 'http://ex.com/fooA100%mBr?abc=the%231?&foo=bar#frag': { + href: 'http://ex.com/fooA100%mBr?abc=the%231?&foo=bar#frag', + protocol: 'http:', + hostname: 'ex.com', + hash: '#frag', + search: '?abc=the#1?&foo=bar', + pathname: '/fooA100%mBr', + } +}; + +Object.keys(formatTests).forEach(function(u) { + test('format(' + u + ')', function() { + var expect = formatTests[u].href; + delete formatTests[u].href; + var actual = url.format(u); + var actualObj = url.format(formatTests[u]); + assert.equal(actual, expect, + 'wonky format(' + u + ') == ' + expect + + '\nactual:' + actual); + assert.equal(actualObj, expect, + 'wonky format(' + JSON.stringify(formatTests[u]) + + ') == ' + expect + + '\nactual: ' + actualObj); + }); +}); + +/* + [from, path, expected] +*/ +var relativeTests = [ + ['/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'], + ['/foo/bar/baz', '../quux/baz', '/foo/quux/baz'], + ['/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'], + ['/foo/bar/baz', '../../../../../../../quux/baz', '/quux/baz'], + ['/foo', '.', '/'], + ['/foo', '..', '/'], + ['/foo/', '.', '/foo/'], + ['/foo/', '..', '/'], + ['/foo/bar', '.', '/foo/'], + ['/foo/bar', '..', '/'], + ['/foo/bar/', '.', '/foo/bar/'], + ['/foo/bar/', '..', '/foo/'], + ['foo/bar', '../../../baz', '../../baz'], + ['foo/bar/', '../../../baz', '../baz'], + ['http://example.com/b//c//d;p?q#blarg', 'https:#hash2', 'https:///#hash2'], + ['http://example.com/b//c//d;p?q#blarg', + 'https:/p/a/t/h?s#hash2', + 'https://p/a/t/h?s#hash2'], + ['http://example.com/b//c//d;p?q#blarg', + 'https://u:p@h.com/p/a/t/h?s#hash2', + 'https://u:p@h.com/p/a/t/h?s#hash2'], + ['http://example.com/b//c//d;p?q#blarg', + 'https:/a/b/c/d', + 'https://a/b/c/d'], + ['http://example.com/b//c//d;p?q#blarg', + 'http:#hash2', + 'http://example.com/b//c//d;p?q#hash2'], + ['http://example.com/b//c//d;p?q#blarg', + 'http:/p/a/t/h?s#hash2', + 'http://example.com/p/a/t/h?s#hash2'], + ['http://example.com/b//c//d;p?q#blarg', + 'http://u:p@h.com/p/a/t/h?s#hash2', + 'http://u:p@h.com/p/a/t/h?s#hash2'], + ['http://example.com/b//c//d;p?q#blarg', + 'http:/a/b/c/d', + 'http://example.com/a/b/c/d'], + ['/foo/bar/baz', '/../etc/passwd', '/etc/passwd'] +]; + +relativeTests.forEach(function(relativeTest) { + test('resolve(' + [relativeTest[0], relativeTest[1]] + ')', function() { + var a = url.resolve(relativeTest[0], relativeTest[1]), + e = relativeTest[2]; + assert.equal(a, e, + 'resolve(' + [relativeTest[0], relativeTest[1]] + ') == ' + e + + '\n actual=' + a); + }); +}); + + +// https://github.com/joyent/node/issues/568 +[ + undefined, + null, + true, + false, + 0.0, + 0, + [], + {} +].forEach(function(val) { + test('parse(' + val + ')', function() { + assert.throws(function() { url.parse(val); }, TypeError); + }); +}); + + +// +// Tests below taken from Chiron +// http://code.google.com/p/chironjs/source/browse/trunk/src/test/http/url.js +// +// Copyright (c) 2002-2008 Kris Kowal <http://cixar.com/~kris.kowal> +// used with permission under MIT License +// +// Changes marked with @isaacs + +var bases = [ + 'http://a/b/c/d;p?q', + 'http://a/b/c/d;p?q=1/2', + 'http://a/b/c/d;p=1/2?q', + 'fred:///s//a/b/c', + 'http:///s//a/b/c' +]; + +//[to, from, result] +var relativeTests2 = [ + // http://lists.w3.org/Archives/Public/uri/2004Feb/0114.html + ['../c', 'foo:a/b', 'foo:c'], + ['foo:.', 'foo:a', 'foo:'], + ['/foo/../../../bar', 'zz:abc', 'zz:/bar'], + ['/foo/../bar', 'zz:abc', 'zz:/bar'], + // @isaacs Disagree. Not how web browsers resolve this. + ['foo/../../../bar', 'zz:abc', 'zz:bar'], + // ['foo/../../../bar', 'zz:abc', 'zz:../../bar'], // @isaacs Added + ['foo/../bar', 'zz:abc', 'zz:bar'], + ['zz:.', 'zz:abc', 'zz:'], + ['/.', bases[0], 'http://a/'], + ['/.foo', bases[0], 'http://a/.foo'], + ['.foo', bases[0], 'http://a/b/c/.foo'], + + // http://gbiv.com/protocols/uri/test/rel_examples1.html + // examples from RFC 2396 + ['g:h', bases[0], 'g:h'], + ['g', bases[0], 'http://a/b/c/g'], + ['./g', bases[0], 'http://a/b/c/g'], + ['g/', bases[0], 'http://a/b/c/g/'], + ['/g', bases[0], 'http://a/g'], + ['//g', bases[0], 'http://g/'], + // changed with RFC 2396bis + //('?y', bases[0], 'http://a/b/c/d;p?y'], + ['?y', bases[0], 'http://a/b/c/d;p?y'], + ['g?y', bases[0], 'http://a/b/c/g?y'], + // changed with RFC 2396bis + //('#s', bases[0], CURRENT_DOC_URI + '#s'], + ['#s', bases[0], 'http://a/b/c/d;p?q#s'], + ['g#s', bases[0], 'http://a/b/c/g#s'], + ['g?y#s', bases[0], 'http://a/b/c/g?y#s'], + [';x', bases[0], 'http://a/b/c/;x'], + ['g;x', bases[0], 'http://a/b/c/g;x'], + ['g;x?y#s' , bases[0], 'http://a/b/c/g;x?y#s'], + // changed with RFC 2396bis + //('', bases[0], CURRENT_DOC_URI], + ['', bases[0], 'http://a/b/c/d;p?q'], + ['.', bases[0], 'http://a/b/c/'], + ['./', bases[0], 'http://a/b/c/'], + ['..', bases[0], 'http://a/b/'], + ['../', bases[0], 'http://a/b/'], + ['../g', bases[0], 'http://a/b/g'], + ['../..', bases[0], 'http://a/'], + ['../../', bases[0], 'http://a/'], + ['../../g' , bases[0], 'http://a/g'], + ['../../../g', bases[0], ('http://a/../g', 'http://a/g')], + ['../../../../g', bases[0], ('http://a/../../g', 'http://a/g')], + // changed with RFC 2396bis + //('/./g', bases[0], 'http://a/./g'], + ['/./g', bases[0], 'http://a/g'], + // changed with RFC 2396bis + //('/../g', bases[0], 'http://a/../g'], + ['/../g', bases[0], 'http://a/g'], + ['g.', bases[0], 'http://a/b/c/g.'], + ['.g', bases[0], 'http://a/b/c/.g'], + ['g..', bases[0], 'http://a/b/c/g..'], + ['..g', bases[0], 'http://a/b/c/..g'], + ['./../g', bases[0], 'http://a/b/g'], + ['./g/.', bases[0], 'http://a/b/c/g/'], + ['g/./h', bases[0], 'http://a/b/c/g/h'], + ['g/../h', bases[0], 'http://a/b/c/h'], + ['g;x=1/./y', bases[0], 'http://a/b/c/g;x=1/y'], + ['g;x=1/../y', bases[0], 'http://a/b/c/y'], + ['g?y/./x', bases[0], 'http://a/b/c/g?y/./x'], + ['g?y/../x', bases[0], 'http://a/b/c/g?y/../x'], + ['g#s/./x', bases[0], 'http://a/b/c/g#s/./x'], + ['g#s/../x', bases[0], 'http://a/b/c/g#s/../x'], + ['http:g', bases[0], ('http:g', 'http://a/b/c/g')], + ['http:', bases[0], ('http:', bases[0])], + // not sure where this one originated + ['/a/b/c/./../../g', bases[0], 'http://a/a/g'], + + // http://gbiv.com/protocols/uri/test/rel_examples2.html + // slashes in base URI's query args + ['g', bases[1], 'http://a/b/c/g'], + ['./g', bases[1], 'http://a/b/c/g'], + ['g/', bases[1], 'http://a/b/c/g/'], + ['/g', bases[1], 'http://a/g'], + ['//g', bases[1], 'http://g/'], + // changed in RFC 2396bis + //('?y', bases[1], 'http://a/b/c/?y'], + ['?y', bases[1], 'http://a/b/c/d;p?y'], + ['g?y', bases[1], 'http://a/b/c/g?y'], + ['g?y/./x' , bases[1], 'http://a/b/c/g?y/./x'], + ['g?y/../x', bases[1], 'http://a/b/c/g?y/../x'], + ['g#s', bases[1], 'http://a/b/c/g#s'], + ['g#s/./x' , bases[1], 'http://a/b/c/g#s/./x'], + ['g#s/../x', bases[1], 'http://a/b/c/g#s/../x'], + ['./', bases[1], 'http://a/b/c/'], + ['../', bases[1], 'http://a/b/'], + ['../g', bases[1], 'http://a/b/g'], + ['../../', bases[1], 'http://a/'], + ['../../g' , bases[1], 'http://a/g'], + + // http://gbiv.com/protocols/uri/test/rel_examples3.html + // slashes in path params + // all of these changed in RFC 2396bis + ['g', bases[2], 'http://a/b/c/d;p=1/g'], + ['./g', bases[2], 'http://a/b/c/d;p=1/g'], + ['g/', bases[2], 'http://a/b/c/d;p=1/g/'], + ['g?y', bases[2], 'http://a/b/c/d;p=1/g?y'], + [';x', bases[2], 'http://a/b/c/d;p=1/;x'], + ['g;x', bases[2], 'http://a/b/c/d;p=1/g;x'], + ['g;x=1/./y', bases[2], 'http://a/b/c/d;p=1/g;x=1/y'], + ['g;x=1/../y', bases[2], 'http://a/b/c/d;p=1/y'], + ['./', bases[2], 'http://a/b/c/d;p=1/'], + ['../', bases[2], 'http://a/b/c/'], + ['../g', bases[2], 'http://a/b/c/g'], + ['../../', bases[2], 'http://a/b/'], + ['../../g' , bases[2], 'http://a/b/g'], + + // http://gbiv.com/protocols/uri/test/rel_examples4.html + // double and triple slash, unknown scheme + ['g:h', bases[3], 'g:h'], + ['g', bases[3], 'fred:///s//a/b/g'], + ['./g', bases[3], 'fred:///s//a/b/g'], + ['g/', bases[3], 'fred:///s//a/b/g/'], + ['/g', bases[3], 'fred:///g'], // may change to fred:///s//a/g + ['//g', bases[3], 'fred://g'], // may change to fred:///s//g + ['//g/x', bases[3], 'fred://g/x'], // may change to fred:///s//g/x + ['///g', bases[3], 'fred:///g'], + ['./', bases[3], 'fred:///s//a/b/'], + ['../', bases[3], 'fred:///s//a/'], + ['../g', bases[3], 'fred:///s//a/g'], + + ['../../', bases[3], 'fred:///s//'], + ['../../g' , bases[3], 'fred:///s//g'], + ['../../../g', bases[3], 'fred:///s/g'], + // may change to fred:///s//a/../../../g + ['../../../../g', bases[3], 'fred:///g'], + + // http://gbiv.com/protocols/uri/test/rel_examples5.html + // double and triple slash, well-known scheme + ['g:h', bases[4], 'g:h'], + ['g', bases[4], 'http:///s//a/b/g'], + ['./g', bases[4], 'http:///s//a/b/g'], + ['g/', bases[4], 'http:///s//a/b/g/'], + ['/g', bases[4], 'http:///g'], // may change to http:///s//a/g + ['//g', bases[4], 'http://g/'], // may change to http:///s//g + ['//g/x', bases[4], 'http://g/x'], // may change to http:///s//g/x + ['///g', bases[4], 'http:///g'], + ['./', bases[4], 'http:///s//a/b/'], + ['../', bases[4], 'http:///s//a/'], + ['../g', bases[4], 'http:///s//a/g'], + ['../../', bases[4], 'http:///s//'], + ['../../g' , bases[4], 'http:///s//g'], + // may change to http:///s//a/../../g + ['../../../g', bases[4], 'http:///s/g'], + // may change to http:///s//a/../../../g + ['../../../../g', bases[4], 'http:///g'], + + // from Dan Connelly's tests in http://www.w3.org/2000/10/swap/uripath.py + ['bar:abc', 'foo:xyz', 'bar:abc'], + ['../abc', 'http://example/x/y/z', 'http://example/x/abc'], + ['http://example/x/abc', 'http://example2/x/y/z', 'http://example/x/abc'], + ['../r', 'http://ex/x/y/z', 'http://ex/x/r'], + ['q/r', 'http://ex/x/y', 'http://ex/x/q/r'], + ['q/r#s', 'http://ex/x/y', 'http://ex/x/q/r#s'], + ['q/r#s/t', 'http://ex/x/y', 'http://ex/x/q/r#s/t'], + ['ftp://ex/x/q/r', 'http://ex/x/y', 'ftp://ex/x/q/r'], + ['', 'http://ex/x/y', 'http://ex/x/y'], + ['', 'http://ex/x/y/', 'http://ex/x/y/'], + ['', 'http://ex/x/y/pdq', 'http://ex/x/y/pdq'], + ['z/', 'http://ex/x/y/', 'http://ex/x/y/z/'], + ['#Animal', + 'file:/swap/test/animal.rdf', + 'file:/swap/test/animal.rdf#Animal'], + ['../abc', 'file:/e/x/y/z', 'file:/e/x/abc'], + ['/example/x/abc', 'file:/example2/x/y/z', 'file:/example/x/abc'], + ['../r', 'file:/ex/x/y/z', 'file:/ex/x/r'], + ['/r', 'file:/ex/x/y/z', 'file:/r'], + ['q/r', 'file:/ex/x/y', 'file:/ex/x/q/r'], + ['q/r#s', 'file:/ex/x/y', 'file:/ex/x/q/r#s'], + ['q/r#', 'file:/ex/x/y', 'file:/ex/x/q/r#'], + ['q/r#s/t', 'file:/ex/x/y', 'file:/ex/x/q/r#s/t'], + ['ftp://ex/x/q/r', 'file:/ex/x/y', 'ftp://ex/x/q/r'], + ['', 'file:/ex/x/y', 'file:/ex/x/y'], + ['', 'file:/ex/x/y/', 'file:/ex/x/y/'], + ['', 'file:/ex/x/y/pdq', 'file:/ex/x/y/pdq'], + ['z/', 'file:/ex/x/y/', 'file:/ex/x/y/z/'], + ['file://meetings.example.com/cal#m1', + 'file:/devel/WWW/2000/10/swap/test/reluri-1.n3', + 'file://meetings.example.com/cal#m1'], + ['file://meetings.example.com/cal#m1', + 'file:/home/connolly/w3ccvs/WWW/2000/10/swap/test/reluri-1.n3', + 'file://meetings.example.com/cal#m1'], + ['./#blort', 'file:/some/dir/foo', 'file:/some/dir/#blort'], + ['./#', 'file:/some/dir/foo', 'file:/some/dir/#'], + // Ryan Lee + ['./', 'http://example/x/abc.efg', 'http://example/x/'], + + + // Graham Klyne's tests + // http://www.ninebynine.org/Software/HaskellUtils/Network/UriTest.xls + // 01-31 are from Connelly's cases + + // 32-49 + ['./q:r', 'http://ex/x/y', 'http://ex/x/q:r'], + ['./p=q:r', 'http://ex/x/y', 'http://ex/x/p=q:r'], + ['?pp/rr', 'http://ex/x/y?pp/qq', 'http://ex/x/y?pp/rr'], + ['y/z', 'http://ex/x/y?pp/qq', 'http://ex/x/y/z'], + ['local/qual@domain.org#frag', + 'mailto:local', + 'mailto:local/qual@domain.org#frag'], + ['more/qual2@domain2.org#frag', + 'mailto:local/qual1@domain1.org', + 'mailto:local/more/qual2@domain2.org#frag'], + ['y?q', 'http://ex/x/y?q', 'http://ex/x/y?q'], + ['/x/y?q', 'http://ex?p', 'http://ex/x/y?q'], + ['c/d', 'foo:a/b', 'foo:a/c/d'], + ['/c/d', 'foo:a/b', 'foo:/c/d'], + ['', 'foo:a/b?c#d', 'foo:a/b?c'], + ['b/c', 'foo:a', 'foo:b/c'], + ['../b/c', 'foo:/a/y/z', 'foo:/a/b/c'], + ['./b/c', 'foo:a', 'foo:b/c'], + ['/./b/c', 'foo:a', 'foo:/b/c'], + ['../../d', 'foo://a//b/c', 'foo://a/d'], + ['.', 'foo:a', 'foo:'], + ['..', 'foo:a', 'foo:'], + + // 50-57[cf. TimBL comments -- + // http://lists.w3.org/Archives/Public/uri/2003Feb/0028.html, + // http://lists.w3.org/Archives/Public/uri/2003Jan/0008.html) + ['abc', 'http://example/x/y%2Fz', 'http://example/x/abc'], + ['../../x%2Fabc', 'http://example/a/x/y/z', 'http://example/a/x%2Fabc'], + ['../x%2Fabc', 'http://example/a/x/y%2Fz', 'http://example/a/x%2Fabc'], + ['abc', 'http://example/x%2Fy/z', 'http://example/x%2Fy/abc'], + ['q%3Ar', 'http://ex/x/y', 'http://ex/x/q%3Ar'], + ['/x%2Fabc', 'http://example/x/y%2Fz', 'http://example/x%2Fabc'], + ['/x%2Fabc', 'http://example/x/y/z', 'http://example/x%2Fabc'], + ['/x%2Fabc', 'http://example/x/y%2Fz', 'http://example/x%2Fabc'], + + // 70-77 + ['local2@domain2', 'mailto:local1@domain1?query1', 'mailto:local2@domain2'], + ['local2@domain2?query2', + 'mailto:local1@domain1', + 'mailto:local2@domain2?query2'], + ['local2@domain2?query2', + 'mailto:local1@domain1?query1', + 'mailto:local2@domain2?query2'], + ['?query2', 'mailto:local@domain?query1', 'mailto:local@domain?query2'], + ['local@domain?query2', 'mailto:?query1', 'mailto:local@domain?query2'], + ['?query2', 'mailto:local@domain?query1', 'mailto:local@domain?query2'], + ['http://example/a/b?c/../d', 'foo:bar', 'http://example/a/b?c/../d'], + ['http://example/a/b#c/../d', 'foo:bar', 'http://example/a/b#c/../d'], + + // 82-88 + // @isaacs Disagree. Not how browsers do it. + // ['http:this', 'http://example.org/base/uri', 'http:this'], + // @isaacs Added + ['http:this', 'http://example.org/base/uri', 'http://example.org/base/this'], + ['http:this', 'http:base', 'http:this'], + ['.//g', 'f:/a', 'f://g'], + ['b/c//d/e', 'f://example.org/base/a', 'f://example.org/base/b/c//d/e'], + ['m2@example.ord/c2@example.org', + 'mid:m@example.ord/c@example.org', + 'mid:m@example.ord/m2@example.ord/c2@example.org'], + ['mini1.xml', + 'file:///C:/DEV/Haskell/lib/HXmlToolbox-3.01/examples/', + 'file:///C:/DEV/Haskell/lib/HXmlToolbox-3.01/examples/mini1.xml'], + ['../b/c', 'foo:a/y/z', 'foo:a/b/c'], + + //changeing auth + ['http://diff:auth@www.example.com', + 'http://asdf:qwer@www.example.com', + 'http://diff:auth@www.example.com/'] +]; + +relativeTests2.forEach(function(relativeTest) { + test('resolve(' + [relativeTest[1], relativeTest[0]] + ')', function() { + var a = url.resolve(relativeTest[1], relativeTest[0]), + e = relativeTest[2]; + assert.equal(a, e, + 'resolve(' + [relativeTest[1], relativeTest[0]] + ') == ' + e + + '\n actual=' + a); + }); +}); + +//if format and parse are inverse operations then +//resolveObject(parse(x), y) == parse(resolve(x, y)) + +//host and hostname are special, in this case a '' value is important +var emptyIsImportant = {'host': true, 'hostname': ''}; + +//format: [from, path, expected] +relativeTests.forEach(function(relativeTest) { +test('resolveObject(' + [relativeTest[0], relativeTest[1]] + ')', function() { + var actual = url.resolveObject(url.parse(relativeTest[0]), relativeTest[1]), + expected = url.parse(relativeTest[2]); + + + assert.deepEqual(actual, expected); + + expected = relativeTest[2]; + actual = url.format(actual); + + assert.equal(actual, expected, + 'format(' + actual + ') == ' + expected + '\nactual:' + actual); + }); +}); + +//format: [to, from, result] +// the test: ['.//g', 'f:/a', 'f://g'] is a fundamental problem +// url.parse('f:/a') does not have a host +// url.resolve('f:/a', './/g') does not have a host because you have moved +// down to the g directory. i.e. f: //g, however when this url is parsed +// f:// will indicate that the host is g which is not the case. +// it is unclear to me how to keep this information from being lost +// it may be that a pathname of ////g should collapse to /g but this seems +// to be a lot of work for an edge case. Right now I remove the test +if (relativeTests2[181][0] === './/g' && + relativeTests2[181][1] === 'f:/a' && + relativeTests2[181][2] === 'f://g') { + relativeTests2.splice(181, 1); +} + +relativeTests2.forEach(function(relativeTest) { + test('resolveObject(' + [relativeTest[1], relativeTest[0]] + ')', function() { + var actual = url.resolveObject(url.parse(relativeTest[1]), relativeTest[0]), + expected = url.parse(relativeTest[2]); + + assert.deepEqual(actual, expected); + + var expected = relativeTest[2], + actual = url.format(actual); + + assert.equal(actual, expected, + 'format(' + relativeTest[1] + ') == ' + expected + + '\nactual:' + actual); + }); +}); diff --git a/node_modules/url/url.js b/node_modules/url/url.js new file mode 100644 index 000000000..23ac6f5db --- /dev/null +++ b/node_modules/url/url.js @@ -0,0 +1,732 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +var punycode = require('punycode'); +var util = require('./util'); + +exports.parse = urlParse; +exports.resolve = urlResolve; +exports.resolveObject = urlResolveObject; +exports.format = urlFormat; + +exports.Url = Url; + +function Url() { + this.protocol = null; + this.slashes = null; + this.auth = null; + this.host = null; + this.port = null; + this.hostname = null; + this.hash = null; + this.search = null; + this.query = null; + this.pathname = null; + this.path = null; + this.href = null; +} + +// Reference: RFC 3986, RFC 1808, RFC 2396 + +// define these here so at least they only have to be +// compiled once on the first module load. +var protocolPattern = /^([a-z0-9.+-]+:)/i, + portPattern = /:[0-9]*$/, + + // Special case for a simple path URL + simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/, + + // RFC 2396: characters reserved for delimiting URLs. + // We actually just auto-escape these. + delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'], + + // RFC 2396: characters not allowed for various reasons. + unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims), + + // Allowed by RFCs, but cause of XSS attacks. Always escape these. + autoEscape = ['\''].concat(unwise), + // Characters that are never ever allowed in a hostname. + // Note that any invalid chars are also handled, but these + // are the ones that are *expected* to be seen, so we fast-path + // them. + nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape), + hostEndingChars = ['/', '?', '#'], + hostnameMaxLen = 255, + hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/, + hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/, + // protocols that can allow "unsafe" and "unwise" chars. + unsafeProtocol = { + 'javascript': true, + 'javascript:': true + }, + // protocols that never have a hostname. + hostlessProtocol = { + 'javascript': true, + 'javascript:': true + }, + // protocols that always contain a // bit. + slashedProtocol = { + 'http': true, + 'https': true, + 'ftp': true, + 'gopher': true, + 'file': true, + 'http:': true, + 'https:': true, + 'ftp:': true, + 'gopher:': true, + 'file:': true + }, + querystring = require('querystring'); + +function urlParse(url, parseQueryString, slashesDenoteHost) { + if (url && util.isObject(url) && url instanceof Url) return url; + + var u = new Url; + u.parse(url, parseQueryString, slashesDenoteHost); + return u; +} + +Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) { + if (!util.isString(url)) { + throw new TypeError("Parameter 'url' must be a string, not " + typeof url); + } + + // Copy chrome, IE, opera backslash-handling behavior. + // Back slashes before the query string get converted to forward slashes + // See: https://code.google.com/p/chromium/issues/detail?id=25916 + var queryIndex = url.indexOf('?'), + splitter = + (queryIndex !== -1 && queryIndex < url.indexOf('#')) ? '?' : '#', + uSplit = url.split(splitter), + slashRegex = /\\/g; + uSplit[0] = uSplit[0].replace(slashRegex, '/'); + url = uSplit.join(splitter); + + var rest = url; + + // trim before proceeding. + // This is to support parse stuff like " http://foo.com \n" + rest = rest.trim(); + + if (!slashesDenoteHost && url.split('#').length === 1) { + // Try fast path regexp + var simplePath = simplePathPattern.exec(rest); + if (simplePath) { + this.path = rest; + this.href = rest; + this.pathname = simplePath[1]; + if (simplePath[2]) { + this.search = simplePath[2]; + if (parseQueryString) { + this.query = querystring.parse(this.search.substr(1)); + } else { + this.query = this.search.substr(1); + } + } else if (parseQueryString) { + this.search = ''; + this.query = {}; + } + return this; + } + } + + var proto = protocolPattern.exec(rest); + if (proto) { + proto = proto[0]; + var lowerProto = proto.toLowerCase(); + this.protocol = lowerProto; + rest = rest.substr(proto.length); + } + + // figure out if it's got a host + // user@server is *always* interpreted as a hostname, and url + // resolution will treat //foo/bar as host=foo,path=bar because that's + // how the browser resolves relative URLs. + if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) { + var slashes = rest.substr(0, 2) === '//'; + if (slashes && !(proto && hostlessProtocol[proto])) { + rest = rest.substr(2); + this.slashes = true; + } + } + + if (!hostlessProtocol[proto] && + (slashes || (proto && !slashedProtocol[proto]))) { + + // there's a hostname. + // the first instance of /, ?, ;, or # ends the host. + // + // If there is an @ in the hostname, then non-host chars *are* allowed + // to the left of the last @ sign, unless some host-ending character + // comes *before* the @-sign. + // URLs are obnoxious. + // + // ex: + // http://a@b@c/ => user:a@b host:c + // http://a@b?@c => user:a host:c path:/?@c + + // v0.12 TODO(isaacs): This is not quite how Chrome does things. + // Review our test case against browsers more comprehensively. + + // find the first instance of any hostEndingChars + var hostEnd = -1; + for (var i = 0; i < hostEndingChars.length; i++) { + var hec = rest.indexOf(hostEndingChars[i]); + if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) + hostEnd = hec; + } + + // at this point, either we have an explicit point where the + // auth portion cannot go past, or the last @ char is the decider. + var auth, atSign; + if (hostEnd === -1) { + // atSign can be anywhere. + atSign = rest.lastIndexOf('@'); + } else { + // atSign must be in auth portion. + // http://a@b/c@d => host:b auth:a path:/c@d + atSign = rest.lastIndexOf('@', hostEnd); + } + + // Now we have a portion which is definitely the auth. + // Pull that off. + if (atSign !== -1) { + auth = rest.slice(0, atSign); + rest = rest.slice(atSign + 1); + this.auth = decodeURIComponent(auth); + } + + // the host is the remaining to the left of the first non-host char + hostEnd = -1; + for (var i = 0; i < nonHostChars.length; i++) { + var hec = rest.indexOf(nonHostChars[i]); + if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) + hostEnd = hec; + } + // if we still have not hit it, then the entire thing is a host. + if (hostEnd === -1) + hostEnd = rest.length; + + this.host = rest.slice(0, hostEnd); + rest = rest.slice(hostEnd); + + // pull out port. + this.parseHost(); + + // we've indicated that there is a hostname, + // so even if it's empty, it has to be present. + this.hostname = this.hostname || ''; + + // if hostname begins with [ and ends with ] + // assume that it's an IPv6 address. + var ipv6Hostname = this.hostname[0] === '[' && + this.hostname[this.hostname.length - 1] === ']'; + + // validate a little. + if (!ipv6Hostname) { + var hostparts = this.hostname.split(/\./); + for (var i = 0, l = hostparts.length; i < l; i++) { + var part = hostparts[i]; + if (!part) continue; + if (!part.match(hostnamePartPattern)) { + var newpart = ''; + for (var j = 0, k = part.length; j < k; j++) { + if (part.charCodeAt(j) > 127) { + // we replace non-ASCII char with a temporary placeholder + // we need this to make sure size of hostname is not + // broken by replacing non-ASCII by nothing + newpart += 'x'; + } else { + newpart += part[j]; + } + } + // we test again with ASCII char only + if (!newpart.match(hostnamePartPattern)) { + var validParts = hostparts.slice(0, i); + var notHost = hostparts.slice(i + 1); + var bit = part.match(hostnamePartStart); + if (bit) { + validParts.push(bit[1]); + notHost.unshift(bit[2]); + } + if (notHost.length) { + rest = '/' + notHost.join('.') + rest; + } + this.hostname = validParts.join('.'); + break; + } + } + } + } + + if (this.hostname.length > hostnameMaxLen) { + this.hostname = ''; + } else { + // hostnames are always lower case. + this.hostname = this.hostname.toLowerCase(); + } + + if (!ipv6Hostname) { + // IDNA Support: Returns a punycoded representation of "domain". + // It only converts parts of the domain name that + // have non-ASCII characters, i.e. it doesn't matter if + // you call it with a domain that already is ASCII-only. + this.hostname = punycode.toASCII(this.hostname); + } + + var p = this.port ? ':' + this.port : ''; + var h = this.hostname || ''; + this.host = h + p; + this.href += this.host; + + // strip [ and ] from the hostname + // the host field still retains them, though + if (ipv6Hostname) { + this.hostname = this.hostname.substr(1, this.hostname.length - 2); + if (rest[0] !== '/') { + rest = '/' + rest; + } + } + } + + // now rest is set to the post-host stuff. + // chop off any delim chars. + if (!unsafeProtocol[lowerProto]) { + + // First, make 100% sure that any "autoEscape" chars get + // escaped, even if encodeURIComponent doesn't think they + // need to be. + for (var i = 0, l = autoEscape.length; i < l; i++) { + var ae = autoEscape[i]; + if (rest.indexOf(ae) === -1) + continue; + var esc = encodeURIComponent(ae); + if (esc === ae) { + esc = escape(ae); + } + rest = rest.split(ae).join(esc); + } + } + + + // chop off from the tail first. + var hash = rest.indexOf('#'); + if (hash !== -1) { + // got a fragment string. + this.hash = rest.substr(hash); + rest = rest.slice(0, hash); + } + var qm = rest.indexOf('?'); + if (qm !== -1) { + this.search = rest.substr(qm); + this.query = rest.substr(qm + 1); + if (parseQueryString) { + this.query = querystring.parse(this.query); + } + rest = rest.slice(0, qm); + } else if (parseQueryString) { + // no query string, but parseQueryString still requested + this.search = ''; + this.query = {}; + } + if (rest) this.pathname = rest; + if (slashedProtocol[lowerProto] && + this.hostname && !this.pathname) { + this.pathname = '/'; + } + + //to support http.request + if (this.pathname || this.search) { + var p = this.pathname || ''; + var s = this.search || ''; + this.path = p + s; + } + + // finally, reconstruct the href based on what has been validated. + this.href = this.format(); + return this; +}; + +// format a parsed object into a url string +function urlFormat(obj) { + // ensure it's an object, and not a string url. + // If it's an obj, this is a no-op. + // this way, you can call url_format() on strings + // to clean up potentially wonky urls. + if (util.isString(obj)) obj = urlParse(obj); + if (!(obj instanceof Url)) return Url.prototype.format.call(obj); + return obj.format(); +} + +Url.prototype.format = function() { + var auth = this.auth || ''; + if (auth) { + auth = encodeURIComponent(auth); + auth = auth.replace(/%3A/i, ':'); + auth += '@'; + } + + var protocol = this.protocol || '', + pathname = this.pathname || '', + hash = this.hash || '', + host = false, + query = ''; + + if (this.host) { + host = auth + this.host; + } else if (this.hostname) { + host = auth + (this.hostname.indexOf(':') === -1 ? + this.hostname : + '[' + this.hostname + ']'); + if (this.port) { + host += ':' + this.port; + } + } + + if (this.query && + util.isObject(this.query) && + Object.keys(this.query).length) { + query = querystring.stringify(this.query); + } + + var search = this.search || (query && ('?' + query)) || ''; + + if (protocol && protocol.substr(-1) !== ':') protocol += ':'; + + // only the slashedProtocols get the //. Not mailto:, xmpp:, etc. + // unless they had them to begin with. + if (this.slashes || + (!protocol || slashedProtocol[protocol]) && host !== false) { + host = '//' + (host || ''); + if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname; + } else if (!host) { + host = ''; + } + + if (hash && hash.charAt(0) !== '#') hash = '#' + hash; + if (search && search.charAt(0) !== '?') search = '?' + search; + + pathname = pathname.replace(/[?#]/g, function(match) { + return encodeURIComponent(match); + }); + search = search.replace('#', '%23'); + + return protocol + host + pathname + search + hash; +}; + +function urlResolve(source, relative) { + return urlParse(source, false, true).resolve(relative); +} + +Url.prototype.resolve = function(relative) { + return this.resolveObject(urlParse(relative, false, true)).format(); +}; + +function urlResolveObject(source, relative) { + if (!source) return relative; + return urlParse(source, false, true).resolveObject(relative); +} + +Url.prototype.resolveObject = function(relative) { + if (util.isString(relative)) { + var rel = new Url(); + rel.parse(relative, false, true); + relative = rel; + } + + var result = new Url(); + var tkeys = Object.keys(this); + for (var tk = 0; tk < tkeys.length; tk++) { + var tkey = tkeys[tk]; + result[tkey] = this[tkey]; + } + + // hash is always overridden, no matter what. + // even href="" will remove it. + result.hash = relative.hash; + + // if the relative url is empty, then there's nothing left to do here. + if (relative.href === '') { + result.href = result.format(); + return result; + } + + // hrefs like //foo/bar always cut to the protocol. + if (relative.slashes && !relative.protocol) { + // take everything except the protocol from relative + var rkeys = Object.keys(relative); + for (var rk = 0; rk < rkeys.length; rk++) { + var rkey = rkeys[rk]; + if (rkey !== 'protocol') + result[rkey] = relative[rkey]; + } + + //urlParse appends trailing / to urls like http://www.example.com + if (slashedProtocol[result.protocol] && + result.hostname && !result.pathname) { + result.path = result.pathname = '/'; + } + + result.href = result.format(); + return result; + } + + if (relative.protocol && relative.protocol !== result.protocol) { + // if it's a known url protocol, then changing + // the protocol does weird things + // first, if it's not file:, then we MUST have a host, + // and if there was a path + // to begin with, then we MUST have a path. + // if it is file:, then the host is dropped, + // because that's known to be hostless. + // anything else is assumed to be absolute. + if (!slashedProtocol[relative.protocol]) { + var keys = Object.keys(relative); + for (var v = 0; v < keys.length; v++) { + var k = keys[v]; + result[k] = relative[k]; + } + result.href = result.format(); + return result; + } + + result.protocol = relative.protocol; + if (!relative.host && !hostlessProtocol[relative.protocol]) { + var relPath = (relative.pathname || '').split('/'); + while (relPath.length && !(relative.host = relPath.shift())); + if (!relative.host) relative.host = ''; + if (!relative.hostname) relative.hostname = ''; + if (relPath[0] !== '') relPath.unshift(''); + if (relPath.length < 2) relPath.unshift(''); + result.pathname = relPath.join('/'); + } else { + result.pathname = relative.pathname; + } + result.search = relative.search; + result.query = relative.query; + result.host = relative.host || ''; + result.auth = relative.auth; + result.hostname = relative.hostname || relative.host; + result.port = relative.port; + // to support http.request + if (result.pathname || result.search) { + var p = result.pathname || ''; + var s = result.search || ''; + result.path = p + s; + } + result.slashes = result.slashes || relative.slashes; + result.href = result.format(); + return result; + } + + var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'), + isRelAbs = ( + relative.host || + relative.pathname && relative.pathname.charAt(0) === '/' + ), + mustEndAbs = (isRelAbs || isSourceAbs || + (result.host && relative.pathname)), + removeAllDots = mustEndAbs, + srcPath = result.pathname && result.pathname.split('/') || [], + relPath = relative.pathname && relative.pathname.split('/') || [], + psychotic = result.protocol && !slashedProtocol[result.protocol]; + + // if the url is a non-slashed url, then relative + // links like ../.. should be able + // to crawl up to the hostname, as well. This is strange. + // result.protocol has already been set by now. + // Later on, put the first path part into the host field. + if (psychotic) { + result.hostname = ''; + result.port = null; + if (result.host) { + if (srcPath[0] === '') srcPath[0] = result.host; + else srcPath.unshift(result.host); + } + result.host = ''; + if (relative.protocol) { + relative.hostname = null; + relative.port = null; + if (relative.host) { + if (relPath[0] === '') relPath[0] = relative.host; + else relPath.unshift(relative.host); + } + relative.host = null; + } + mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === ''); + } + + if (isRelAbs) { + // it's absolute. + result.host = (relative.host || relative.host === '') ? + relative.host : result.host; + result.hostname = (relative.hostname || relative.hostname === '') ? + relative.hostname : result.hostname; + result.search = relative.search; + result.query = relative.query; + srcPath = relPath; + // fall through to the dot-handling below. + } else if (relPath.length) { + // it's relative + // throw away the existing file, and take the new path instead. + if (!srcPath) srcPath = []; + srcPath.pop(); + srcPath = srcPath.concat(relPath); + result.search = relative.search; + result.query = relative.query; + } else if (!util.isNullOrUndefined(relative.search)) { + // just pull out the search. + // like href='?foo'. + // Put this after the other two cases because it simplifies the booleans + if (psychotic) { + result.hostname = result.host = srcPath.shift(); + //occationaly the auth can get stuck only in host + //this especially happens in cases like + //url.resolveObject('mailto:local1@domain1', 'local2@domain2') + var authInHost = result.host && result.host.indexOf('@') > 0 ? + result.host.split('@') : false; + if (authInHost) { + result.auth = authInHost.shift(); + result.host = result.hostname = authInHost.shift(); + } + } + result.search = relative.search; + result.query = relative.query; + //to support http.request + if (!util.isNull(result.pathname) || !util.isNull(result.search)) { + result.path = (result.pathname ? result.pathname : '') + + (result.search ? result.search : ''); + } + result.href = result.format(); + return result; + } + + if (!srcPath.length) { + // no path at all. easy. + // we've already handled the other stuff above. + result.pathname = null; + //to support http.request + if (result.search) { + result.path = '/' + result.search; + } else { + result.path = null; + } + result.href = result.format(); + return result; + } + + // if a url ENDs in . or .., then it must get a trailing slash. + // however, if it ends in anything else non-slashy, + // then it must NOT get a trailing slash. + var last = srcPath.slice(-1)[0]; + var hasTrailingSlash = ( + (result.host || relative.host || srcPath.length > 1) && + (last === '.' || last === '..') || last === ''); + + // strip single dots, resolve double dots to parent dir + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = srcPath.length; i >= 0; i--) { + last = srcPath[i]; + if (last === '.') { + srcPath.splice(i, 1); + } else if (last === '..') { + srcPath.splice(i, 1); + up++; + } else if (up) { + srcPath.splice(i, 1); + up--; + } + } + + // if the path is allowed to go above the root, restore leading ..s + if (!mustEndAbs && !removeAllDots) { + for (; up--; up) { + srcPath.unshift('..'); + } + } + + if (mustEndAbs && srcPath[0] !== '' && + (!srcPath[0] || srcPath[0].charAt(0) !== '/')) { + srcPath.unshift(''); + } + + if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) { + srcPath.push(''); + } + + var isAbsolute = srcPath[0] === '' || + (srcPath[0] && srcPath[0].charAt(0) === '/'); + + // put the host back + if (psychotic) { + result.hostname = result.host = isAbsolute ? '' : + srcPath.length ? srcPath.shift() : ''; + //occationaly the auth can get stuck only in host + //this especially happens in cases like + //url.resolveObject('mailto:local1@domain1', 'local2@domain2') + var authInHost = result.host && result.host.indexOf('@') > 0 ? + result.host.split('@') : false; + if (authInHost) { + result.auth = authInHost.shift(); + result.host = result.hostname = authInHost.shift(); + } + } + + mustEndAbs = mustEndAbs || (result.host && srcPath.length); + + if (mustEndAbs && !isAbsolute) { + srcPath.unshift(''); + } + + if (!srcPath.length) { + result.pathname = null; + result.path = null; + } else { + result.pathname = srcPath.join('/'); + } + + //to support request.http + if (!util.isNull(result.pathname) || !util.isNull(result.search)) { + result.path = (result.pathname ? result.pathname : '') + + (result.search ? result.search : ''); + } + result.auth = relative.auth || result.auth; + result.slashes = result.slashes || relative.slashes; + result.href = result.format(); + return result; +}; + +Url.prototype.parseHost = function() { + var host = this.host; + var port = portPattern.exec(host); + if (port) { + port = port[0]; + if (port !== ':') { + this.port = port.substr(1); + } + host = host.substr(0, host.length - port.length); + } + if (host) this.hostname = host; +}; diff --git a/node_modules/url/util.js b/node_modules/url/util.js new file mode 100644 index 000000000..97dcf31cd --- /dev/null +++ b/node_modules/url/util.js @@ -0,0 +1,16 @@ +'use strict'; + +module.exports = { + isString: function(arg) { + return typeof(arg) === 'string'; + }, + isObject: function(arg) { + return typeof(arg) === 'object' && arg !== null; + }, + isNull: function(arg) { + return arg === null; + }, + isNullOrUndefined: function(arg) { + return arg == null; + } +}; |