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