test.js
   1  // Copyright Joyent, Inc. and other Node contributors.
   2  //
   3  // Permission is hereby granted, free of charge, to any person obtaining a
   4  // copy of this software and associated documentation files (the
   5  // "Software"), to deal in the Software without restriction, including
   6  // without limitation the rights to use, copy, modify, merge, publish,
   7  // distribute, sublicense, and/or sell copies of the Software, and to permit
   8  // persons to whom the Software is furnished to do so, subject to the
   9  // following conditions:
  10  //
  11  // The above copyright notice and this permission notice shall be included
  12  // in all copies or substantial portions of the Software.
  13  //
  14  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  15  // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
  17  // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  18  // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  19  // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  20  // USE OR OTHER DEALINGS IN THE SOFTWARE.
  21  
  22  var assert = require('assert');
  23  var util = require('util');
  24  
  25  var url = require('./url');
  26  
  27  test('god', function() {
  28  
  29  // URLs to parse, and expected data
  30  // { url : parsed }
  31  var parseTests = {
  32    '//some_path' : {
  33      'href': '//some_path',
  34      'pathname': '//some_path',
  35      'path': '//some_path'
  36    },
  37  
  38    'HTTP://www.example.com/' : {
  39      'href': 'http://www.example.com/',
  40      'protocol': 'http:',
  41      'slashes': true,
  42      'host': 'www.example.com',
  43      'hostname': 'www.example.com',
  44      'pathname': '/',
  45      'path': '/'
  46    },
  47  
  48    'HTTP://www.example.com' : {
  49      'href': 'http://www.example.com/',
  50      'protocol': 'http:',
  51      'slashes': true,
  52      'host': 'www.example.com',
  53      'hostname': 'www.example.com',
  54      'pathname': '/',
  55      'path': '/'
  56    },
  57  
  58    'http://www.ExAmPlE.com/' : {
  59      'href': 'http://www.example.com/',
  60      'protocol': 'http:',
  61      'slashes': true,
  62      'host': 'www.example.com',
  63      'hostname': 'www.example.com',
  64      'pathname': '/',
  65      'path': '/'
  66    },
  67  
  68    'http://user:pw@www.ExAmPlE.com/' : {
  69      'href': 'http://user:pw@www.example.com/',
  70      'protocol': 'http:',
  71      'slashes': true,
  72      'auth': 'user:pw',
  73      'host': 'www.example.com',
  74      'hostname': 'www.example.com',
  75      'pathname': '/',
  76      'path': '/'
  77    },
  78  
  79    'http://USER:PW@www.ExAmPlE.com/' : {
  80      'href': 'http://USER:PW@www.example.com/',
  81      'protocol': 'http:',
  82      'slashes': true,
  83      'auth': 'USER:PW',
  84      'host': 'www.example.com',
  85      'hostname': 'www.example.com',
  86      'pathname': '/',
  87      'path': '/'
  88    },
  89  
  90    'http://user@www.example.com/' : {
  91      'href': 'http://user@www.example.com/',
  92      'protocol': 'http:',
  93      'slashes': true,
  94      'auth': 'user',
  95      'host': 'www.example.com',
  96      'hostname': 'www.example.com',
  97      'pathname': '/',
  98      'path': '/'
  99    },
 100  
 101    'http://user%3Apw@www.example.com/' : {
 102      'href': 'http://user:pw@www.example.com/',
 103      'protocol': 'http:',
 104      'slashes': true,
 105      'auth': 'user:pw',
 106      'host': 'www.example.com',
 107      'hostname': 'www.example.com',
 108      'pathname': '/',
 109      'path': '/'
 110    },
 111  
 112    'http://x.com/path?that\'s#all, folks' : {
 113      'href': 'http://x.com/path?that%27s#all,%20folks',
 114      'protocol': 'http:',
 115      'slashes': true,
 116      'host': 'x.com',
 117      'hostname': 'x.com',
 118      'search': '?that%27s',
 119      'query': 'that%27s',
 120      'pathname': '/path',
 121      'hash': '#all,%20folks',
 122      'path': '/path?that%27s'
 123    },
 124  
 125    'HTTP://X.COM/Y' : {
 126      'href': 'http://x.com/Y',
 127      'protocol': 'http:',
 128      'slashes': true,
 129      'host': 'x.com',
 130      'hostname': 'x.com',
 131      'pathname': '/Y',
 132      'path': '/Y'
 133    },
 134  
 135    // an unexpected invalid char in the hostname.
 136    'HtTp://x.y.cOm*a/b/c?d=e#f g<h>i' : {
 137      'href': 'http://x.y.com/*a/b/c?d=e#f%20g%3Ch%3Ei',
 138      'protocol': 'http:',
 139      'slashes': true,
 140      'host': 'x.y.com',
 141      'hostname': 'x.y.com',
 142      'pathname': '/*a/b/c',
 143      'search': '?d=e',
 144      'query': 'd=e',
 145      'hash': '#f%20g%3Ch%3Ei',
 146      'path': '/*a/b/c?d=e'
 147    },
 148  
 149    // make sure that we don't accidentally lcast the path parts.
 150    'HtTp://x.y.cOm*A/b/c?d=e#f g<h>i' : {
 151      'href': 'http://x.y.com/*A/b/c?d=e#f%20g%3Ch%3Ei',
 152      'protocol': 'http:',
 153      'slashes': true,
 154      'host': 'x.y.com',
 155      'hostname': 'x.y.com',
 156      'pathname': '/*A/b/c',
 157      'search': '?d=e',
 158      'query': 'd=e',
 159      'hash': '#f%20g%3Ch%3Ei',
 160      'path': '/*A/b/c?d=e'
 161    },
 162  
 163    'http://x...y...#p': {
 164      'href': 'http://x...y.../#p',
 165      'protocol': 'http:',
 166      'slashes': true,
 167      'host': 'x...y...',
 168      'hostname': 'x...y...',
 169      'hash': '#p',
 170      'pathname': '/',
 171      'path': '/'
 172    },
 173  
 174    'http://x/p/"quoted"': {
 175      'href': 'http://x/p/%22quoted%22',
 176      'protocol': 'http:',
 177      'slashes': true,
 178      'host': 'x',
 179      'hostname': 'x',
 180      'pathname': '/p/%22quoted%22',
 181      'path': '/p/%22quoted%22'
 182    },
 183  
 184    '<http://goo.corn/bread> Is a URL!': {
 185      'href': '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!',
 186      'pathname': '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!',
 187      'path': '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!'
 188    },
 189  
 190    'http://www.narwhaljs.org/blog/categories?id=news' : {
 191      'href': 'http://www.narwhaljs.org/blog/categories?id=news',
 192      'protocol': 'http:',
 193      'slashes': true,
 194      'host': 'www.narwhaljs.org',
 195      'hostname': 'www.narwhaljs.org',
 196      'search': '?id=news',
 197      'query': 'id=news',
 198      'pathname': '/blog/categories',
 199      'path': '/blog/categories?id=news'
 200    },
 201  
 202    'http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=' : {
 203      'href': 'http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=',
 204      'protocol': 'http:',
 205      'slashes': true,
 206      'host': 'mt0.google.com',
 207      'hostname': 'mt0.google.com',
 208      'pathname': '/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=',
 209      'path': '/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s='
 210    },
 211  
 212    'http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=' : {
 213      'href': 'http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api' +
 214          '&x=2&y=2&z=3&s=',
 215      'protocol': 'http:',
 216      'slashes': true,
 217      'host': 'mt0.google.com',
 218      'hostname': 'mt0.google.com',
 219      'search': '???&hl=en&src=api&x=2&y=2&z=3&s=',
 220      'query': '??&hl=en&src=api&x=2&y=2&z=3&s=',
 221      'pathname': '/vt/lyrs=m@114',
 222      'path': '/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s='
 223    },
 224  
 225    'http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=':
 226        {
 227          'href': 'http://user:pass@mt0.google.com/vt/lyrs=m@114???' +
 228              '&hl=en&src=api&x=2&y=2&z=3&s=',
 229          'protocol': 'http:',
 230          'slashes': true,
 231          'host': 'mt0.google.com',
 232          'auth': 'user:pass',
 233          'hostname': 'mt0.google.com',
 234          'search': '???&hl=en&src=api&x=2&y=2&z=3&s=',
 235          'query': '??&hl=en&src=api&x=2&y=2&z=3&s=',
 236          'pathname': '/vt/lyrs=m@114',
 237          'path': '/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s='
 238        },
 239  
 240    'file:///etc/passwd' : {
 241      'href': 'file:///etc/passwd',
 242      'slashes': true,
 243      'protocol': 'file:',
 244      'pathname': '/etc/passwd',
 245      'hostname': '',
 246      'host': '',
 247      'path': '/etc/passwd'
 248    },
 249  
 250    'file://localhost/etc/passwd' : {
 251      'href': 'file://localhost/etc/passwd',
 252      'protocol': 'file:',
 253      'slashes': true,
 254      'pathname': '/etc/passwd',
 255      'hostname': 'localhost',
 256      'host': 'localhost',
 257      'path': '/etc/passwd'
 258    },
 259  
 260    'file://foo/etc/passwd' : {
 261      'href': 'file://foo/etc/passwd',
 262      'protocol': 'file:',
 263      'slashes': true,
 264      'pathname': '/etc/passwd',
 265      'hostname': 'foo',
 266      'host': 'foo',
 267      'path': '/etc/passwd'
 268    },
 269  
 270    'file:///etc/node/' : {
 271      'href': 'file:///etc/node/',
 272      'slashes': true,
 273      'protocol': 'file:',
 274      'pathname': '/etc/node/',
 275      'hostname': '',
 276      'host': '',
 277      'path': '/etc/node/'
 278    },
 279  
 280    'file://localhost/etc/node/' : {
 281      'href': 'file://localhost/etc/node/',
 282      'protocol': 'file:',
 283      'slashes': true,
 284      'pathname': '/etc/node/',
 285      'hostname': 'localhost',
 286      'host': 'localhost',
 287      'path': '/etc/node/'
 288    },
 289  
 290    'file://foo/etc/node/' : {
 291      'href': 'file://foo/etc/node/',
 292      'protocol': 'file:',
 293      'slashes': true,
 294      'pathname': '/etc/node/',
 295      'hostname': 'foo',
 296      'host': 'foo',
 297      'path': '/etc/node/'
 298    },
 299  
 300    'http:/baz/../foo/bar' : {
 301      'href': 'http:/baz/../foo/bar',
 302      'protocol': 'http:',
 303      'pathname': '/baz/../foo/bar',
 304      'path': '/baz/../foo/bar'
 305    },
 306  
 307    'http://user:pass@example.com:8000/foo/bar?baz=quux#frag' : {
 308      'href': 'http://user:pass@example.com:8000/foo/bar?baz=quux#frag',
 309      'protocol': 'http:',
 310      'slashes': true,
 311      'host': 'example.com:8000',
 312      'auth': 'user:pass',
 313      'port': '8000',
 314      'hostname': 'example.com',
 315      'hash': '#frag',
 316      'search': '?baz=quux',
 317      'query': 'baz=quux',
 318      'pathname': '/foo/bar',
 319      'path': '/foo/bar?baz=quux'
 320    },
 321  
 322    '//user:pass@example.com:8000/foo/bar?baz=quux#frag' : {
 323      'href': '//user:pass@example.com:8000/foo/bar?baz=quux#frag',
 324      'slashes': true,
 325      'host': 'example.com:8000',
 326      'auth': 'user:pass',
 327      'port': '8000',
 328      'hostname': 'example.com',
 329      'hash': '#frag',
 330      'search': '?baz=quux',
 331      'query': 'baz=quux',
 332      'pathname': '/foo/bar',
 333      'path': '/foo/bar?baz=quux'
 334    },
 335  
 336    '/foo/bar?baz=quux#frag' : {
 337      'href': '/foo/bar?baz=quux#frag',
 338      'hash': '#frag',
 339      'search': '?baz=quux',
 340      'query': 'baz=quux',
 341      'pathname': '/foo/bar',
 342      'path': '/foo/bar?baz=quux'
 343    },
 344  
 345    'http:/foo/bar?baz=quux#frag' : {
 346      'href': 'http:/foo/bar?baz=quux#frag',
 347      'protocol': 'http:',
 348      'hash': '#frag',
 349      'search': '?baz=quux',
 350      'query': 'baz=quux',
 351      'pathname': '/foo/bar',
 352      'path': '/foo/bar?baz=quux'
 353    },
 354  
 355    'mailto:foo@bar.com?subject=hello' : {
 356      'href': 'mailto:foo@bar.com?subject=hello',
 357      'protocol': 'mailto:',
 358      'host': 'bar.com',
 359      'auth' : 'foo',
 360      'hostname' : 'bar.com',
 361      'search': '?subject=hello',
 362      'query': 'subject=hello',
 363      'path': '?subject=hello'
 364    },
 365  
 366    'javascript:alert(\'hello\');' : {
 367      'href': 'javascript:alert(\'hello\');',
 368      'protocol': 'javascript:',
 369      'pathname': 'alert(\'hello\');',
 370      'path': 'alert(\'hello\');'
 371    },
 372  
 373    'xmpp:isaacschlueter@jabber.org' : {
 374      'href': 'xmpp:isaacschlueter@jabber.org',
 375      'protocol': 'xmpp:',
 376      'host': 'jabber.org',
 377      'auth': 'isaacschlueter',
 378      'hostname': 'jabber.org'
 379    },
 380  
 381    'http://atpass:foo%40bar@127.0.0.1:8080/path?search=foo#bar' : {
 382      'href' : 'http://atpass:foo%40bar@127.0.0.1:8080/path?search=foo#bar',
 383      'protocol' : 'http:',
 384      'slashes': true,
 385      'host' : '127.0.0.1:8080',
 386      'auth' : 'atpass:foo@bar',
 387      'hostname' : '127.0.0.1',
 388      'port' : '8080',
 389      'pathname': '/path',
 390      'search' : '?search=foo',
 391      'query' : 'search=foo',
 392      'hash' : '#bar',
 393      'path': '/path?search=foo'
 394    },
 395  
 396    'svn+ssh://foo/bar': {
 397      'href': 'svn+ssh://foo/bar',
 398      'host': 'foo',
 399      'hostname': 'foo',
 400      'protocol': 'svn+ssh:',
 401      'pathname': '/bar',
 402      'path': '/bar',
 403      'slashes': true
 404    },
 405  
 406    'dash-test://foo/bar': {
 407      'href': 'dash-test://foo/bar',
 408      'host': 'foo',
 409      'hostname': 'foo',
 410      'protocol': 'dash-test:',
 411      'pathname': '/bar',
 412      'path': '/bar',
 413      'slashes': true
 414    },
 415  
 416    'dash-test:foo/bar': {
 417      'href': 'dash-test:foo/bar',
 418      'host': 'foo',
 419      'hostname': 'foo',
 420      'protocol': 'dash-test:',
 421      'pathname': '/bar',
 422      'path': '/bar'
 423    },
 424  
 425    'dot.test://foo/bar': {
 426      'href': 'dot.test://foo/bar',
 427      'host': 'foo',
 428      'hostname': 'foo',
 429      'protocol': 'dot.test:',
 430      'pathname': '/bar',
 431      'path': '/bar',
 432      'slashes': true
 433    },
 434  
 435    'dot.test:foo/bar': {
 436      'href': 'dot.test:foo/bar',
 437      'host': 'foo',
 438      'hostname': 'foo',
 439      'protocol': 'dot.test:',
 440      'pathname': '/bar',
 441      'path': '/bar'
 442    },
 443  
 444    // IDNA tests
 445    'http://www.日本語.com/' : {
 446      'href': 'http://www.xn--wgv71a119e.com/',
 447      'protocol': 'http:',
 448      'slashes': true,
 449      'host': 'www.xn--wgv71a119e.com',
 450      'hostname': 'www.xn--wgv71a119e.com',
 451      'pathname': '/',
 452      'path': '/'
 453    },
 454  
 455    'http://example.Bücher.com/' : {
 456      'href': 'http://example.xn--bcher-kva.com/',
 457      'protocol': 'http:',
 458      'slashes': true,
 459      'host': 'example.xn--bcher-kva.com',
 460      'hostname': 'example.xn--bcher-kva.com',
 461      'pathname': '/',
 462      'path': '/'
 463    },
 464  
 465    'http://www.Äffchen.com/' : {
 466      'href': 'http://www.xn--ffchen-9ta.com/',
 467      'protocol': 'http:',
 468      'slashes': true,
 469      'host': 'www.xn--ffchen-9ta.com',
 470      'hostname': 'www.xn--ffchen-9ta.com',
 471      'pathname': '/',
 472      'path': '/'
 473    },
 474  
 475    'http://www.Äffchen.cOm*A/b/c?d=e#f g<h>i' : {
 476      'href': 'http://www.xn--ffchen-9ta.com/*A/b/c?d=e#f%20g%3Ch%3Ei',
 477      'protocol': 'http:',
 478      'slashes': true,
 479      'host': 'www.xn--ffchen-9ta.com',
 480      'hostname': 'www.xn--ffchen-9ta.com',
 481      'pathname': '/*A/b/c',
 482      'search': '?d=e',
 483      'query': 'd=e',
 484      'hash': '#f%20g%3Ch%3Ei',
 485      'path': '/*A/b/c?d=e'
 486    },
 487  
 488    'http://SÉLIER.COM/' : {
 489      'href': 'http://xn--slier-bsa.com/',
 490      'protocol': 'http:',
 491      'slashes': true,
 492      'host': 'xn--slier-bsa.com',
 493      'hostname': 'xn--slier-bsa.com',
 494      'pathname': '/',
 495      'path': '/'
 496    },
 497  
 498    'http://ليهمابتكلموشعربي؟.ي؟/' : {
 499      'href': 'http://xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f/',
 500      'protocol': 'http:',
 501      'slashes': true,
 502      'host': 'xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f',
 503      'hostname': 'xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f',
 504      'pathname': '/',
 505      'path': '/'
 506    },
 507  
 508    'http://âž¡.ws/âž¡' : {
 509      'href': 'http://xn--hgi.ws/âž¡',
 510      'protocol': 'http:',
 511      'slashes': true,
 512      'host': 'xn--hgi.ws',
 513      'hostname': 'xn--hgi.ws',
 514      'pathname': '/âž¡',
 515      'path': '/âž¡'
 516    },
 517  
 518    'http://bucket_name.s3.amazonaws.com/image.jpg': {
 519      protocol: 'http:',
 520      slashes: true,
 521      host: 'bucket_name.s3.amazonaws.com',
 522      hostname: 'bucket_name.s3.amazonaws.com',
 523      pathname: '/image.jpg',
 524      href: 'http://bucket_name.s3.amazonaws.com/image.jpg',
 525      'path': '/image.jpg'
 526    },
 527  
 528    'git+http://github.com/joyent/node.git': {
 529      protocol: 'git+http:',
 530      slashes: true,
 531      host: 'github.com',
 532      hostname: 'github.com',
 533      pathname: '/joyent/node.git',
 534      path: '/joyent/node.git',
 535      href: 'git+http://github.com/joyent/node.git'
 536    },
 537  
 538    //if local1@domain1 is uses as a relative URL it may
 539    //be parse into auth@hostname, but here there is no
 540    //way to make it work in url.parse, I add the test to be explicit
 541    'local1@domain1': {
 542      'pathname': 'local1@domain1',
 543      'path': 'local1@domain1',
 544      'href': 'local1@domain1'
 545    },
 546  
 547    //While this may seem counter-intuitive, a browser will parse
 548    //<a href='www.google.com'> as a path.
 549    'www.example.com' : {
 550      'href': 'www.example.com',
 551      'pathname': 'www.example.com',
 552      'path': 'www.example.com'
 553    },
 554  
 555    // ipv6 support
 556    '[fe80::1]': {
 557      'href': '[fe80::1]',
 558      'pathname': '[fe80::1]',
 559      'path': '[fe80::1]'
 560    },
 561  
 562    'coap://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]': {
 563      'protocol': 'coap:',
 564      'slashes': true,
 565      'host': '[fedc:ba98:7654:3210:fedc:ba98:7654:3210]',
 566      'hostname': 'fedc:ba98:7654:3210:fedc:ba98:7654:3210',
 567      'href': 'coap://[fedc:ba98:7654:3210:fedc:ba98:7654:3210]/',
 568      'pathname': '/',
 569      'path': '/'
 570    },
 571  
 572    'coap://[1080:0:0:0:8:800:200C:417A]:61616/': {
 573      'protocol': 'coap:',
 574      'slashes': true,
 575      'host': '[1080:0:0:0:8:800:200c:417a]:61616',
 576      'port': '61616',
 577      'hostname': '1080:0:0:0:8:800:200c:417a',
 578      'href': 'coap://[1080:0:0:0:8:800:200c:417a]:61616/',
 579      'pathname': '/',
 580      'path': '/'
 581    },
 582  
 583    'http://user:password@[3ffe:2a00:100:7031::1]:8080': {
 584      'protocol': 'http:',
 585      'slashes': true,
 586      'auth': 'user:password',
 587      'host': '[3ffe:2a00:100:7031::1]:8080',
 588      'port': '8080',
 589      'hostname': '3ffe:2a00:100:7031::1',
 590      'href': 'http://user:password@[3ffe:2a00:100:7031::1]:8080/',
 591      'pathname': '/',
 592      'path': '/'
 593    },
 594  
 595    'coap://u:p@[::192.9.5.5]:61616/.well-known/r?n=Temperature': {
 596      'protocol': 'coap:',
 597      'slashes': true,
 598      'auth': 'u:p',
 599      'host': '[::192.9.5.5]:61616',
 600      'port': '61616',
 601      'hostname': '::192.9.5.5',
 602      'href': 'coap://u:p@[::192.9.5.5]:61616/.well-known/r?n=Temperature',
 603      'search': '?n=Temperature',
 604      'query': 'n=Temperature',
 605      'pathname': '/.well-known/r',
 606      'path': '/.well-known/r?n=Temperature'
 607    },
 608  
 609    // empty port
 610    'http://example.com:': {
 611      'protocol': 'http:',
 612      'slashes': true,
 613      'host': 'example.com',
 614      'hostname': 'example.com',
 615      'href': 'http://example.com/',
 616      'pathname': '/',
 617      'path': '/'
 618    },
 619  
 620    'http://example.com:/a/b.html': {
 621      'protocol': 'http:',
 622      'slashes': true,
 623      'host': 'example.com',
 624      'hostname': 'example.com',
 625      'href': 'http://example.com/a/b.html',
 626      'pathname': '/a/b.html',
 627      'path': '/a/b.html'
 628    },
 629  
 630    'http://example.com:?a=b': {
 631      'protocol': 'http:',
 632      'slashes': true,
 633      'host': 'example.com',
 634      'hostname': 'example.com',
 635      'href': 'http://example.com/?a=b',
 636      'search': '?a=b',
 637      'query': 'a=b',
 638      'pathname': '/',
 639      'path': '/?a=b'
 640    },
 641  
 642    'http://example.com:#abc': {
 643      'protocol': 'http:',
 644      'slashes': true,
 645      'host': 'example.com',
 646      'hostname': 'example.com',
 647      'href': 'http://example.com/#abc',
 648      'hash': '#abc',
 649      'pathname': '/',
 650      'path': '/'
 651    },
 652  
 653    'http://[fe80::1]:/a/b?a=b#abc': {
 654      'protocol': 'http:',
 655      'slashes': true,
 656      'host': '[fe80::1]',
 657      'hostname': 'fe80::1',
 658      'href': 'http://[fe80::1]/a/b?a=b#abc',
 659      'search': '?a=b',
 660      'query': 'a=b',
 661      'hash': '#abc',
 662      'pathname': '/a/b',
 663      'path': '/a/b?a=b'
 664    },
 665  
 666    'http://-lovemonsterz.tumblr.com/rss': {
 667      'protocol': 'http:',
 668      'slashes': true,
 669      'host': '-lovemonsterz.tumblr.com',
 670      'hostname': '-lovemonsterz.tumblr.com',
 671      'href': 'http://-lovemonsterz.tumblr.com/rss',
 672      'pathname': '/rss',
 673      'path': '/rss',
 674    },
 675  
 676    'http://-lovemonsterz.tumblr.com:80/rss': {
 677      'protocol': 'http:',
 678      'slashes': true,
 679      'port': '80',
 680      'host': '-lovemonsterz.tumblr.com:80',
 681      'hostname': '-lovemonsterz.tumblr.com',
 682      'href': 'http://-lovemonsterz.tumblr.com:80/rss',
 683      'pathname': '/rss',
 684      'path': '/rss',
 685    },
 686  
 687    'http://user:pass@-lovemonsterz.tumblr.com/rss': {
 688      'protocol': 'http:',
 689      'slashes': true,
 690      'auth': 'user:pass',
 691      'host': '-lovemonsterz.tumblr.com',
 692      'hostname': '-lovemonsterz.tumblr.com',
 693      'href': 'http://user:pass@-lovemonsterz.tumblr.com/rss',
 694      'pathname': '/rss',
 695      'path': '/rss',
 696    },
 697  
 698    'http://user:pass@-lovemonsterz.tumblr.com:80/rss': {
 699      'protocol': 'http:',
 700      'slashes': true,
 701      'auth': 'user:pass',
 702      'port': '80',
 703      'host': '-lovemonsterz.tumblr.com:80',
 704      'hostname': '-lovemonsterz.tumblr.com',
 705      'href': 'http://user:pass@-lovemonsterz.tumblr.com:80/rss',
 706      'pathname': '/rss',
 707      'path': '/rss',
 708    },
 709  
 710    'http://_jabber._tcp.google.com/test': {
 711      'protocol': 'http:',
 712      'slashes': true,
 713      'host': '_jabber._tcp.google.com',
 714      'hostname': '_jabber._tcp.google.com',
 715      'href': 'http://_jabber._tcp.google.com/test',
 716      'pathname': '/test',
 717      'path': '/test',
 718    },
 719  
 720    'http://user:pass@_jabber._tcp.google.com/test': {
 721      'protocol': 'http:',
 722      'slashes': true,
 723      'auth': 'user:pass',
 724      'host': '_jabber._tcp.google.com',
 725      'hostname': '_jabber._tcp.google.com',
 726      'href': 'http://user:pass@_jabber._tcp.google.com/test',
 727      'pathname': '/test',
 728      'path': '/test',
 729    },
 730  
 731    'http://_jabber._tcp.google.com:80/test': {
 732      'protocol': 'http:',
 733      'slashes': true,
 734      'port': '80',
 735      'host': '_jabber._tcp.google.com:80',
 736      'hostname': '_jabber._tcp.google.com',
 737      'href': 'http://_jabber._tcp.google.com:80/test',
 738      'pathname': '/test',
 739      'path': '/test',
 740    },
 741  
 742    'http://user:pass@_jabber._tcp.google.com:80/test': {
 743      'protocol': 'http:',
 744      'slashes': true,
 745      'auth': 'user:pass',
 746      'port': '80',
 747      'host': '_jabber._tcp.google.com:80',
 748      'hostname': '_jabber._tcp.google.com',
 749      'href': 'http://user:pass@_jabber._tcp.google.com:80/test',
 750      'pathname': '/test',
 751      'path': '/test',
 752    },
 753  
 754    'http://a@b@c/': {
 755      protocol: 'http:',
 756      slashes: true,
 757      auth: 'a@b',
 758      host: 'c',
 759      hostname: 'c',
 760      href: 'http://a%40b@c/',
 761      path: '/',
 762      pathname: '/'
 763    },
 764  
 765    'http://a@b?@c': {
 766      protocol: 'http:',
 767      slashes: true,
 768      auth: 'a',
 769      host: 'b',
 770      hostname: 'b',
 771      href: 'http://a@b/?@c',
 772      path: '/?@c',
 773      pathname: '/',
 774      search: '?@c',
 775      query: '@c'
 776    },
 777  
 778    'http://a\r" \t\n<\'b:b@c\r\nd/e?f':{
 779      protocol: 'http:',
 780      slashes: true,
 781      auth: 'a\r" \t\n<\'b:b',
 782      host: 'c',
 783      port: null,
 784      hostname: 'c',
 785      hash: null,
 786      search: '?f',
 787      query: 'f',
 788      pathname: '%0D%0Ad/e',
 789      path: '%0D%0Ad/e?f',
 790      href: 'http://a%0D%22%20%09%0A%3C\'b:b@c/%0D%0Ad/e?f'
 791    }
 792  
 793  };
 794  
 795  for (var u in parseTests) {
 796    var actual = url.parse(u),
 797        spaced = url.parse('     \t  ' + u + '\n\t');
 798        expected = parseTests[u];
 799  
 800    Object.keys(actual).forEach(function (i) {
 801      if (expected[i] === undefined && actual[i] === null) {
 802        expected[i] = null;
 803      }
 804    });
 805  
 806    assert.deepEqual(actual, expected);
 807    assert.deepEqual(spaced, expected);
 808  
 809    var expected = parseTests[u].href,
 810        actual = url.format(parseTests[u]);
 811  
 812    assert.equal(actual, expected,
 813                 'format(' + u + ') == ' + u + '\nactual:' + actual);
 814  }
 815  
 816  var parseTestsWithQueryString = {
 817    '/foo/bar?baz=quux#frag' : {
 818      'href': '/foo/bar?baz=quux#frag',
 819      'hash': '#frag',
 820      'search': '?baz=quux',
 821      'query': {
 822        'baz': 'quux'
 823      },
 824      'pathname': '/foo/bar',
 825      'path': '/foo/bar?baz=quux'
 826    },
 827    'http://example.com' : {
 828      'href': 'http://example.com/',
 829      'protocol': 'http:',
 830      'slashes': true,
 831      'host': 'example.com',
 832      'hostname': 'example.com',
 833      'query': {},
 834      'search': '',
 835      'pathname': '/',
 836      'path': '/'
 837    }
 838  };
 839  for (var u in parseTestsWithQueryString) {
 840    var actual = url.parse(u, true);
 841    var expected = parseTestsWithQueryString[u];
 842    for (var i in actual) {
 843      if (actual[i] === null && expected[i] === undefined) {
 844        expected[i] = null;
 845      }
 846    }
 847  
 848    assert.deepEqual(actual, expected);
 849  }
 850  
 851  // some extra formatting tests, just to verify
 852  // that it'll format slightly wonky content to a valid url.
 853  var formatTests = {
 854    'http://example.com?' : {
 855      'href': 'http://example.com/?',
 856      'protocol': 'http:',
 857      'slashes': true,
 858      'host': 'example.com',
 859      'hostname': 'example.com',
 860      'search': '?',
 861      'query': {},
 862      'pathname': '/'
 863    },
 864    'http://example.com?foo=bar#frag' : {
 865      'href': 'http://example.com/?foo=bar#frag',
 866      'protocol': 'http:',
 867      'host': 'example.com',
 868      'hostname': 'example.com',
 869      'hash': '#frag',
 870      'search': '?foo=bar',
 871      'query': 'foo=bar',
 872      'pathname': '/'
 873    },
 874    'http://example.com?foo=@bar#frag' : {
 875      'href': 'http://example.com/?foo=@bar#frag',
 876      'protocol': 'http:',
 877      'host': 'example.com',
 878      'hostname': 'example.com',
 879      'hash': '#frag',
 880      'search': '?foo=@bar',
 881      'query': 'foo=@bar',
 882      'pathname': '/'
 883    },
 884    'http://example.com?foo=/bar/#frag' : {
 885      'href': 'http://example.com/?foo=/bar/#frag',
 886      'protocol': 'http:',
 887      'host': 'example.com',
 888      'hostname': 'example.com',
 889      'hash': '#frag',
 890      'search': '?foo=/bar/',
 891      'query': 'foo=/bar/',
 892      'pathname': '/'
 893    },
 894    'http://example.com?foo=?bar/#frag' : {
 895      'href': 'http://example.com/?foo=?bar/#frag',
 896      'protocol': 'http:',
 897      'host': 'example.com',
 898      'hostname': 'example.com',
 899      'hash': '#frag',
 900      'search': '?foo=?bar/',
 901      'query': 'foo=?bar/',
 902      'pathname': '/'
 903    },
 904    'http://example.com#frag=?bar/#frag' : {
 905      'href': 'http://example.com/#frag=?bar/#frag',
 906      'protocol': 'http:',
 907      'host': 'example.com',
 908      'hostname': 'example.com',
 909      'hash': '#frag=?bar/#frag',
 910      'pathname': '/'
 911    },
 912    'http://google.com" onload="alert(42)/' : {
 913      'href': 'http://google.com/%22%20onload=%22alert(42)/',
 914      'protocol': 'http:',
 915      'host': 'google.com',
 916      'pathname': '/%22%20onload=%22alert(42)/'
 917    },
 918    'http://a.com/a/b/c?s#h' : {
 919      'href': 'http://a.com/a/b/c?s#h',
 920      'protocol': 'http',
 921      'host': 'a.com',
 922      'pathname': 'a/b/c',
 923      'hash': 'h',
 924      'search': 's'
 925    },
 926    'xmpp:isaacschlueter@jabber.org' : {
 927      'href': 'xmpp:isaacschlueter@jabber.org',
 928      'protocol': 'xmpp:',
 929      'host': 'jabber.org',
 930      'auth': 'isaacschlueter',
 931      'hostname': 'jabber.org'
 932    },
 933    'http://atpass:foo%40bar@127.0.0.1/' : {
 934      'href': 'http://atpass:foo%40bar@127.0.0.1/',
 935      'auth': 'atpass:foo@bar',
 936      'hostname': '127.0.0.1',
 937      'protocol': 'http:',
 938      'pathname': '/'
 939    },
 940    'http://atslash%2F%40:%2F%40@foo/' : {
 941      'href': 'http://atslash%2F%40:%2F%40@foo/',
 942      'auth': 'atslash/@:/@',
 943      'hostname': 'foo',
 944      'protocol': 'http:',
 945      'pathname': '/'
 946    },
 947    'svn+ssh://foo/bar': {
 948      'href': 'svn+ssh://foo/bar',
 949      'hostname': 'foo',
 950      'protocol': 'svn+ssh:',
 951      'pathname': '/bar',
 952      'slashes': true
 953    },
 954    'dash-test://foo/bar': {
 955      'href': 'dash-test://foo/bar',
 956      'hostname': 'foo',
 957      'protocol': 'dash-test:',
 958      'pathname': '/bar',
 959      'slashes': true
 960    },
 961    'dash-test:foo/bar': {
 962      'href': 'dash-test:foo/bar',
 963      'hostname': 'foo',
 964      'protocol': 'dash-test:',
 965      'pathname': '/bar'
 966    },
 967    'dot.test://foo/bar': {
 968      'href': 'dot.test://foo/bar',
 969      'hostname': 'foo',
 970      'protocol': 'dot.test:',
 971      'pathname': '/bar',
 972      'slashes': true
 973    },
 974    'dot.test:foo/bar': {
 975      'href': 'dot.test:foo/bar',
 976      'hostname': 'foo',
 977      'protocol': 'dot.test:',
 978      'pathname': '/bar'
 979    },
 980    // ipv6 support
 981    'coap:u:p@[::1]:61616/.well-known/r?n=Temperature': {
 982      'href': 'coap:u:p@[::1]:61616/.well-known/r?n=Temperature',
 983      'protocol': 'coap:',
 984      'auth': 'u:p',
 985      'hostname': '::1',
 986      'port': '61616',
 987      'pathname': '/.well-known/r',
 988      'search': 'n=Temperature'
 989    },
 990    'coap:[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616/s/stopButton': {
 991      'href': 'coap:[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616/s/stopButton',
 992      'protocol': 'coap',
 993      'host': '[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616',
 994      'pathname': '/s/stopButton'
 995    },
 996  
 997    // encode context-specific delimiters in path and query, but do not touch
 998    // other non-delimiter chars like `%`.
 999    // <https://github.com/joyent/node/issues/4082>
1000  
1001    // `#`,`?` in path
1002    '/path/to/%%23%3F+=&.txt?foo=theA1#bar' : {
1003      href : '/path/to/%%23%3F+=&.txt?foo=theA1#bar',
1004      pathname: '/path/to/%#?+=&.txt',
1005      query: {
1006        foo: 'theA1'
1007      },
1008      hash: "#bar"
1009    },
1010  
1011    // `#`,`?` in path + `#` in query
1012    '/path/to/%%23%3F+=&.txt?foo=the%231#bar' : {
1013      href : '/path/to/%%23%3F+=&.txt?foo=the%231#bar',
1014      pathname: '/path/to/%#?+=&.txt',
1015      query: {
1016        foo: 'the#1'
1017      },
1018      hash: "#bar"
1019    },
1020  
1021    // `?` and `#` in path and search
1022    'http://ex.com/foo%3F100%m%23r?abc=the%231?&foo=bar#frag': {
1023      href: 'http://ex.com/foo%3F100%m%23r?abc=the%231?&foo=bar#frag',
1024      protocol: 'http:',
1025      hostname: 'ex.com',
1026      hash: '#frag',
1027      search: '?abc=the#1?&foo=bar',
1028      pathname: '/foo?100%m#r',
1029    },
1030  
1031    // `?` and `#` in search only
1032    'http://ex.com/fooA100%mBr?abc=the%231?&foo=bar#frag': {
1033      href: 'http://ex.com/fooA100%mBr?abc=the%231?&foo=bar#frag',
1034      protocol: 'http:',
1035      hostname: 'ex.com',
1036      hash: '#frag',
1037      search: '?abc=the#1?&foo=bar',
1038      pathname: '/fooA100%mBr',
1039    }
1040  };
1041  for (var u in formatTests) {
1042    var expect = formatTests[u].href;
1043    delete formatTests[u].href;
1044    var actual = url.format(u);
1045    var actualObj = url.format(formatTests[u]);
1046    assert.equal(actual, expect,
1047                 'wonky format(' + u + ') == ' + expect +
1048                 '\nactual:' + actual);
1049    assert.equal(actualObj, expect,
1050                 'wonky format(' + JSON.stringify(formatTests[u]) +
1051                 ') == ' + expect +
1052                 '\nactual: ' + actualObj);
1053  }
1054  
1055  /*
1056   [from, path, expected]
1057  */
1058  var relativeTests = [
1059    ['/foo/bar/baz', 'quux', '/foo/bar/quux'],
1060    ['/foo/bar/baz', 'quux/asdf', '/foo/bar/quux/asdf'],
1061    ['/foo/bar/baz', 'quux/baz', '/foo/bar/quux/baz'],
1062    ['/foo/bar/baz', '../quux/baz', '/foo/quux/baz'],
1063    ['/foo/bar/baz', '/bar', '/bar'],
1064    ['/foo/bar/baz/', 'quux', '/foo/bar/baz/quux'],
1065    ['/foo/bar/baz/', 'quux/baz', '/foo/bar/baz/quux/baz'],
1066    ['/foo/bar/baz', '../../../../../../../../quux/baz', '/quux/baz'],
1067    ['/foo/bar/baz', '../../../../../../../quux/baz', '/quux/baz'],
1068    ['foo/bar', '../../../baz', '../../baz'],
1069    ['foo/bar/', '../../../baz', '../baz'],
1070    ['http://example.com/b//c//d;p?q#blarg', 'https:#hash2', 'https:///#hash2'],
1071    ['http://example.com/b//c//d;p?q#blarg',
1072     'https:/p/a/t/h?s#hash2',
1073     'https://p/a/t/h?s#hash2'],
1074    ['http://example.com/b//c//d;p?q#blarg',
1075     'https://u:p@h.com/p/a/t/h?s#hash2',
1076     'https://u:p@h.com/p/a/t/h?s#hash2'],
1077    ['http://example.com/b//c//d;p?q#blarg',
1078     'https:/a/b/c/d',
1079     'https://a/b/c/d'],
1080    ['http://example.com/b//c//d;p?q#blarg',
1081     'http:#hash2',
1082     'http://example.com/b//c//d;p?q#hash2'],
1083    ['http://example.com/b//c//d;p?q#blarg',
1084     'http:/p/a/t/h?s#hash2',
1085     'http://example.com/p/a/t/h?s#hash2'],
1086    ['http://example.com/b//c//d;p?q#blarg',
1087     'http://u:p@h.com/p/a/t/h?s#hash2',
1088     'http://u:p@h.com/p/a/t/h?s#hash2'],
1089    ['http://example.com/b//c//d;p?q#blarg',
1090     'http:/a/b/c/d',
1091     'http://example.com/a/b/c/d'],
1092    ['/foo/bar/baz', '/../etc/passwd', '/etc/passwd']
1093  ];
1094  relativeTests.forEach(function(relativeTest) {
1095    var a = url.resolve(relativeTest[0], relativeTest[1]),
1096        e = relativeTest[2];
1097    assert.equal(a, e,
1098                 'resolve(' + [relativeTest[0], relativeTest[1]] + ') == ' + e +
1099                 '\n  actual=' + a);
1100  });
1101  
1102  
1103  // https://github.com/joyent/node/issues/568
1104  [
1105    undefined,
1106    null,
1107    true,
1108    false,
1109    0.0,
1110    0,
1111    [],
1112    {}
1113  ].forEach(function(val) {
1114    assert.throws(function() { url.parse(val); }, TypeError);
1115  });
1116  
1117  
1118  //
1119  // Tests below taken from Chiron
1120  // http://code.google.com/p/chironjs/source/browse/trunk/src/test/http/url.js
1121  //
1122  // Copyright (c) 2002-2008 Kris Kowal <http://cixar.com/~kris.kowal>
1123  // used with permission under MIT License
1124  //
1125  // Changes marked with @isaacs
1126  
1127  var bases = [
1128    'http://a/b/c/d;p?q',
1129    'http://a/b/c/d;p?q=1/2',
1130    'http://a/b/c/d;p=1/2?q',
1131    'fred:///s//a/b/c',
1132    'http:///s//a/b/c'
1133  ];
1134  
1135  //[to, from, result]
1136  var relativeTests2 = [
1137    // http://lists.w3.org/Archives/Public/uri/2004Feb/0114.html
1138    ['../c', 'foo:a/b', 'foo:c'],
1139    ['foo:.', 'foo:a', 'foo:'],
1140    ['/foo/../../../bar', 'zz:abc', 'zz:/bar'],
1141    ['/foo/../bar', 'zz:abc', 'zz:/bar'],
1142    // @isaacs Disagree. Not how web browsers resolve this.
1143    ['foo/../../../bar', 'zz:abc', 'zz:bar'],
1144    // ['foo/../../../bar',  'zz:abc', 'zz:../../bar'], // @isaacs Added
1145    ['foo/../bar', 'zz:abc', 'zz:bar'],
1146    ['zz:.', 'zz:abc', 'zz:'],
1147    ['/.', bases[0], 'http://a/'],
1148    ['/.foo', bases[0], 'http://a/.foo'],
1149    ['.foo', bases[0], 'http://a/b/c/.foo'],
1150  
1151    // http://gbiv.com/protocols/uri/test/rel_examples1.html
1152    // examples from RFC 2396
1153    ['g:h', bases[0], 'g:h'],
1154    ['g', bases[0], 'http://a/b/c/g'],
1155    ['./g', bases[0], 'http://a/b/c/g'],
1156    ['g/', bases[0], 'http://a/b/c/g/'],
1157    ['/g', bases[0], 'http://a/g'],
1158    ['//g', bases[0], 'http://g/'],
1159    // changed with RFC 2396bis
1160    //('?y', bases[0], 'http://a/b/c/d;p?y'],
1161    ['?y', bases[0], 'http://a/b/c/d;p?y'],
1162    ['g?y', bases[0], 'http://a/b/c/g?y'],
1163    // changed with RFC 2396bis
1164    //('#s', bases[0], CURRENT_DOC_URI + '#s'],
1165    ['#s', bases[0], 'http://a/b/c/d;p?q#s'],
1166    ['g#s', bases[0], 'http://a/b/c/g#s'],
1167    ['g?y#s', bases[0], 'http://a/b/c/g?y#s'],
1168    [';x', bases[0], 'http://a/b/c/;x'],
1169    ['g;x', bases[0], 'http://a/b/c/g;x'],
1170    ['g;x?y#s', bases[0], 'http://a/b/c/g;x?y#s'],
1171    // changed with RFC 2396bis
1172    //('', bases[0], CURRENT_DOC_URI],
1173    ['', bases[0], 'http://a/b/c/d;p?q'],
1174    ['.', bases[0], 'http://a/b/c/'],
1175    ['./', bases[0], 'http://a/b/c/'],
1176    ['..', bases[0], 'http://a/b/'],
1177    ['../', bases[0], 'http://a/b/'],
1178    ['../g', bases[0], 'http://a/b/g'],
1179    ['../..', bases[0], 'http://a/'],
1180    ['../../', bases[0], 'http://a/'],
1181    ['../../g', bases[0], 'http://a/g'],
1182    ['../../../g', bases[0], ('http://a/../g', 'http://a/g')],
1183    ['../../../../g', bases[0], ('http://a/../../g', 'http://a/g')],
1184    // changed with RFC 2396bis
1185    //('/./g', bases[0], 'http://a/./g'],
1186    ['/./g', bases[0], 'http://a/g'],
1187    // changed with RFC 2396bis
1188    //('/../g', bases[0], 'http://a/../g'],
1189    ['/../g', bases[0], 'http://a/g'],
1190    ['g.', bases[0], 'http://a/b/c/g.'],
1191    ['.g', bases[0], 'http://a/b/c/.g'],
1192    ['g..', bases[0], 'http://a/b/c/g..'],
1193    ['..g', bases[0], 'http://a/b/c/..g'],
1194    ['./../g', bases[0], 'http://a/b/g'],
1195    ['./g/.', bases[0], 'http://a/b/c/g/'],
1196    ['g/./h', bases[0], 'http://a/b/c/g/h'],
1197    ['g/../h', bases[0], 'http://a/b/c/h'],
1198    ['g;x=1/./y', bases[0], 'http://a/b/c/g;x=1/y'],
1199    ['g;x=1/../y', bases[0], 'http://a/b/c/y'],
1200    ['g?y/./x', bases[0], 'http://a/b/c/g?y/./x'],
1201    ['g?y/../x', bases[0], 'http://a/b/c/g?y/../x'],
1202    ['g#s/./x', bases[0], 'http://a/b/c/g#s/./x'],
1203    ['g#s/../x', bases[0], 'http://a/b/c/g#s/../x'],
1204    ['http:g', bases[0], ('http:g', 'http://a/b/c/g')],
1205    ['http:', bases[0], ('http:', bases[0])],
1206    // not sure where this one originated
1207    ['/a/b/c/./../../g', bases[0], 'http://a/a/g'],
1208  
1209    // http://gbiv.com/protocols/uri/test/rel_examples2.html
1210    // slashes in base URI's query args
1211    ['g', bases[1], 'http://a/b/c/g'],
1212    ['./g', bases[1], 'http://a/b/c/g'],
1213    ['g/', bases[1], 'http://a/b/c/g/'],
1214    ['/g', bases[1], 'http://a/g'],
1215    ['//g', bases[1], 'http://g/'],
1216    // changed in RFC 2396bis
1217    //('?y', bases[1], 'http://a/b/c/?y'],
1218    ['?y', bases[1], 'http://a/b/c/d;p?y'],
1219    ['g?y', bases[1], 'http://a/b/c/g?y'],
1220    ['g?y/./x', bases[1], 'http://a/b/c/g?y/./x'],
1221    ['g?y/../x', bases[1], 'http://a/b/c/g?y/../x'],
1222    ['g#s', bases[1], 'http://a/b/c/g#s'],
1223    ['g#s/./x', bases[1], 'http://a/b/c/g#s/./x'],
1224    ['g#s/../x', bases[1], 'http://a/b/c/g#s/../x'],
1225    ['./', bases[1], 'http://a/b/c/'],
1226    ['../', bases[1], 'http://a/b/'],
1227    ['../g', bases[1], 'http://a/b/g'],
1228    ['../../', bases[1], 'http://a/'],
1229    ['../../g', bases[1], 'http://a/g'],
1230  
1231    // http://gbiv.com/protocols/uri/test/rel_examples3.html
1232    // slashes in path params
1233    // all of these changed in RFC 2396bis
1234    ['g', bases[2], 'http://a/b/c/d;p=1/g'],
1235    ['./g', bases[2], 'http://a/b/c/d;p=1/g'],
1236    ['g/', bases[2], 'http://a/b/c/d;p=1/g/'],
1237    ['g?y', bases[2], 'http://a/b/c/d;p=1/g?y'],
1238    [';x', bases[2], 'http://a/b/c/d;p=1/;x'],
1239    ['g;x', bases[2], 'http://a/b/c/d;p=1/g;x'],
1240    ['g;x=1/./y', bases[2], 'http://a/b/c/d;p=1/g;x=1/y'],
1241    ['g;x=1/../y', bases[2], 'http://a/b/c/d;p=1/y'],
1242    ['./', bases[2], 'http://a/b/c/d;p=1/'],
1243    ['../', bases[2], 'http://a/b/c/'],
1244    ['../g', bases[2], 'http://a/b/c/g'],
1245    ['../../', bases[2], 'http://a/b/'],
1246    ['../../g', bases[2], 'http://a/b/g'],
1247  
1248    // http://gbiv.com/protocols/uri/test/rel_examples4.html
1249    // double and triple slash, unknown scheme
1250    ['g:h', bases[3], 'g:h'],
1251    ['g', bases[3], 'fred:///s//a/b/g'],
1252    ['./g', bases[3], 'fred:///s//a/b/g'],
1253    ['g/', bases[3], 'fred:///s//a/b/g/'],
1254    ['/g', bases[3], 'fred:///g'],  // may change to fred:///s//a/g
1255    ['//g', bases[3], 'fred://g'],   // may change to fred:///s//g
1256    ['//g/x', bases[3], 'fred://g/x'], // may change to fred:///s//g/x
1257    ['///g', bases[3], 'fred:///g'],
1258    ['./', bases[3], 'fred:///s//a/b/'],
1259    ['../', bases[3], 'fred:///s//a/'],
1260    ['../g', bases[3], 'fred:///s//a/g'],
1261  
1262    ['../../', bases[3], 'fred:///s//'],
1263    ['../../g', bases[3], 'fred:///s//g'],
1264    ['../../../g', bases[3], 'fred:///s/g'],
1265    // may change to fred:///s//a/../../../g
1266    ['../../../../g', bases[3], 'fred:///g'],
1267  
1268    // http://gbiv.com/protocols/uri/test/rel_examples5.html
1269    // double and triple slash, well-known scheme
1270    ['g:h', bases[4], 'g:h'],
1271    ['g', bases[4], 'http:///s//a/b/g'],
1272    ['./g', bases[4], 'http:///s//a/b/g'],
1273    ['g/', bases[4], 'http:///s//a/b/g/'],
1274    ['/g', bases[4], 'http:///g'],  // may change to http:///s//a/g
1275    ['//g', bases[4], 'http://g/'],   // may change to http:///s//g
1276    ['//g/x', bases[4], 'http://g/x'], // may change to http:///s//g/x
1277    ['///g', bases[4], 'http:///g'],
1278    ['./', bases[4], 'http:///s//a/b/'],
1279    ['../', bases[4], 'http:///s//a/'],
1280    ['../g', bases[4], 'http:///s//a/g'],
1281    ['../../', bases[4], 'http:///s//'],
1282    ['../../g', bases[4], 'http:///s//g'],
1283    // may change to http:///s//a/../../g
1284    ['../../../g', bases[4], 'http:///s/g'],
1285    // may change to http:///s//a/../../../g
1286    ['../../../../g', bases[4], 'http:///g'],
1287  
1288    // from Dan Connelly's tests in http://www.w3.org/2000/10/swap/uripath.py
1289    ['bar:abc', 'foo:xyz', 'bar:abc'],
1290    ['../abc', 'http://example/x/y/z', 'http://example/x/abc'],
1291    ['http://example/x/abc', 'http://example2/x/y/z', 'http://example/x/abc'],
1292    ['../r', 'http://ex/x/y/z', 'http://ex/x/r'],
1293    ['q/r', 'http://ex/x/y', 'http://ex/x/q/r'],
1294    ['q/r#s', 'http://ex/x/y', 'http://ex/x/q/r#s'],
1295    ['q/r#s/t', 'http://ex/x/y', 'http://ex/x/q/r#s/t'],
1296    ['ftp://ex/x/q/r', 'http://ex/x/y', 'ftp://ex/x/q/r'],
1297    ['', 'http://ex/x/y', 'http://ex/x/y'],
1298    ['', 'http://ex/x/y/', 'http://ex/x/y/'],
1299    ['', 'http://ex/x/y/pdq', 'http://ex/x/y/pdq'],
1300    ['z/', 'http://ex/x/y/', 'http://ex/x/y/z/'],
1301    ['#Animal',
1302     'file:/swap/test/animal.rdf',
1303     'file:/swap/test/animal.rdf#Animal'],
1304    ['../abc', 'file:/e/x/y/z', 'file:/e/x/abc'],
1305    ['/example/x/abc', 'file:/example2/x/y/z', 'file:/example/x/abc'],
1306    ['../r', 'file:/ex/x/y/z', 'file:/ex/x/r'],
1307    ['/r', 'file:/ex/x/y/z', 'file:/r'],
1308    ['q/r', 'file:/ex/x/y', 'file:/ex/x/q/r'],
1309    ['q/r#s', 'file:/ex/x/y', 'file:/ex/x/q/r#s'],
1310    ['q/r#', 'file:/ex/x/y', 'file:/ex/x/q/r#'],
1311    ['q/r#s/t', 'file:/ex/x/y', 'file:/ex/x/q/r#s/t'],
1312    ['ftp://ex/x/q/r', 'file:/ex/x/y', 'ftp://ex/x/q/r'],
1313    ['', 'file:/ex/x/y', 'file:/ex/x/y'],
1314    ['', 'file:/ex/x/y/', 'file:/ex/x/y/'],
1315    ['', 'file:/ex/x/y/pdq', 'file:/ex/x/y/pdq'],
1316    ['z/', 'file:/ex/x/y/', 'file:/ex/x/y/z/'],
1317    ['file://meetings.example.com/cal#m1',
1318     'file:/devel/WWW/2000/10/swap/test/reluri-1.n3',
1319     'file://meetings.example.com/cal#m1'],
1320    ['file://meetings.example.com/cal#m1',
1321     'file:/home/connolly/w3ccvs/WWW/2000/10/swap/test/reluri-1.n3',
1322     'file://meetings.example.com/cal#m1'],
1323    ['./#blort', 'file:/some/dir/foo', 'file:/some/dir/#blort'],
1324    ['./#', 'file:/some/dir/foo', 'file:/some/dir/#'],
1325    // Ryan Lee
1326    ['./', 'http://example/x/abc.efg', 'http://example/x/'],
1327  
1328  
1329    // Graham Klyne's tests
1330    // http://www.ninebynine.org/Software/HaskellUtils/Network/UriTest.xls
1331    // 01-31 are from Connelly's cases
1332  
1333    // 32-49
1334    ['./q:r', 'http://ex/x/y', 'http://ex/x/q:r'],
1335    ['./p=q:r', 'http://ex/x/y', 'http://ex/x/p=q:r'],
1336    ['?pp/rr', 'http://ex/x/y?pp/qq', 'http://ex/x/y?pp/rr'],
1337    ['y/z', 'http://ex/x/y?pp/qq', 'http://ex/x/y/z'],
1338    ['local/qual@domain.org#frag',
1339     'mailto:local',
1340     'mailto:local/qual@domain.org#frag'],
1341    ['more/qual2@domain2.org#frag',
1342     'mailto:local/qual1@domain1.org',
1343     'mailto:local/more/qual2@domain2.org#frag'],
1344    ['y?q', 'http://ex/x/y?q', 'http://ex/x/y?q'],
1345    ['/x/y?q', 'http://ex?p', 'http://ex/x/y?q'],
1346    ['c/d', 'foo:a/b', 'foo:a/c/d'],
1347    ['/c/d', 'foo:a/b', 'foo:/c/d'],
1348    ['', 'foo:a/b?c#d', 'foo:a/b?c'],
1349    ['b/c', 'foo:a', 'foo:b/c'],
1350    ['../b/c', 'foo:/a/y/z', 'foo:/a/b/c'],
1351    ['./b/c', 'foo:a', 'foo:b/c'],
1352    ['/./b/c', 'foo:a', 'foo:/b/c'],
1353    ['../../d', 'foo://a//b/c', 'foo://a/d'],
1354    ['.', 'foo:a', 'foo:'],
1355    ['..', 'foo:a', 'foo:'],
1356  
1357    // 50-57[cf. TimBL comments --
1358    //  http://lists.w3.org/Archives/Public/uri/2003Feb/0028.html,
1359    //  http://lists.w3.org/Archives/Public/uri/2003Jan/0008.html)
1360    ['abc', 'http://example/x/y%2Fz', 'http://example/x/abc'],
1361    ['../../x%2Fabc', 'http://example/a/x/y/z', 'http://example/a/x%2Fabc'],
1362    ['../x%2Fabc', 'http://example/a/x/y%2Fz', 'http://example/a/x%2Fabc'],
1363    ['abc', 'http://example/x%2Fy/z', 'http://example/x%2Fy/abc'],
1364    ['q%3Ar', 'http://ex/x/y', 'http://ex/x/q%3Ar'],
1365    ['/x%2Fabc', 'http://example/x/y%2Fz', 'http://example/x%2Fabc'],
1366    ['/x%2Fabc', 'http://example/x/y/z', 'http://example/x%2Fabc'],
1367    ['/x%2Fabc', 'http://example/x/y%2Fz', 'http://example/x%2Fabc'],
1368  
1369    // 70-77
1370    ['local2@domain2', 'mailto:local1@domain1?query1', 'mailto:local2@domain2'],
1371    ['local2@domain2?query2',
1372     'mailto:local1@domain1',
1373     'mailto:local2@domain2?query2'],
1374    ['local2@domain2?query2',
1375     'mailto:local1@domain1?query1',
1376     'mailto:local2@domain2?query2'],
1377    ['?query2', 'mailto:local@domain?query1', 'mailto:local@domain?query2'],
1378    ['local@domain?query2', 'mailto:?query1', 'mailto:local@domain?query2'],
1379    ['?query2', 'mailto:local@domain?query1', 'mailto:local@domain?query2'],
1380    ['http://example/a/b?c/../d', 'foo:bar', 'http://example/a/b?c/../d'],
1381    ['http://example/a/b#c/../d', 'foo:bar', 'http://example/a/b#c/../d'],
1382  
1383    // 82-88
1384    // @isaacs Disagree. Not how browsers do it.
1385    // ['http:this', 'http://example.org/base/uri', 'http:this'],
1386    // @isaacs Added
1387    ['http:this', 'http://example.org/base/uri', 'http://example.org/base/this'],
1388    ['http:this', 'http:base', 'http:this'],
1389    ['.//g', 'f:/a', 'f://g'],
1390    ['b/c//d/e', 'f://example.org/base/a', 'f://example.org/base/b/c//d/e'],
1391    ['m2@example.ord/c2@example.org',
1392     'mid:m@example.ord/c@example.org',
1393     'mid:m@example.ord/m2@example.ord/c2@example.org'],
1394    ['mini1.xml',
1395     'file:///C:/DEV/Haskell/lib/HXmlToolbox-3.01/examples/',
1396     'file:///C:/DEV/Haskell/lib/HXmlToolbox-3.01/examples/mini1.xml'],
1397    ['../b/c', 'foo:a/y/z', 'foo:a/b/c'],
1398  
1399    //changeing auth
1400    ['http://diff:auth@www.example.com',
1401     'http://asdf:qwer@www.example.com',
1402     'http://diff:auth@www.example.com/']
1403  ];
1404  relativeTests2.forEach(function(relativeTest) {
1405    var a = url.resolve(relativeTest[1], relativeTest[0]),
1406        e = relativeTest[2];
1407    assert.equal(a, e,
1408                 'resolve(' + [relativeTest[1], relativeTest[0]] + ') == ' + e +
1409                 '\n  actual=' + a);
1410  });
1411  
1412  //if format and parse are inverse operations then
1413  //resolveObject(parse(x), y) == parse(resolve(x, y))
1414  
1415  //host and hostname are special, in this case a '' value is important
1416  var emptyIsImportant = {'host': true, 'hostname': ''};
1417  
1418  //format: [from, path, expected]
1419  relativeTests.forEach(function(relativeTest) {
1420    var actual = url.resolveObject(url.parse(relativeTest[0]), relativeTest[1]),
1421        expected = url.parse(relativeTest[2]);
1422  
1423  
1424    assert.deepEqual(actual, expected);
1425  
1426    expected = relativeTest[2];
1427    actual = url.format(actual);
1428  
1429    assert.equal(actual, expected,
1430                 'format(' + actual + ') == ' + expected + '\nactual:' + actual);
1431  });
1432  
1433  //format: [to, from, result]
1434  // the test: ['.//g', 'f:/a', 'f://g'] is a fundimental problem
1435  // url.parse('f:/a') does not have a host
1436  // url.resolve('f:/a', './/g') does not have a host becuase you have moved
1437  // down to the g directory.  i.e. f:     //g, however when this url is parsed
1438  // f:// will indicate that the host is g which is not the case.
1439  // it is unclear to me how to keep this information from being lost
1440  // it may be that a pathname of ////g should colapse to /g but this seems
1441  // to be a lot of work for an edge case.  Right now I remove the test
1442  if (relativeTests2[181][0] === './/g' &&
1443      relativeTests2[181][1] === 'f:/a' &&
1444      relativeTests2[181][2] === 'f://g') {
1445    relativeTests2.splice(181, 1);
1446  }
1447  relativeTests2.forEach(function(relativeTest) {
1448    var actual = url.resolveObject(url.parse(relativeTest[1]), relativeTest[0]),
1449        expected = url.parse(relativeTest[2]);
1450  
1451    assert.deepEqual(actual, expected);
1452  
1453    var expected = relativeTest[2],
1454        actual = url.format(actual);
1455  
1456    assert.equal(actual, expected,
1457                 'format(' + relativeTest[1] + ') == ' + expected +
1458                 '\nactual:' + actual);
1459  });
1460  
1461  });