Skip to content
File

Blob: src/workerd/api/node/tests/legacy_url-nodejs-test.js

javascript2258 lines
1// Copyright (c) 2024 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4import url from 'node:url';
5import assert from 'node:assert';
6import { inspect } from 'node:util';
7 
8// Ref: https://github.com/nodejs/node/blob/e92446536ed4e268c9eef6ae6f911e384c98eecf/test/parallel/test-url-format.js
9export const format = {
10 async test() {
11 const formatTests = {
12 'http://example.com?': {
13 href: 'http://example.com/?',
14 protocol: 'http:',
15 slashes: true,
16 host: 'example.com',
17 hostname: 'example.com',
18 search: '?',
19 query: {},
20 pathname: '/',
21 },
22 'http://example.com?foo=bar#frag': {
23 href: 'http://example.com/?foo=bar#frag',
24 protocol: 'http:',
25 host: 'example.com',
26 hostname: 'example.com',
27 hash: '#frag',
28 search: '?foo=bar',
29 query: 'foo=bar',
30 pathname: '/',
31 },
32 'http://example.com?foo=@bar#frag': {
33 href: 'http://example.com/?foo=@bar#frag',
34 protocol: 'http:',
35 host: 'example.com',
36 hostname: 'example.com',
37 hash: '#frag',
38 search: '?foo=@bar',
39 query: 'foo=@bar',
40 pathname: '/',
41 },
42 'http://example.com?foo=/bar/#frag': {
43 href: 'http://example.com/?foo=/bar/#frag',
44 protocol: 'http:',
45 host: 'example.com',
46 hostname: 'example.com',
47 hash: '#frag',
48 search: '?foo=/bar/',
49 query: 'foo=/bar/',
50 pathname: '/',
51 },
52 'http://example.com?foo=?bar/#frag': {
53 href: 'http://example.com/?foo=?bar/#frag',
54 protocol: 'http:',
55 host: 'example.com',
56 hostname: 'example.com',
57 hash: '#frag',
58 search: '?foo=?bar/',
59 query: 'foo=?bar/',
60 pathname: '/',
61 },
62 'http://example.com#frag=?bar/#frag': {
63 href: 'http://example.com/#frag=?bar/#frag',
64 protocol: 'http:',
65 host: 'example.com',
66 hostname: 'example.com',
67 hash: '#frag=?bar/#frag',
68 pathname: '/',
69 },
70 'http://google.com" onload="alert(42)/': {
71 href: 'http://google.com/%22%20onload=%22alert(42)/',
72 protocol: 'http:',
73 host: 'google.com',
74 pathname: '/%22%20onload=%22alert(42)/',
75 },
76 'http://a.com/a/b/c?s#h': {
77 href: 'http://a.com/a/b/c?s#h',
78 protocol: 'http',
79 host: 'a.com',
80 pathname: 'a/b/c',
81 hash: 'h',
82 search: 's',
83 },
84 'xmpp:isaacschlueter@jabber.org': {
85 href: 'xmpp:isaacschlueter@jabber.org',
86 protocol: 'xmpp:',
87 host: 'jabber.org',
88 auth: 'isaacschlueter',
89 hostname: 'jabber.org',
90 },
91 'http://atpass:foo%40bar@127.0.0.1/': {
92 href: 'http://atpass:foo%40bar@127.0.0.1/',
93 auth: 'atpass:foo@bar',
94 hostname: '127.0.0.1',
95 protocol: 'http:',
96 pathname: '/',
97 },
98 'http://atslash%2F%40:%2F%40@foo/': {
99 href: 'http://atslash%2F%40:%2F%40@foo/',
100 auth: 'atslash/@:/@',
101 hostname: 'foo',
102 protocol: 'http:',
103 pathname: '/',
104 },
105 'svn+ssh://foo/bar': {
106 href: 'svn+ssh://foo/bar',
107 hostname: 'foo',
108 protocol: 'svn+ssh:',
109 pathname: '/bar',
110 slashes: true,
111 },
112 'dash-test://foo/bar': {
113 href: 'dash-test://foo/bar',
114 hostname: 'foo',
115 protocol: 'dash-test:',
116 pathname: '/bar',
117 slashes: true,
118 },
119 'dash-test:foo/bar': {
120 href: 'dash-test:foo/bar',
121 hostname: 'foo',
122 protocol: 'dash-test:',
123 pathname: '/bar',
124 },
125 'dot.test://foo/bar': {
126 href: 'dot.test://foo/bar',
127 hostname: 'foo',
128 protocol: 'dot.test:',
129 pathname: '/bar',
130 slashes: true,
131 },
132 'dot.test:foo/bar': {
133 href: 'dot.test:foo/bar',
134 hostname: 'foo',
135 protocol: 'dot.test:',
136 pathname: '/bar',
137 },
138 // IPv6 support
139 'coap:u:p@[::1]:61616/.well-known/r?n=Temperature': {
140 href: 'coap:u:p@[::1]:61616/.well-known/r?n=Temperature',
141 protocol: 'coap:',
142 auth: 'u:p',
143 hostname: '::1',
144 port: '61616',
145 pathname: '/.well-known/r',
146 search: 'n=Temperature',
147 },
148 'coap:[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616/s/stopButton': {
149 href: 'coap:[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616/s/stopButton',
150 protocol: 'coap',
151 host: '[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616',
152 pathname: '/s/stopButton',
153 },
154 'http://[::]/': {
155 href: 'http://[::]/',
156 protocol: 'http:',
157 hostname: '[::]',
158 pathname: '/',
159 },
160 
161 // Encode context-specific delimiters in path and query, but do not touch
162 // other non-delimiter chars like `%`.
163 // <https://github.com/nodejs/node-v0.x-archive/issues/4082>
164 
165 // `#`,`?` in path
166 '/path/to/%%23%3F+=&.txt?foo=theA1#bar': {
167 href: '/path/to/%%23%3F+=&.txt?foo=theA1#bar',
168 pathname: '/path/to/%#?+=&.txt',
169 query: {
170 foo: 'theA1',
171 },
172 hash: '#bar',
173 },
174 
175 // `#`,`?` in path + `#` in query
176 '/path/to/%%23%3F+=&.txt?foo=the%231#bar': {
177 href: '/path/to/%%23%3F+=&.txt?foo=the%231#bar',
178 pathname: '/path/to/%#?+=&.txt',
179 query: {
180 foo: 'the#1',
181 },
182 hash: '#bar',
183 },
184 
185 // `#` in path end + `#` in query
186 '/path/to/%%23?foo=the%231#bar': {
187 href: '/path/to/%%23?foo=the%231#bar',
188 pathname: '/path/to/%#',
189 query: {
190 foo: 'the#1',
191 },
192 hash: '#bar',
193 },
194 
195 // `?` and `#` in path and search
196 'http://ex.com/foo%3F100%m%23r?abc=the%231?&foo=bar#frag': {
197 href: 'http://ex.com/foo%3F100%m%23r?abc=the%231?&foo=bar#frag',
198 protocol: 'http:',
199 hostname: 'ex.com',
200 hash: '#frag',
201 search: '?abc=the#1?&foo=bar',
202 pathname: '/foo?100%m#r',
203 },
204 
205 // `?` and `#` in search only
206 'http://ex.com/fooA100%mBr?abc=the%231?&foo=bar#frag': {
207 href: 'http://ex.com/fooA100%mBr?abc=the%231?&foo=bar#frag',
208 protocol: 'http:',
209 hostname: 'ex.com',
210 hash: '#frag',
211 search: '?abc=the#1?&foo=bar',
212 pathname: '/fooA100%mBr',
213 },
214 
215 // Multiple `#` in search
216 'http://example.com/?foo=bar%231%232%233&abc=%234%23%235#frag': {
217 href: 'http://example.com/?foo=bar%231%232%233&abc=%234%23%235#frag',
218 protocol: 'http:',
219 slashes: true,
220 host: 'example.com',
221 hostname: 'example.com',
222 hash: '#frag',
223 search: '?foo=bar#1#2#3&abc=#4##5',
224 query: {},
225 pathname: '/',
226 },
227 
228 // More than 255 characters in hostname which exceeds the limit
229 [`http://${'a'.repeat(255)}.com/node`]: {
230 href: 'http:///node',
231 protocol: 'http:',
232 slashes: true,
233 host: '',
234 hostname: '',
235 pathname: '/node',
236 path: '/node',
237 },
238 
239 // Greater than or equal to 63 characters after `.` in hostname
240 [`http://www.${'z'.repeat(63)}example.com/node`]: {
241 href: `http://www.${'z'.repeat(63)}example.com/node`,
242 protocol: 'http:',
243 slashes: true,
244 host: `www.${'z'.repeat(63)}example.com`,
245 hostname: `www.${'z'.repeat(63)}example.com`,
246 pathname: '/node',
247 path: '/node',
248 },
249 
250 // https://github.com/nodejs/node/issues/3361
251 'file:///home/user': {
252 href: 'file:///home/user',
253 protocol: 'file',
254 pathname: '/home/user',
255 path: '/home/user',
256 },
257 
258 // surrogate in auth
259 'http://%F0%9F%98%80@www.example.com/': {
260 href: 'http://%F0%9F%98%80@www.example.com/',
261 protocol: 'http:',
262 auth: '\uD83D\uDE00',
263 hostname: 'www.example.com',
264 pathname: '/',
265 },
266 };
267 
268 for (const u in formatTests) {
269 const expect = formatTests[u].href;
270 delete formatTests[u].href;
271 const actual = url.format(u);
272 const actualObj = url.format(formatTests[u]);
273 assert.strictEqual(
274 actual,
275 expect,
276 `wonky format(${u}) == ${expect}\nactual:${actual}`
277 );
278 assert.strictEqual(
279 actualObj,
280 expect,
281 `wonky format(${JSON.stringify(formatTests[u])}) == ${
282 expect
283 }\nactual: ${actualObj}`
284 );
285 }
286 },
287};
288 
289// Ref: https://github.com/nodejs/node/blob/e92446536ed4e268c9eef6ae6f911e384c98eecf/test/parallel/test-url-relative.js
290export const relative = {
291 async test() {
292 // When source is false
293 assert.strictEqual(url.resolveObject('', 'foo'), 'foo');
294 
295 // [from, path, expected]
296 const relativeTests = [
297 ['/foo/bar/baz', 'quux', '/foo/bar/quux'],
298 ['/foo/bar/baz', 'quux/asdf', '/foo/bar/quux/asdf'],
299 ['/foo/bar/baz', 'quux/baz', '/foo/bar/quux/baz'],
300 ['/foo/bar/baz', '../quux/baz', '/foo/quux/baz'],
301 ['/foo/bar/baz', '/bar', '/bar'],
302 ['/foo/bar/baz/', 'quux', '/foo/bar/baz/quux'],
303 ['/foo/bar/baz/', 'quux/baz', '/foo/bar/baz/quux/baz'],
304 ['/foo/bar/baz', '../../../../../../../../quux/baz', '/quux/baz'],
305 ['/foo/bar/baz', '../../../../../../../quux/baz', '/quux/baz'],
306 ['/foo', '.', '/'],
307 ['/foo', '..', '/'],
308 ['/foo/', '.', '/foo/'],
309 ['/foo/', '..', '/'],
310 ['/foo/bar', '.', '/foo/'],
311 ['/foo/bar', '..', '/'],
312 ['/foo/bar/', '.', '/foo/bar/'],
313 ['/foo/bar/', '..', '/foo/'],
314 ['foo/bar', '../../../baz', '../../baz'],
315 ['foo/bar/', '../../../baz', '../baz'],
316 [
317 'http://example.com/b//c//d;p?q#blarg',
318 'https:#hash2',
319 'https:///#hash2',
320 ],
321 [
322 'http://example.com/b//c//d;p?q#blarg',
323 'https:/p/a/t/h?s#hash2',
324 'https://p/a/t/h?s#hash2',
325 ],
326 [
327 'http://example.com/b//c//d;p?q#blarg',
328 'https://u:p@h.com/p/a/t/h?s#hash2',
329 'https://u:p@h.com/p/a/t/h?s#hash2',
330 ],
331 [
332 'http://example.com/b//c//d;p?q#blarg',
333 'https:/a/b/c/d',
334 'https://a/b/c/d',
335 ],
336 [
337 'http://example.com/b//c//d;p?q#blarg',
338 'http:#hash2',
339 'http://example.com/b//c//d;p?q#hash2',
340 ],
341 [
342 'http://example.com/b//c//d;p?q#blarg',
343 'http:/p/a/t/h?s#hash2',
344 'http://example.com/p/a/t/h?s#hash2',
345 ],
346 [
347 'http://example.com/b//c//d;p?q#blarg',
348 'http://u:p@h.com/p/a/t/h?s#hash2',
349 'http://u:p@h.com/p/a/t/h?s#hash2',
350 ],
351 [
352 'http://example.com/b//c//d;p?q#blarg',
353 'http:/a/b/c/d',
354 'http://example.com/a/b/c/d',
355 ],
356 ['/foo/bar/baz', '/../etc/passwd', '/etc/passwd'],
357 ['http://localhost', 'file:///Users/foo', 'file:///Users/foo'],
358 ['http://localhost', 'file://foo/Users', 'file://foo/Users'],
359 [
360 'https://registry.npmjs.org',
361 '@foo/bar',
362 'https://registry.npmjs.org/@foo/bar',
363 ],
364 ];
365 for (let i = 0; i < relativeTests.length; i++) {
366 const relativeTest = relativeTests[i];
367 
368 const a = url.resolve(relativeTest[0], relativeTest[1]);
369 const e = relativeTest[2];
370 assert.strictEqual(
371 a,
372 e,
373 `resolve(${relativeTest[0]}, ${relativeTest[1]})` +
374 ` == ${e}\n actual=${a}`
375 );
376 }
377 
378 //
379 // Tests below taken from Chiron
380 // http://code.google.com/p/chironjs/source/browse/trunk/src/test/http/url.js
381 //
382 // Copyright (c) 2002-2008 Kris Kowal <http://cixar.com/~kris.kowal>
383 // used with permission under MIT License
384 //
385 // Changes marked with @isaacs
386 
387 const bases = [
388 'http://a/b/c/d;p?q',
389 'http://a/b/c/d;p?q=1/2',
390 'http://a/b/c/d;p=1/2?q',
391 'fred:///s//a/b/c',
392 'http:///s//a/b/c',
393 ];
394 
395 // [to, from, result]
396 const relativeTests2 = [
397 // http://lists.w3.org/Archives/Public/uri/2004Feb/0114.html
398 ['../c', 'foo:a/b', 'foo:c'],
399 ['foo:.', 'foo:a', 'foo:'],
400 ['/foo/../../../bar', 'zz:abc', 'zz:/bar'],
401 ['/foo/../bar', 'zz:abc', 'zz:/bar'],
402 // @isaacs Disagree. Not how web browsers resolve this.
403 ['foo/../../../bar', 'zz:abc', 'zz:bar'],
404 // ['foo/../../../bar', 'zz:abc', 'zz:../../bar'], // @isaacs Added
405 ['foo/../bar', 'zz:abc', 'zz:bar'],
406 ['zz:.', 'zz:abc', 'zz:'],
407 ['/.', bases[0], 'http://a/'],
408 ['/.foo', bases[0], 'http://a/.foo'],
409 ['.foo', bases[0], 'http://a/b/c/.foo'],
410 
411 // http://gbiv.com/protocols/uri/test/rel_examples1.html
412 // examples from RFC 2396
413 ['g:h', bases[0], 'g:h'],
414 ['g', bases[0], 'http://a/b/c/g'],
415 ['./g', bases[0], 'http://a/b/c/g'],
416 ['g/', bases[0], 'http://a/b/c/g/'],
417 ['/g', bases[0], 'http://a/g'],
418 ['//g', bases[0], 'http://g/'],
419 // Changed with RFC 2396bis
420 // ('?y', bases[0], 'http://a/b/c/d;p?y'],
421 ['?y', bases[0], 'http://a/b/c/d;p?y'],
422 ['g?y', bases[0], 'http://a/b/c/g?y'],
423 // Changed with RFC 2396bis
424 // ('#s', bases[0], CURRENT_DOC_URI + '#s'],
425 ['#s', bases[0], 'http://a/b/c/d;p?q#s'],
426 ['g#s', bases[0], 'http://a/b/c/g#s'],
427 ['g?y#s', bases[0], 'http://a/b/c/g?y#s'],
428 [';x', bases[0], 'http://a/b/c/;x'],
429 ['g;x', bases[0], 'http://a/b/c/g;x'],
430 ['g;x?y#s', bases[0], 'http://a/b/c/g;x?y#s'],
431 // Changed with RFC 2396bis
432 // ('', bases[0], CURRENT_DOC_URI],
433 ['', bases[0], 'http://a/b/c/d;p?q'],
434 ['.', bases[0], 'http://a/b/c/'],
435 ['./', bases[0], 'http://a/b/c/'],
436 ['..', bases[0], 'http://a/b/'],
437 ['../', bases[0], 'http://a/b/'],
438 ['../g', bases[0], 'http://a/b/g'],
439 ['../..', bases[0], 'http://a/'],
440 ['../../', bases[0], 'http://a/'],
441 ['../../g', bases[0], 'http://a/g'],
442 ['../../../g', bases[0], ('http://a/../g', 'http://a/g')],
443 ['../../../../g', bases[0], ('http://a/../../g', 'http://a/g')],
444 // Changed with RFC 2396bis
445 // ('/./g', bases[0], 'http://a/./g'],
446 ['/./g', bases[0], 'http://a/g'],
447 // Changed with RFC 2396bis
448 // ('/../g', bases[0], 'http://a/../g'],
449 ['/../g', bases[0], 'http://a/g'],
450 ['g.', bases[0], 'http://a/b/c/g.'],
451 ['.g', bases[0], 'http://a/b/c/.g'],
452 ['g..', bases[0], 'http://a/b/c/g..'],
453 ['..g', bases[0], 'http://a/b/c/..g'],
454 ['./../g', bases[0], 'http://a/b/g'],
455 ['./g/.', bases[0], 'http://a/b/c/g/'],
456 ['g/./h', bases[0], 'http://a/b/c/g/h'],
457 ['g/../h', bases[0], 'http://a/b/c/h'],
458 ['g;x=1/./y', bases[0], 'http://a/b/c/g;x=1/y'],
459 ['g;x=1/../y', bases[0], 'http://a/b/c/y'],
460 ['g?y/./x', bases[0], 'http://a/b/c/g?y/./x'],
461 ['g?y/../x', bases[0], 'http://a/b/c/g?y/../x'],
462 ['g#s/./x', bases[0], 'http://a/b/c/g#s/./x'],
463 ['g#s/../x', bases[0], 'http://a/b/c/g#s/../x'],
464 ['http:g', bases[0], ('http:g', 'http://a/b/c/g')],
465 ['http:', bases[0], ('http:', bases[0])],
466 // Not sure where this one originated
467 ['/a/b/c/./../../g', bases[0], 'http://a/a/g'],
468 
469 // http://gbiv.com/protocols/uri/test/rel_examples2.html
470 // slashes in base URI's query args
471 ['g', bases[1], 'http://a/b/c/g'],
472 ['./g', bases[1], 'http://a/b/c/g'],
473 ['g/', bases[1], 'http://a/b/c/g/'],
474 ['/g', bases[1], 'http://a/g'],
475 ['//g', bases[1], 'http://g/'],
476 // Changed in RFC 2396bis
477 // ('?y', bases[1], 'http://a/b/c/?y'],
478 ['?y', bases[1], 'http://a/b/c/d;p?y'],
479 ['g?y', bases[1], 'http://a/b/c/g?y'],
480 ['g?y/./x', bases[1], 'http://a/b/c/g?y/./x'],
481 ['g?y/../x', bases[1], 'http://a/b/c/g?y/../x'],
482 ['g#s', bases[1], 'http://a/b/c/g#s'],
483 ['g#s/./x', bases[1], 'http://a/b/c/g#s/./x'],
484 ['g#s/../x', bases[1], 'http://a/b/c/g#s/../x'],
485 ['./', bases[1], 'http://a/b/c/'],
486 ['../', bases[1], 'http://a/b/'],
487 ['../g', bases[1], 'http://a/b/g'],
488 ['../../', bases[1], 'http://a/'],
489 ['../../g', bases[1], 'http://a/g'],
490 
491 // http://gbiv.com/protocols/uri/test/rel_examples3.html
492 // slashes in path params
493 // all of these changed in RFC 2396bis
494 ['g', bases[2], 'http://a/b/c/d;p=1/g'],
495 ['./g', bases[2], 'http://a/b/c/d;p=1/g'],
496 ['g/', bases[2], 'http://a/b/c/d;p=1/g/'],
497 ['g?y', bases[2], 'http://a/b/c/d;p=1/g?y'],
498 [';x', bases[2], 'http://a/b/c/d;p=1/;x'],
499 ['g;x', bases[2], 'http://a/b/c/d;p=1/g;x'],
500 ['g;x=1/./y', bases[2], 'http://a/b/c/d;p=1/g;x=1/y'],
501 ['g;x=1/../y', bases[2], 'http://a/b/c/d;p=1/y'],
502 ['./', bases[2], 'http://a/b/c/d;p=1/'],
503 ['../', bases[2], 'http://a/b/c/'],
504 ['../g', bases[2], 'http://a/b/c/g'],
505 ['../../', bases[2], 'http://a/b/'],
506 ['../../g', bases[2], 'http://a/b/g'],
507 
508 // http://gbiv.com/protocols/uri/test/rel_examples4.html
509 // double and triple slash, unknown scheme
510 ['g:h', bases[3], 'g:h'],
511 ['g', bases[3], 'fred:///s//a/b/g'],
512 ['./g', bases[3], 'fred:///s//a/b/g'],
513 ['g/', bases[3], 'fred:///s//a/b/g/'],
514 ['/g', bases[3], 'fred:///g'], // May change to fred:///s//a/g
515 ['//g', bases[3], 'fred://g'], // May change to fred:///s//g
516 ['//g/x', bases[3], 'fred://g/x'], // May change to fred:///s//g/x
517 ['///g', bases[3], 'fred:///g'],
518 ['./', bases[3], 'fred:///s//a/b/'],
519 ['../', bases[3], 'fred:///s//a/'],
520 ['../g', bases[3], 'fred:///s//a/g'],
521 
522 ['../../', bases[3], 'fred:///s//'],
523 ['../../g', bases[3], 'fred:///s//g'],
524 ['../../../g', bases[3], 'fred:///s/g'],
525 // May change to fred:///s//a/../../../g
526 ['../../../../g', bases[3], 'fred:///g'],
527 
528 // http://gbiv.com/protocols/uri/test/rel_examples5.html
529 // double and triple slash, well-known scheme
530 ['g:h', bases[4], 'g:h'],
531 ['g', bases[4], 'http:///s//a/b/g'],
532 ['./g', bases[4], 'http:///s//a/b/g'],
533 ['g/', bases[4], 'http:///s//a/b/g/'],
534 ['/g', bases[4], 'http:///g'], // May change to http:///s//a/g
535 ['//g', bases[4], 'http://g/'], // May change to http:///s//g
536 ['//g/x', bases[4], 'http://g/x'], // May change to http:///s//g/x
537 ['///g', bases[4], 'http:///g'],
538 ['./', bases[4], 'http:///s//a/b/'],
539 ['../', bases[4], 'http:///s//a/'],
540 ['../g', bases[4], 'http:///s//a/g'],
541 ['../../', bases[4], 'http:///s//'],
542 ['../../g', bases[4], 'http:///s//g'],
543 // May change to http:///s//a/../../g
544 ['../../../g', bases[4], 'http:///s/g'],
545 // May change to http:///s//a/../../../g
546 ['../../../../g', bases[4], 'http:///g'],
547 
548 // From Dan Connelly's tests in http://www.w3.org/2000/10/swap/uripath.py
549 ['bar:abc', 'foo:xyz', 'bar:abc'],
550 ['../abc', 'http://example/x/y/z', 'http://example/x/abc'],
551 ['http://example/x/abc', 'http://example2/x/y/z', 'http://example/x/abc'],
552 ['../r', 'http://ex/x/y/z', 'http://ex/x/r'],
553 ['q/r', 'http://ex/x/y', 'http://ex/x/q/r'],
554 ['q/r#s', 'http://ex/x/y', 'http://ex/x/q/r#s'],
555 ['q/r#s/t', 'http://ex/x/y', 'http://ex/x/q/r#s/t'],
556 ['ftp://ex/x/q/r', 'http://ex/x/y', 'ftp://ex/x/q/r'],
557 ['', 'http://ex/x/y', 'http://ex/x/y'],
558 ['', 'http://ex/x/y/', 'http://ex/x/y/'],
559 ['', 'http://ex/x/y/pdq', 'http://ex/x/y/pdq'],
560 ['z/', 'http://ex/x/y/', 'http://ex/x/y/z/'],
561 [
562 '#Animal',
563 'file:/swap/test/animal.rdf',
564 'file:/swap/test/animal.rdf#Animal',
565 ],
566 ['../abc', 'file:/e/x/y/z', 'file:/e/x/abc'],
567 ['/example/x/abc', 'file:/example2/x/y/z', 'file:/example/x/abc'],
568 ['../r', 'file:/ex/x/y/z', 'file:/ex/x/r'],
569 ['/r', 'file:/ex/x/y/z', 'file:/r'],
570 ['q/r', 'file:/ex/x/y', 'file:/ex/x/q/r'],
571 ['q/r#s', 'file:/ex/x/y', 'file:/ex/x/q/r#s'],
572 ['q/r#', 'file:/ex/x/y', 'file:/ex/x/q/r#'],
573 ['q/r#s/t', 'file:/ex/x/y', 'file:/ex/x/q/r#s/t'],
574 ['ftp://ex/x/q/r', 'file:/ex/x/y', 'ftp://ex/x/q/r'],
575 ['', 'file:/ex/x/y', 'file:/ex/x/y'],
576 ['', 'file:/ex/x/y/', 'file:/ex/x/y/'],
577 ['', 'file:/ex/x/y/pdq', 'file:/ex/x/y/pdq'],
578 ['z/', 'file:/ex/x/y/', 'file:/ex/x/y/z/'],
579 [
580 'file://meetings.example.com/cal#m1',
581 'file:/devel/WWW/2000/10/swap/test/reluri-1.n3',
582 'file://meetings.example.com/cal#m1',
583 ],
584 [
585 'file://meetings.example.com/cal#m1',
586 'file:/home/connolly/w3ccvs/WWW/2000/10/swap/test/reluri-1.n3',
587 'file://meetings.example.com/cal#m1',
588 ],
589 ['./#blort', 'file:/some/dir/foo', 'file:/some/dir/#blort'],
590 ['./#', 'file:/some/dir/foo', 'file:/some/dir/#'],
591 // Ryan Lee
592 ['./', 'http://example/x/abc.efg', 'http://example/x/'],
593 
594 // Graham Klyne's tests
595 // http://www.ninebynine.org/Software/HaskellUtils/Network/UriTest.xls
596 // 01-31 are from Connelly's cases
597 
598 // 32-49
599 ['./q:r', 'http://ex/x/y', 'http://ex/x/q:r'],
600 ['./p=q:r', 'http://ex/x/y', 'http://ex/x/p=q:r'],
601 ['?pp/rr', 'http://ex/x/y?pp/qq', 'http://ex/x/y?pp/rr'],
602 ['y/z', 'http://ex/x/y?pp/qq', 'http://ex/x/y/z'],
603 [
604 'local/qual@domain.org#frag',
605 'mailto:local',
606 'mailto:local/qual@domain.org#frag',
607 ],
608 [
609 'more/qual2@domain2.org#frag',
610 'mailto:local/qual1@domain1.org',
611 'mailto:local/more/qual2@domain2.org#frag',
612 ],
613 ['y?q', 'http://ex/x/y?q', 'http://ex/x/y?q'],
614 ['/x/y?q', 'http://ex?p', 'http://ex/x/y?q'],
615 ['c/d', 'foo:a/b', 'foo:a/c/d'],
616 ['/c/d', 'foo:a/b', 'foo:/c/d'],
617 ['', 'foo:a/b?c#d', 'foo:a/b?c'],
618 ['b/c', 'foo:a', 'foo:b/c'],
619 ['../b/c', 'foo:/a/y/z', 'foo:/a/b/c'],
620 ['./b/c', 'foo:a', 'foo:b/c'],
621 ['/./b/c', 'foo:a', 'foo:/b/c'],
622 ['../../d', 'foo://a//b/c', 'foo://a/d'],
623 ['.', 'foo:a', 'foo:'],
624 ['..', 'foo:a', 'foo:'],
625 
626 // 50-57[cf. TimBL comments --
627 // http://lists.w3.org/Archives/Public/uri/2003Feb/0028.html,
628 // http://lists.w3.org/Archives/Public/uri/2003Jan/0008.html)
629 ['abc', 'http://example/x/y%2Fz', 'http://example/x/abc'],
630 ['../../x%2Fabc', 'http://example/a/x/y/z', 'http://example/a/x%2Fabc'],
631 ['../x%2Fabc', 'http://example/a/x/y%2Fz', 'http://example/a/x%2Fabc'],
632 ['abc', 'http://example/x%2Fy/z', 'http://example/x%2Fy/abc'],
633 ['q%3Ar', 'http://ex/x/y', 'http://ex/x/q%3Ar'],
634 ['/x%2Fabc', 'http://example/x/y%2Fz', 'http://example/x%2Fabc'],
635 ['/x%2Fabc', 'http://example/x/y/z', 'http://example/x%2Fabc'],
636 ['/x%2Fabc', 'http://example/x/y%2Fz', 'http://example/x%2Fabc'],
637 
638 // 70-77
639 [
640 'local2@domain2',
641 'mailto:local1@domain1?query1',
642 'mailto:local2@domain2',
643 ],
644 [
645 'local2@domain2?query2',
646 'mailto:local1@domain1',
647 'mailto:local2@domain2?query2',
648 ],
649 [
650 'local2@domain2?query2',
651 'mailto:local1@domain1?query1',
652 'mailto:local2@domain2?query2',
653 ],
654 ['?query2', 'mailto:local@domain?query1', 'mailto:local@domain?query2'],
655 ['local@domain?query2', 'mailto:?query1', 'mailto:local@domain?query2'],
656 ['?query2', 'mailto:local@domain?query1', 'mailto:local@domain?query2'],
657 ['http://example/a/b?c/../d', 'foo:bar', 'http://example/a/b?c/../d'],
658 ['http://example/a/b#c/../d', 'foo:bar', 'http://example/a/b#c/../d'],
659 
660 // 82-88
661 // @isaacs Disagree. Not how browsers do it.
662 // ['http:this', 'http://example.org/base/uri', 'http:this'],
663 // @isaacs Added
664 [
665 'http:this',
666 'http://example.org/base/uri',
667 'http://example.org/base/this',
668 ],
669 ['http:this', 'http:base', 'http:this'],
670 ['.//g', 'f:/a', 'f://g'],
671 ['b/c//d/e', 'f://example.org/base/a', 'f://example.org/base/b/c//d/e'],
672 [
673 'm2@example.ord/c2@example.org',
674 'mid:m@example.ord/c@example.org',
675 'mid:m@example.ord/m2@example.ord/c2@example.org',
676 ],
677 [
678 'mini1.xml',
679 'file:///C:/DEV/Haskell/lib/HXmlToolbox-3.01/examples/',
680 'file:///C:/DEV/Haskell/lib/HXmlToolbox-3.01/examples/mini1.xml',
681 ],
682 ['../b/c', 'foo:a/y/z', 'foo:a/b/c'],
683 
684 // changing auth
685 [
686 'http://diff:auth@www.example.com',
687 'http://asdf:qwer@www.example.com',
688 'http://diff:auth@www.example.com/',
689 ],
690 
691 // changing port
692 [
693 'https://example.com:81/',
694 'https://example.com:82/',
695 'https://example.com:81/',
696 ],
697 
698 // https://github.com/nodejs/node/issues/1435
699 [
700 'https://another.host.com/',
701 'https://user:password@example.org/',
702 'https://another.host.com/',
703 ],
704 [
705 '//another.host.com/',
706 'https://user:password@example.org/',
707 'https://another.host.com/',
708 ],
709 [
710 'http://another.host.com/',
711 'https://user:password@example.org/',
712 'http://another.host.com/',
713 ],
714 [
715 'mailto:another.host.com',
716 'mailto:user@example.org',
717 'mailto:another.host.com',
718 ],
719 [
720 'https://example.com/foo',
721 'https://user:password@example.com',
722 'https://user:password@example.com/foo',
723 ],
724 
725 // No path at all
726 ['#hash1', '#hash2', '#hash1'],
727 ];
728 for (let i = 0; i < relativeTests2.length; i++) {
729 const relativeTest = relativeTests2[i];
730 
731 const a = url.resolve(relativeTest[1], relativeTest[0]);
732 const e = url.format(relativeTest[2]);
733 assert.strictEqual(
734 a,
735 e,
736 `resolve(${relativeTest[0]}, ${relativeTest[1]})` +
737 ` == ${e}\n actual=${a}`
738 );
739 }
740 
741 // If format and parse are inverse operations then
742 // resolveObject(parse(x), y) == parse(resolve(x, y))
743 
744 // format: [from, path, expected]
745 for (let i = 0; i < relativeTests.length; i++) {
746 const relativeTest = relativeTests[i];
747 
748 let actual = url.resolveObject(
749 url.parse(relativeTest[0]),
750 relativeTest[1]
751 );
752 let expected = url.parse(relativeTest[2]);
753 
754 assert.deepStrictEqual(actual, expected);
755 
756 expected = relativeTest[2];
757 actual = url.format(actual);
758 
759 assert.strictEqual(
760 actual,
761 expected,
762 `format(${actual}) == ${expected}\n` + `actual: ${actual}`
763 );
764 }
765 
766 // format: [to, from, result]
767 // the test: ['.//g', 'f:/a', 'f://g'] is a fundamental problem
768 // url.parse('f:/a') does not have a host
769 // url.resolve('f:/a', './/g') does not have a host because you have moved
770 // down to the g directory. i.e. f: //g, however when this url is parsed
771 // f:// will indicate that the host is g which is not the case.
772 // it is unclear to me how to keep this information from being lost
773 // it may be that a pathname of ////g should collapse to /g but this seems
774 // to be a lot of work for an edge case. Right now I remove the test
775 if (
776 relativeTests2[181][0] === './/g' &&
777 relativeTests2[181][1] === 'f:/a' &&
778 relativeTests2[181][2] === 'f://g'
779 ) {
780 relativeTests2.splice(181, 1);
781 }
782 for (let i = 0; i < relativeTests2.length; i++) {
783 const relativeTest = relativeTests2[i];
784 
785 let actual = url.resolveObject(
786 url.parse(relativeTest[1]),
787 relativeTest[0]
788 );
789 let expected = url.parse(relativeTest[2]);
790 
791 assert.deepStrictEqual(
792 actual,
793 expected,
794 `expected ${inspect(expected)} but got ${inspect(actual)}`
795 );
796 
797 expected = url.format(relativeTest[2]);
798 actual = url.format(actual);
799 
800 assert.strictEqual(
801 actual,
802 expected,
803 `format(${relativeTest[1]}) == ${expected}\n` + `actual: ${actual}`
804 );
805 }
806 },
807};
808 
809// Ref: https://github.com/nodejs/node/blob/e92446536ed4e268c9eef6ae6f911e384c98eecf/test/parallel/test-url-urltooptions.js
810export const urlToHttpOptions = {
811 async test() {
812 // Test urlToHttpOptions
813 const urlObj = new URL('http://user:pass@foo.bar.com:21/aaa/zzz?l=24#test');
814 const opts = url.urlToHttpOptions(urlObj);
815 assert.strictEqual(opts instanceof URL, false);
816 assert.strictEqual(opts.protocol, 'http:');
817 assert.strictEqual(opts.auth, 'user:pass');
818 assert.strictEqual(opts.hostname, 'foo.bar.com');
819 assert.strictEqual(opts.port, 21);
820 assert.strictEqual(opts.path, '/aaa/zzz?l=24');
821 assert.strictEqual(opts.pathname, '/aaa/zzz');
822 assert.strictEqual(opts.search, '?l=24');
823 assert.strictEqual(opts.hash, '#test');
824 
825 const { hostname } = url.urlToHttpOptions(new URL('http://[::1]:21'));
826 assert.strictEqual(hostname, '::1');
827 
828 // If a WHATWG URL object is copied, it is possible that the resulting copy
829 // contains the Symbols that Node uses for brand checking, but not the data
830 // properties, which are getters. Verify that urlToHttpOptions() can handle
831 // such a case.
832 const copiedUrlObj = { ...urlObj };
833 const copiedOpts = url.urlToHttpOptions(copiedUrlObj);
834 assert.strictEqual(copiedOpts instanceof URL, false);
835 assert.strictEqual(copiedOpts.protocol, undefined);
836 assert.strictEqual(copiedOpts.auth, undefined);
837 assert.strictEqual(copiedOpts.hostname, undefined);
838 assert.strictEqual(copiedOpts.port, NaN);
839 assert.strictEqual(copiedOpts.path, '');
840 assert.strictEqual(copiedOpts.pathname, undefined);
841 assert.strictEqual(copiedOpts.search, undefined);
842 assert.strictEqual(copiedOpts.hash, undefined);
843 assert.strictEqual(copiedOpts.href, undefined);
844 },
845};
846 
847// Ref: https://github.com/nodejs/node/blob/e92446536ed4e268c9eef6ae6f911e384c98eecf/test/parallel/test-url-format-whatwg.js
848export const formatWhatwg = {
849 async test() {
850 const myURL = new URL(
851 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
852 );
853 
854 // should format
855 assert.strictEqual(
856 url.format(myURL),
857 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
858 );
859 
860 assert.strictEqual(
861 url.format(myURL, {}),
862 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
863 );
864 
865 // handle invalid arguments
866 for (const value of [true, 1, 'test', Infinity]) {
867 assert.throws(() => url.format(myURL, value), {
868 code: 'ERR_INVALID_ARG_TYPE',
869 name: 'TypeError',
870 });
871 }
872 
873 // any falsy value other than undefined will be treated as false
874 assert.strictEqual(
875 url.format(myURL, { auth: false }),
876 'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c'
877 );
878 
879 assert.strictEqual(
880 url.format(myURL, { auth: '' }),
881 'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c'
882 );
883 
884 assert.strictEqual(
885 url.format(myURL, { auth: 0 }),
886 'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c'
887 );
888 
889 assert.strictEqual(
890 url.format(myURL, { auth: 1 }),
891 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
892 );
893 
894 assert.strictEqual(
895 url.format(myURL, { auth: {} }),
896 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
897 );
898 
899 assert.strictEqual(
900 url.format(myURL, { fragment: false }),
901 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b'
902 );
903 
904 assert.strictEqual(
905 url.format(myURL, { fragment: '' }),
906 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b'
907 );
908 
909 assert.strictEqual(
910 url.format(myURL, { fragment: 0 }),
911 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b'
912 );
913 
914 assert.strictEqual(
915 url.format(myURL, { fragment: 1 }),
916 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
917 );
918 
919 assert.strictEqual(
920 url.format(myURL, { fragment: {} }),
921 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
922 );
923 
924 assert.strictEqual(
925 url.format(myURL, { search: false }),
926 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a#c'
927 );
928 
929 assert.strictEqual(
930 url.format(myURL, { search: '' }),
931 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a#c'
932 );
933 
934 assert.strictEqual(
935 url.format(myURL, { search: 0 }),
936 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a#c'
937 );
938 
939 assert.strictEqual(
940 url.format(myURL, { search: 1 }),
941 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
942 );
943 
944 assert.strictEqual(
945 url.format(myURL, { search: {} }),
946 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
947 );
948 
949 assert.strictEqual(
950 url.format(myURL, { unicode: true }),
951 'http://user:pass@理容ナカムラ.com/a?a=b#c'
952 );
953 
954 assert.strictEqual(
955 url.format(myURL, { unicode: 1 }),
956 'http://user:pass@理容ナカムラ.com/a?a=b#c'
957 );
958 
959 assert.strictEqual(
960 url.format(myURL, { unicode: {} }),
961 'http://user:pass@理容ナカムラ.com/a?a=b#c'
962 );
963 
964 assert.strictEqual(
965 url.format(myURL, { unicode: false }),
966 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
967 );
968 
969 assert.strictEqual(
970 url.format(myURL, { unicode: 0 }),
971 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
972 );
973 
974 // should format with unicode: true
975 assert.strictEqual(
976 url.format(new URL('http://user:pass@xn--0zwm56d.com:8080/path'), {
977 unicode: true,
978 }),
979 'http://user:pass@测试.com:8080/path'
980 );
981 
982 // should format tel: prefix
983 assert.strictEqual(
984 url.format(new URL('tel:123')),
985 url.format(new URL('tel:123'), { unicode: true })
986 );
987 },
988};
989 
990// Ref: https://github.com/nodejs/node/blob/e92446536ed4e268c9eef6ae6f911e384c98eecf/test/parallel/test-url-parse-query.js
991export const urlParseQuery = {
992 async test() {
993 function createWithNoPrototype(properties = []) {
994 const noProto = { __proto__: null };
995 properties.forEach((property) => {
996 noProto[property.key] = property.value;
997 });
998 return noProto;
999 }
1000 
1001 function check(actual, expected) {
1002 assert.notStrictEqual(Object.getPrototypeOf(actual), Object.prototype);
1003 assert.deepStrictEqual(
1004 Object.keys(actual).sort(),
1005 Object.keys(expected).sort()
1006 );
1007 Object.keys(expected).forEach(function (key) {
1008 assert.deepStrictEqual(actual[key], expected[key]);
1009 });
1010 }
1011 
1012 const parseTestsWithQueryString = {
1013 '/foo/bar?baz=quux#frag': {
1014 href: '/foo/bar?baz=quux#frag',
1015 hash: '#frag',
1016 search: '?baz=quux',
1017 query: createWithNoPrototype([{ key: 'baz', value: 'quux' }]),
1018 pathname: '/foo/bar',
1019 path: '/foo/bar?baz=quux',
1020 },
1021 'http://example.com': {
1022 href: 'http://example.com/',
1023 protocol: 'http:',
1024 slashes: true,
1025 host: 'example.com',
1026 hostname: 'example.com',
1027 query: createWithNoPrototype(),
1028 search: null,
1029 pathname: '/',
1030 path: '/',
1031 },
1032 '/example': {
1033 protocol: null,
1034 slashes: null,
1035 auth: undefined,
1036 host: null,
1037 port: null,
1038 hostname: null,
1039 hash: null,
1040 search: null,
1041 query: createWithNoPrototype(),
1042 pathname: '/example',
1043 path: '/example',
1044 href: '/example',
1045 },
1046 '/example?query=value': {
1047 protocol: null,
1048 slashes: null,
1049 auth: undefined,
1050 host: null,
1051 port: null,
1052 hostname: null,
1053 hash: null,
1054 search: '?query=value',
1055 query: createWithNoPrototype([{ key: 'query', value: 'value' }]),
1056 pathname: '/example',
1057 path: '/example?query=value',
1058 href: '/example?query=value',
1059 },
1060 };
1061 for (const u in parseTestsWithQueryString) {
1062 const actual = url.parse(u, true);
1063 const expected = Object.assign(
1064 new url.Url(),
1065 parseTestsWithQueryString[u]
1066 );
1067 for (const i in actual) {
1068 if (actual[i] === null && expected[i] === undefined) {
1069 expected[i] = null;
1070 }
1071 }
1072 
1073 const properties = Object.keys(actual).sort();
1074 assert.deepStrictEqual(properties, Object.keys(expected).sort());
1075 properties.forEach((property) => {
1076 if (property === 'query') {
1077 check(actual[property], expected[property]);
1078 } else {
1079 assert.deepStrictEqual(actual[property], expected[property]);
1080 }
1081 });
1082 }
1083 },
1084};
1085 
1086// Ref: https://github.com/nodejs/node/blob/e92446536ed4e268c9eef6ae6f911e384c98eecf/test/parallel/test-url-parse-format.js
1087export const urlParseFormat = {
1088 async test() {
1089 // URLs to parse, and expected data
1090 // { url : parsed }
1091 const parseTests = {
1092 '//some_path': {
1093 href: '//some_path',
1094 pathname: '//some_path',
1095 path: '//some_path',
1096 },
1097 
1098 'http:\\\\evil-phisher\\foo.html#h\\a\\s\\h': {
1099 protocol: 'http:',
1100 slashes: true,
1101 host: 'evil-phisher',
1102 hostname: 'evil-phisher',
1103 pathname: '/foo.html',
1104 path: '/foo.html',
1105 hash: '#h%5Ca%5Cs%5Ch',
1106 href: 'http://evil-phisher/foo.html#h%5Ca%5Cs%5Ch',
1107 },
1108 
1109 'http:\\\\evil-phisher\\foo.html?json="\\"foo\\""#h\\a\\s\\h': {
1110 protocol: 'http:',
1111 slashes: true,
1112 host: 'evil-phisher',
1113 hostname: 'evil-phisher',
1114 pathname: '/foo.html',
1115 search: '?json=%22%5C%22foo%5C%22%22',
1116 query: 'json=%22%5C%22foo%5C%22%22',
1117 path: '/foo.html?json=%22%5C%22foo%5C%22%22',
1118 hash: '#h%5Ca%5Cs%5Ch',
1119 href: 'http://evil-phisher/foo.html?json=%22%5C%22foo%5C%22%22#h%5Ca%5Cs%5Ch',
1120 },
1121 
1122 'http:\\\\evil-phisher\\foo.html#h\\a\\s\\h?blarg': {
1123 protocol: 'http:',
1124 slashes: true,
1125 host: 'evil-phisher',
1126 hostname: 'evil-phisher',
1127 pathname: '/foo.html',
1128 path: '/foo.html',
1129 hash: '#h%5Ca%5Cs%5Ch?blarg',
1130 href: 'http://evil-phisher/foo.html#h%5Ca%5Cs%5Ch?blarg',
1131 },
1132 
1133 'http:\\\\evil-phisher\\foo.html': {
1134 protocol: 'http:',
1135 slashes: true,
1136 host: 'evil-phisher',
1137 hostname: 'evil-phisher',
1138 pathname: '/foo.html',
1139 path: '/foo.html',
1140 href: 'http://evil-phisher/foo.html',
1141 },
1142 
1143 'HTTP://www.example.com/': {
1144 href: 'http://www.example.com/',
1145 protocol: 'http:',
1146 slashes: true,
1147 host: 'www.example.com',
1148 hostname: 'www.example.com',
1149 pathname: '/',
1150 path: '/',
1151 },
1152 
1153 'HTTP://www.example.com': {
1154 href: 'http://www.example.com/',
1155 protocol: 'http:',
1156 slashes: true,
1157 host: 'www.example.com',
1158 hostname: 'www.example.com',
1159 pathname: '/',
1160 path: '/',
1161 },
1162 
1163 'http://www.ExAmPlE.com/': {
1164 href: 'http://www.example.com/',
1165 protocol: 'http:',
1166 slashes: true,
1167 host: 'www.example.com',
1168 hostname: 'www.example.com',
1169 pathname: '/',
1170 path: '/',
1171 },
1172 
1173 'http://user:pw@www.ExAmPlE.com/': {
1174 href: 'http://user:pw@www.example.com/',
1175 protocol: 'http:',
1176 slashes: true,
1177 auth: 'user:pw',
1178 host: 'www.example.com',
1179 hostname: 'www.example.com',
1180 pathname: '/',
1181 path: '/',
1182 },
1183 
1184 'http://USER:PW@www.ExAmPlE.com/': {
1185 href: 'http://USER:PW@www.example.com/',
1186 protocol: 'http:',
1187 slashes: true,
1188 auth: 'USER:PW',
1189 host: 'www.example.com',
1190 hostname: 'www.example.com',
1191 pathname: '/',
1192 path: '/',
1193 },
1194 
1195 'http://user@www.example.com/': {
1196 href: 'http://user@www.example.com/',
1197 protocol: 'http:',
1198 slashes: true,
1199 auth: 'user',
1200 host: 'www.example.com',
1201 hostname: 'www.example.com',
1202 pathname: '/',
1203 path: '/',
1204 },
1205 
1206 'http://user%3Apw@www.example.com/': {
1207 href: 'http://user:pw@www.example.com/',
1208 protocol: 'http:',
1209 slashes: true,
1210 auth: 'user:pw',
1211 host: 'www.example.com',
1212 hostname: 'www.example.com',
1213 pathname: '/',
1214 path: '/',
1215 },
1216 
1217 "http://x.com/path?that's#all, folks": {
1218 href: 'http://x.com/path?that%27s#all,%20folks',
1219 protocol: 'http:',
1220 slashes: true,
1221 host: 'x.com',
1222 hostname: 'x.com',
1223 search: '?that%27s',
1224 query: 'that%27s',
1225 pathname: '/path',
1226 hash: '#all,%20folks',
1227 path: '/path?that%27s',
1228 },
1229 
1230 'HTTP://X.COM/Y': {
1231 href: 'http://x.com/Y',
1232 protocol: 'http:',
1233 slashes: true,
1234 host: 'x.com',
1235 hostname: 'x.com',
1236 pathname: '/Y',
1237 path: '/Y',
1238 },
1239 
1240 // Whitespace in the front
1241 ' http://www.example.com/': {
1242 href: 'http://www.example.com/',
1243 protocol: 'http:',
1244 slashes: true,
1245 host: 'www.example.com',
1246 hostname: 'www.example.com',
1247 pathname: '/',
1248 path: '/',
1249 },
1250 
1251 // + not an invalid host character
1252 // per https://url.spec.whatwg.org/#host-parsing
1253 'http://x.y.com+a/b/c': {
1254 href: 'http://x.y.com+a/b/c',
1255 protocol: 'http:',
1256 slashes: true,
1257 host: 'x.y.com+a',
1258 hostname: 'x.y.com+a',
1259 pathname: '/b/c',
1260 path: '/b/c',
1261 },
1262 
1263 // An unexpected invalid char in the hostname.
1264 'HtTp://x.y.cOm;a/b/c?d=e#f g<h>i': {
1265 href: 'http://x.y.com/;a/b/c?d=e#f%20g%3Ch%3Ei',
1266 protocol: 'http:',
1267 slashes: true,
1268 host: 'x.y.com',
1269 hostname: 'x.y.com',
1270 pathname: ';a/b/c',
1271 search: '?d=e',
1272 query: 'd=e',
1273 hash: '#f%20g%3Ch%3Ei',
1274 path: ';a/b/c?d=e',
1275 },
1276 
1277 // Make sure that we don't accidentally lcast the path parts.
1278 'HtTp://x.y.cOm;A/b/c?d=e#f g<h>i': {
1279 href: 'http://x.y.com/;A/b/c?d=e#f%20g%3Ch%3Ei',
1280 protocol: 'http:',
1281 slashes: true,
1282 host: 'x.y.com',
1283 hostname: 'x.y.com',
1284 pathname: ';A/b/c',
1285 search: '?d=e',
1286 query: 'd=e',
1287 hash: '#f%20g%3Ch%3Ei',
1288 path: ';A/b/c?d=e',
1289 },
1290 
1291 'http://x...y...#p': {
1292 href: 'http://x...y.../#p',
1293 protocol: 'http:',
1294 slashes: true,
1295 host: 'x...y...',
1296 hostname: 'x...y...',
1297 hash: '#p',
1298 pathname: '/',
1299 path: '/',
1300 },
1301 
1302 'http://x/p/"quoted"': {
1303 href: 'http://x/p/%22quoted%22',
1304 protocol: 'http:',
1305 slashes: true,
1306 host: 'x',
1307 hostname: 'x',
1308 pathname: '/p/%22quoted%22',
1309 path: '/p/%22quoted%22',
1310 },
1311 
1312 '<http://goo.corn/bread> Is a URL!': {
1313 href: '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!',
1314 pathname: '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!',
1315 path: '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!',
1316 },
1317 
1318 'http://www.narwhaljs.org/blog/categories?id=news': {
1319 href: 'http://www.narwhaljs.org/blog/categories?id=news',
1320 protocol: 'http:',
1321 slashes: true,
1322 host: 'www.narwhaljs.org',
1323 hostname: 'www.narwhaljs.org',
1324 search: '?id=news',
1325 query: 'id=news',
1326 pathname: '/blog/categories',
1327 path: '/blog/categories?id=news',
1328 },
1329 
1330 'http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=': {
1331 href: 'http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=',
1332 protocol: 'http:',
1333 slashes: true,
1334 host: 'mt0.google.com',
1335 hostname: 'mt0.google.com',
1336 pathname: '/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=',
1337 path: '/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=',
1338 },
1339 
1340 'http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=': {
1341 href:
1342 'http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api' +
1343 '&x=2&y=2&z=3&s=',
1344 protocol: 'http:',
1345 slashes: true,
1346 host: 'mt0.google.com',
1347 hostname: 'mt0.google.com',
1348 search: '???&hl=en&src=api&x=2&y=2&z=3&s=',
1349 query: '??&hl=en&src=api&x=2&y=2&z=3&s=',
1350 pathname: '/vt/lyrs=m@114',
1351 path: '/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=',
1352 },
1353 
1354 'http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=':
1355 {
1356 href: 'http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=',
1357 protocol: 'http:',
1358 slashes: true,
1359 host: 'mt0.google.com',
1360 auth: 'user:pass',
1361 hostname: 'mt0.google.com',
1362 search: '???&hl=en&src=api&x=2&y=2&z=3&s=',
1363 query: '??&hl=en&src=api&x=2&y=2&z=3&s=',
1364 pathname: '/vt/lyrs=m@114',
1365 path: '/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=',
1366 },
1367 
1368 'file:///etc/passwd': {
1369 href: 'file:///etc/passwd',
1370 slashes: true,
1371 protocol: 'file:',
1372 pathname: '/etc/passwd',
1373 hostname: '',
1374 host: '',
1375 path: '/etc/passwd',
1376 },
1377 
1378 'file://localhost/etc/passwd': {
1379 href: 'file://localhost/etc/passwd',
1380 protocol: 'file:',
1381 slashes: true,
1382 pathname: '/etc/passwd',
1383 hostname: 'localhost',
1384 host: 'localhost',
1385 path: '/etc/passwd',
1386 },
1387 
1388 'file://foo/etc/passwd': {
1389 href: 'file://foo/etc/passwd',
1390 protocol: 'file:',
1391 slashes: true,
1392 pathname: '/etc/passwd',
1393 hostname: 'foo',
1394 host: 'foo',
1395 path: '/etc/passwd',
1396 },
1397 
1398 'file:///etc/node/': {
1399 href: 'file:///etc/node/',
1400 slashes: true,
1401 protocol: 'file:',
1402 pathname: '/etc/node/',
1403 hostname: '',
1404 host: '',
1405 path: '/etc/node/',
1406 },
1407 
1408 'file://localhost/etc/node/': {
1409 href: 'file://localhost/etc/node/',
1410 protocol: 'file:',
1411 slashes: true,
1412 pathname: '/etc/node/',
1413 hostname: 'localhost',
1414 host: 'localhost',
1415 path: '/etc/node/',
1416 },
1417 
1418 'file://foo/etc/node/': {
1419 href: 'file://foo/etc/node/',
1420 protocol: 'file:',
1421 slashes: true,
1422 pathname: '/etc/node/',
1423 hostname: 'foo',
1424 host: 'foo',
1425 path: '/etc/node/',
1426 },
1427 
1428 'http:/baz/../foo/bar': {
1429 href: 'http:/baz/../foo/bar',
1430 protocol: 'http:',
1431 pathname: '/baz/../foo/bar',
1432 path: '/baz/../foo/bar',
1433 },
1434 
1435 'http://user:pass@example.com:8000/foo/bar?baz=quux#frag': {
1436 href: 'http://user:pass@example.com:8000/foo/bar?baz=quux#frag',
1437 protocol: 'http:',
1438 slashes: true,
1439 host: 'example.com:8000',
1440 auth: 'user:pass',
1441 port: '8000',
1442 hostname: 'example.com',
1443 hash: '#frag',
1444 search: '?baz=quux',
1445 query: 'baz=quux',
1446 pathname: '/foo/bar',
1447 path: '/foo/bar?baz=quux',
1448 },
1449 
1450 '//user:pass@example.com:8000/foo/bar?baz=quux#frag': {
1451 href: '//user:pass@example.com:8000/foo/bar?baz=quux#frag',
1452 slashes: true,
1453 host: 'example.com:8000',
1454 auth: 'user:pass',
1455 port: '8000',
1456 hostname: 'example.com',
1457 hash: '#frag',
1458 search: '?baz=quux',
1459 query: 'baz=quux',
1460 pathname: '/foo/bar',
1461 path: '/foo/bar?baz=quux',
1462 },
1463 
1464 '/foo/bar?baz=quux#frag': {
1465 href: '/foo/bar?baz=quux#frag',
1466 hash: '#frag',
1467 search: '?baz=quux',
1468 query: 'baz=quux',
1469 pathname: '/foo/bar',
1470 path: '/foo/bar?baz=quux',
1471 },
1472 
1473 'http:/foo/bar?baz=quux#frag': {
1474 href: 'http:/foo/bar?baz=quux#frag',
1475 protocol: 'http:',
1476 hash: '#frag',
1477 search: '?baz=quux',
1478 query: 'baz=quux',
1479 pathname: '/foo/bar',
1480 path: '/foo/bar?baz=quux',
1481 },
1482 
1483 'mailto:foo@bar.com?subject=hello': {
1484 href: 'mailto:foo@bar.com?subject=hello',
1485 protocol: 'mailto:',
1486 host: 'bar.com',
1487 auth: 'foo',
1488 hostname: 'bar.com',
1489 search: '?subject=hello',
1490 query: 'subject=hello',
1491 path: '?subject=hello',
1492 },
1493 
1494 "javascript:alert('hello');": {
1495 href: "javascript:alert('hello');",
1496 protocol: 'javascript:',
1497 pathname: "alert('hello');",
1498 path: "alert('hello');",
1499 },
1500 
1501 'xmpp:isaacschlueter@jabber.org': {
1502 href: 'xmpp:isaacschlueter@jabber.org',
1503 protocol: 'xmpp:',
1504 host: 'jabber.org',
1505 auth: 'isaacschlueter',
1506 hostname: 'jabber.org',
1507 },
1508 
1509 'http://atpass:foo%40bar@127.0.0.1:8080/path?search=foo#bar': {
1510 href: 'http://atpass:foo%40bar@127.0.0.1:8080/path?search=foo#bar',
1511 protocol: 'http:',
1512 slashes: true,
1513 host: '127.0.0.1:8080',
1514 auth: 'atpass:foo@bar',
1515 hostname: '127.0.0.1',
1516 port: '8080',
1517 pathname: '/path',
1518 search: '?search=foo',
1519 query: 'search=foo',
1520 hash: '#bar',
1521 path: '/path?search=foo',
1522 },
1523 
1524 'svn+ssh://foo/bar': {
1525 href: 'svn+ssh://foo/bar',
1526 host: 'foo',
1527 hostname: 'foo',
1528 protocol: 'svn+ssh:',
1529 pathname: '/bar',
1530 path: '/bar',
1531 slashes: true,
1532 },
1533 
1534 'dash-test://foo/bar': {
1535 href: 'dash-test://foo/bar',
1536 host: 'foo',
1537 hostname: 'foo',
1538 protocol: 'dash-test:',
1539 pathname: '/bar',
1540 path: '/bar',
1541 slashes: true,
1542 },
1543 
1544 'dash-test:foo/bar': {
1545 href: 'dash-test:foo/bar',
1546 host: 'foo',
1547 hostname: 'foo',
1548 protocol: 'dash-test:',
1549 pathname: '/bar',
1550 path: '/bar',
1551 },
1552 
1553 'dot.test://foo/bar': {
1554 href: 'dot.test://foo/bar',
1555 host: 'foo',
1556 hostname: 'foo',
1557 protocol: 'dot.test:',
1558 pathname: '/bar',
1559 path: '/bar',
1560 slashes: true,
1561 },
1562 
1563 'dot.test:foo/bar': {
1564 href: 'dot.test:foo/bar',
1565 host: 'foo',
1566 hostname: 'foo',
1567 protocol: 'dot.test:',
1568 pathname: '/bar',
1569 path: '/bar',
1570 },
1571 
1572 // IDNA tests
1573 'http://www.日本語.com/': {
1574 href: 'http://www.xn--wgv71a119e.com/',
1575 protocol: 'http:',
1576 slashes: true,
1577 host: 'www.xn--wgv71a119e.com',
1578 hostname: 'www.xn--wgv71a119e.com',
1579 pathname: '/',
1580 path: '/',
1581 },
1582 
1583 'http://example.Bücher.com/': {
1584 href: 'http://example.xn--bcher-kva.com/',
1585 protocol: 'http:',
1586 slashes: true,
1587 host: 'example.xn--bcher-kva.com',
1588 hostname: 'example.xn--bcher-kva.com',
1589 pathname: '/',
1590 path: '/',
1591 },
1592 
1593 'http://www.Äffchen.com/': {
1594 href: 'http://www.xn--ffchen-9ta.com/',
1595 protocol: 'http:',
1596 slashes: true,
1597 host: 'www.xn--ffchen-9ta.com',
1598 hostname: 'www.xn--ffchen-9ta.com',
1599 pathname: '/',
1600 path: '/',
1601 },
1602 
1603 'http://www.Äffchen.cOm;A/b/c?d=e#f g<h>i': {
1604 href: 'http://www.xn--ffchen-9ta.com/;A/b/c?d=e#f%20g%3Ch%3Ei',
1605 protocol: 'http:',
1606 slashes: true,
1607 host: 'www.xn--ffchen-9ta.com',
1608 hostname: 'www.xn--ffchen-9ta.com',
1609 pathname: ';A/b/c',
1610 search: '?d=e',
1611 query: 'd=e',
1612 hash: '#f%20g%3Ch%3Ei',
1613 path: ';A/b/c?d=e',
1614 },
1615 
1616 'http://SÉLIER.COM/': {
1617 href: 'http://xn--slier-bsa.com/',
1618 protocol: 'http:',
1619 slashes: true,
1620 host: 'xn--slier-bsa.com',
1621 hostname: 'xn--slier-bsa.com',
1622 pathname: '/',
1623 path: '/',
1624 },
1625 
1626 'http://ليهمابتكلموشعربي؟.ي؟/': {
1627 href: 'http://xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f/',
1628 protocol: 'http:',
1629 slashes: true,
1630 host: 'xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f',
1631 hostname: 'xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f',
1632 pathname: '/',
1633 path: '/',
1634 },
1635 
1636 'http://➡.ws/➡': {
1637 href: 'http://xn--hgi.ws/➡',
1638 protocol: 'http:',
1639 slashes: true,
1640 host: 'xn--hgi.ws',
1641 hostname: 'xn--hgi.ws',
1642 pathname: '/➡',
1643 path: '/➡',
1644 },
1645 
1646 'http://bucket_name.s3.amazonaws.com/image.jpg': {
1647 protocol: 'http:',
1648 slashes: true,
1649 host: 'bucket_name.s3.amazonaws.com',
1650 hostname: 'bucket_name.s3.amazonaws.com',
1651 pathname: '/image.jpg',
1652 href: 'http://bucket_name.s3.amazonaws.com/image.jpg',
1653 path: '/image.jpg',
1654 },
1655 
1656 'git+http://github.com/joyent/node.git': {
1657 protocol: 'git+http:',
1658 slashes: true,
1659 host: 'github.com',
1660 hostname: 'github.com',
1661 pathname: '/joyent/node.git',
1662 path: '/joyent/node.git',
1663 href: 'git+http://github.com/joyent/node.git',
1664 },
1665 
1666 // If local1@domain1 is uses as a relative URL it may
1667 // be parse into auth@hostname, but here there is no
1668 // way to make it work in url.parse, I add the test to be explicit
1669 'local1@domain1': {
1670 pathname: 'local1@domain1',
1671 path: 'local1@domain1',
1672 href: 'local1@domain1',
1673 },
1674 
1675 // While this may seem counter-intuitive, a browser will parse
1676 // <a href='www.google.com'> as a path.
1677 'www.example.com': {
1678 href: 'www.example.com',
1679 pathname: 'www.example.com',
1680 path: 'www.example.com',
1681 },
1682 
1683 // ipv6 support
1684 '[fe80::1]': {
1685 href: '[fe80::1]',
1686 pathname: '[fe80::1]',
1687 path: '[fe80::1]',
1688 },
1689 
1690 'coap://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]': {
1691 protocol: 'coap:',
1692 slashes: true,
1693 host: '[fedc:ba98:7654:3210:fedc:ba98:7654:3210]',
1694 hostname: 'fedc:ba98:7654:3210:fedc:ba98:7654:3210',
1695 href: 'coap://[fedc:ba98:7654:3210:fedc:ba98:7654:3210]/',
1696 pathname: '/',
1697 path: '/',
1698 },
1699 
1700 'coap://[1080:0:0:0:8:800:200C:417A]:61616/': {
1701 protocol: 'coap:',
1702 slashes: true,
1703 host: '[1080:0:0:0:8:800:200c:417a]:61616',
1704 port: '61616',
1705 hostname: '1080:0:0:0:8:800:200c:417a',
1706 href: 'coap://[1080:0:0:0:8:800:200c:417a]:61616/',
1707 pathname: '/',
1708 path: '/',
1709 },
1710 
1711 'http://user:password@[3ffe:2a00:100:7031::1]:8080': {
1712 protocol: 'http:',
1713 slashes: true,
1714 auth: 'user:password',
1715 host: '[3ffe:2a00:100:7031::1]:8080',
1716 port: '8080',
1717 hostname: '3ffe:2a00:100:7031::1',
1718 href: 'http://user:password@[3ffe:2a00:100:7031::1]:8080/',
1719 pathname: '/',
1720 path: '/',
1721 },
1722 
1723 'coap://u:p@[::192.9.5.5]:61616/.well-known/r?n=Temperature': {
1724 protocol: 'coap:',
1725 slashes: true,
1726 auth: 'u:p',
1727 host: '[::192.9.5.5]:61616',
1728 port: '61616',
1729 hostname: '::192.9.5.5',
1730 href: 'coap://u:p@[::192.9.5.5]:61616/.well-known/r?n=Temperature',
1731 search: '?n=Temperature',
1732 query: 'n=Temperature',
1733 pathname: '/.well-known/r',
1734 path: '/.well-known/r?n=Temperature',
1735 },
1736 
1737 // empty port
1738 'http://example.com:': {
1739 protocol: 'http:',
1740 slashes: true,
1741 host: 'example.com',
1742 hostname: 'example.com',
1743 href: 'http://example.com/',
1744 pathname: '/',
1745 path: '/',
1746 },
1747 
1748 'http://example.com:/a/b.html': {
1749 protocol: 'http:',
1750 slashes: true,
1751 host: 'example.com',
1752 hostname: 'example.com',
1753 href: 'http://example.com/a/b.html',
1754 pathname: '/a/b.html',
1755 path: '/a/b.html',
1756 },
1757 
1758 'http://example.com:?a=b': {
1759 protocol: 'http:',
1760 slashes: true,
1761 host: 'example.com',
1762 hostname: 'example.com',
1763 href: 'http://example.com/?a=b',
1764 search: '?a=b',
1765 query: 'a=b',
1766 pathname: '/',
1767 path: '/?a=b',
1768 },
1769 
1770 'http://example.com:#abc': {
1771 protocol: 'http:',
1772 slashes: true,
1773 host: 'example.com',
1774 hostname: 'example.com',
1775 href: 'http://example.com/#abc',
1776 hash: '#abc',
1777 pathname: '/',
1778 path: '/',
1779 },
1780 
1781 'http://[fe80::1]:/a/b?a=b#abc': {
1782 protocol: 'http:',
1783 slashes: true,
1784 host: '[fe80::1]',
1785 hostname: 'fe80::1',
1786 href: 'http://[fe80::1]/a/b?a=b#abc',
1787 search: '?a=b',
1788 query: 'a=b',
1789 hash: '#abc',
1790 pathname: '/a/b',
1791 path: '/a/b?a=b',
1792 },
1793 
1794 'http://-lovemonsterz.tumblr.com/rss': {
1795 protocol: 'http:',
1796 slashes: true,
1797 host: '-lovemonsterz.tumblr.com',
1798 hostname: '-lovemonsterz.tumblr.com',
1799 href: 'http://-lovemonsterz.tumblr.com/rss',
1800 pathname: '/rss',
1801 path: '/rss',
1802 },
1803 
1804 'http://-lovemonsterz.tumblr.com:80/rss': {
1805 protocol: 'http:',
1806 slashes: true,
1807 port: '80',
1808 host: '-lovemonsterz.tumblr.com:80',
1809 hostname: '-lovemonsterz.tumblr.com',
1810 href: 'http://-lovemonsterz.tumblr.com:80/rss',
1811 pathname: '/rss',
1812 path: '/rss',
1813 },
1814 
1815 'http://user:pass@-lovemonsterz.tumblr.com/rss': {
1816 protocol: 'http:',
1817 slashes: true,
1818 auth: 'user:pass',
1819 host: '-lovemonsterz.tumblr.com',
1820 hostname: '-lovemonsterz.tumblr.com',
1821 href: 'http://user:pass@-lovemonsterz.tumblr.com/rss',
1822 pathname: '/rss',
1823 path: '/rss',
1824 },
1825 
1826 'http://user:pass@-lovemonsterz.tumblr.com:80/rss': {
1827 protocol: 'http:',
1828 slashes: true,
1829 auth: 'user:pass',
1830 port: '80',
1831 host: '-lovemonsterz.tumblr.com:80',
1832 hostname: '-lovemonsterz.tumblr.com',
1833 href: 'http://user:pass@-lovemonsterz.tumblr.com:80/rss',
1834 pathname: '/rss',
1835 path: '/rss',
1836 },
1837 
1838 'http://_jabber._tcp.google.com/test': {
1839 protocol: 'http:',
1840 slashes: true,
1841 host: '_jabber._tcp.google.com',
1842 hostname: '_jabber._tcp.google.com',
1843 href: 'http://_jabber._tcp.google.com/test',
1844 pathname: '/test',
1845 path: '/test',
1846 },
1847 
1848 'http://user:pass@_jabber._tcp.google.com/test': {
1849 protocol: 'http:',
1850 slashes: true,
1851 auth: 'user:pass',
1852 host: '_jabber._tcp.google.com',
1853 hostname: '_jabber._tcp.google.com',
1854 href: 'http://user:pass@_jabber._tcp.google.com/test',
1855 pathname: '/test',
1856 path: '/test',
1857 },
1858 
1859 'http://_jabber._tcp.google.com:80/test': {
1860 protocol: 'http:',
1861 slashes: true,
1862 port: '80',
1863 host: '_jabber._tcp.google.com:80',
1864 hostname: '_jabber._tcp.google.com',
1865 href: 'http://_jabber._tcp.google.com:80/test',
1866 pathname: '/test',
1867 path: '/test',
1868 },
1869 
1870 'http://user:pass@_jabber._tcp.google.com:80/test': {
1871 protocol: 'http:',
1872 slashes: true,
1873 auth: 'user:pass',
1874 port: '80',
1875 host: '_jabber._tcp.google.com:80',
1876 hostname: '_jabber._tcp.google.com',
1877 href: 'http://user:pass@_jabber._tcp.google.com:80/test',
1878 pathname: '/test',
1879 path: '/test',
1880 },
1881 
1882 'http://x:1/\' <>"`/{}|\\^~`/': {
1883 protocol: 'http:',
1884 slashes: true,
1885 host: 'x:1',
1886 port: '1',
1887 hostname: 'x',
1888 pathname: '/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/',
1889 path: '/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/',
1890 href: 'http://x:1/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/',
1891 },
1892 
1893 'http://a@b@c/': {
1894 protocol: 'http:',
1895 slashes: true,
1896 auth: 'a@b',
1897 host: 'c',
1898 hostname: 'c',
1899 href: 'http://a%40b@c/',
1900 path: '/',
1901 pathname: '/',
1902 },
1903 
1904 'http://a@b?@c': {
1905 protocol: 'http:',
1906 slashes: true,
1907 auth: 'a',
1908 host: 'b',
1909 hostname: 'b',
1910 href: 'http://a@b/?@c',
1911 path: '/?@c',
1912 pathname: '/',
1913 search: '?@c',
1914 query: '@c',
1915 },
1916 
1917 'http://a.b/\tbc\ndr\ref g"hq\'j<kl>?mn\\op^q=r`99{st|uv}wz': {
1918 protocol: 'http:',
1919 slashes: true,
1920 host: 'a.b',
1921 port: null,
1922 hostname: 'a.b',
1923 hash: null,
1924 pathname: '/%09bc%0Adr%0Def%20g%22hq%27j%3Ckl%3E',
1925 path: '/%09bc%0Adr%0Def%20g%22hq%27j%3Ckl%3E?mn%5Cop%5Eq=r%6099%7Bst%7Cuv%7Dwz',
1926 search: '?mn%5Cop%5Eq=r%6099%7Bst%7Cuv%7Dwz',
1927 query: 'mn%5Cop%5Eq=r%6099%7Bst%7Cuv%7Dwz',
1928 href: 'http://a.b/%09bc%0Adr%0Def%20g%22hq%27j%3Ckl%3E?mn%5Cop%5Eq=r%6099%7Bst%7Cuv%7Dwz',
1929 },
1930 
1931 'http://a\r" \t\n<\'b:b@c\r\nd/e?f': {
1932 protocol: 'http:',
1933 slashes: true,
1934 auth: 'a" <\'b:b',
1935 host: 'cd',
1936 port: null,
1937 hostname: 'cd',
1938 hash: null,
1939 search: '?f',
1940 query: 'f',
1941 pathname: '/e',
1942 path: '/e?f',
1943 href: "http://a%22%20%3C'b:b@cd/e?f",
1944 },
1945 
1946 // Git urls used by npm
1947 'git+ssh://git@github.com:npm/npm': {
1948 protocol: 'git+ssh:',
1949 slashes: true,
1950 auth: 'git',
1951 host: 'github.com',
1952 port: null,
1953 hostname: 'github.com',
1954 hash: null,
1955 search: null,
1956 query: null,
1957 pathname: '/:npm/npm',
1958 path: '/:npm/npm',
1959 href: 'git+ssh://git@github.com/:npm/npm',
1960 },
1961 
1962 'https://*': {
1963 protocol: 'https:',
1964 slashes: true,
1965 auth: null,
1966 host: '*',
1967 port: null,
1968 hostname: '*',
1969 hash: null,
1970 search: null,
1971 query: null,
1972 pathname: '/',
1973 path: '/',
1974 href: 'https://*/',
1975 },
1976 
1977 // The following two URLs are the same, but they differ for a capital A.
1978 // Verify that the protocol is checked in a case-insensitive manner.
1979 'javascript:alert(1);a=\x27@white-listed.com\x27': {
1980 protocol: 'javascript:',
1981 slashes: null,
1982 auth: null,
1983 host: null,
1984 port: null,
1985 hostname: null,
1986 hash: null,
1987 search: null,
1988 query: null,
1989 pathname: "alert(1);a='@white-listed.com'",
1990 path: "alert(1);a='@white-listed.com'",
1991 href: "javascript:alert(1);a='@white-listed.com'",
1992 },
1993 
1994 'javAscript:alert(1);a=\x27@white-listed.com\x27': {
1995 protocol: 'javascript:',
1996 slashes: null,
1997 auth: null,
1998 host: null,
1999 port: null,
2000 hostname: null,
2001 hash: null,
2002 search: null,
2003 query: null,
2004 pathname: "alert(1);a='@white-listed.com'",
2005 path: "alert(1);a='@white-listed.com'",
2006 href: "javascript:alert(1);a='@white-listed.com'",
2007 },
2008 
2009 'ws://www.example.com': {
2010 protocol: 'ws:',
2011 slashes: true,
2012 hostname: 'www.example.com',
2013 host: 'www.example.com',
2014 pathname: '/',
2015 path: '/',
2016 href: 'ws://www.example.com/',
2017 },
2018 
2019 'wss://www.example.com': {
2020 protocol: 'wss:',
2021 slashes: true,
2022 hostname: 'www.example.com',
2023 host: 'www.example.com',
2024 pathname: '/',
2025 path: '/',
2026 href: 'wss://www.example.com/',
2027 },
2028 
2029 '//fhqwhgads@example.com/everybody-to-the-limit': {
2030 protocol: null,
2031 slashes: true,
2032 auth: 'fhqwhgads',
2033 host: 'example.com',
2034 port: null,
2035 hostname: 'example.com',
2036 hash: null,
2037 search: null,
2038 query: null,
2039 pathname: '/everybody-to-the-limit',
2040 path: '/everybody-to-the-limit',
2041 href: '//fhqwhgads@example.com/everybody-to-the-limit',
2042 },
2043 
2044 '//fhqwhgads@example.com/everybody#to-the-limit': {
2045 protocol: null,
2046 slashes: true,
2047 auth: 'fhqwhgads',
2048 host: 'example.com',
2049 port: null,
2050 hostname: 'example.com',
2051 hash: '#to-the-limit',
2052 search: null,
2053 query: null,
2054 pathname: '/everybody',
2055 path: '/everybody',
2056 href: '//fhqwhgads@example.com/everybody#to-the-limit',
2057 },
2058 
2059 '\bhttp://example.com/\b': {
2060 protocol: 'http:',
2061 slashes: true,
2062 auth: null,
2063 host: 'example.com',
2064 port: null,
2065 hostname: 'example.com',
2066 hash: null,
2067 search: null,
2068 query: null,
2069 pathname: '/',
2070 path: '/',
2071 href: 'http://example.com/',
2072 },
2073 
2074 'https://evil.com$.example.com': {
2075 protocol: 'https:',
2076 slashes: true,
2077 auth: null,
2078 host: 'evil.com$.example.com',
2079 port: null,
2080 hostname: 'evil.com$.example.com',
2081 hash: null,
2082 search: null,
2083 query: null,
2084 pathname: '/',
2085 path: '/',
2086 href: 'https://evil.com$.example.com/',
2087 },
2088 
2089 // Validate the output of hostname with commas.
2090 'x://0.0,1.1/': {
2091 protocol: 'x:',
2092 slashes: true,
2093 auth: null,
2094 host: '0.0,1.1',
2095 port: null,
2096 hostname: '0.0,1.1',
2097 hash: null,
2098 search: null,
2099 query: null,
2100 pathname: '/',
2101 path: '/',
2102 href: 'x://0.0,1.1/',
2103 },
2104 };
2105 
2106 // should parse and format
2107 {
2108 for (const u in parseTests) {
2109 let actual = url.parse(u);
2110 const spaced = url.parse(` \t ${u}\n\t`);
2111 let expected = Object.assign(new url.Url(), parseTests[u]);
2112 
2113 Object.keys(actual).forEach(function (i) {
2114 if (expected[i] === undefined && actual[i] === null) {
2115 expected[i] = null;
2116 }
2117 });
2118 
2119 assert.deepStrictEqual(
2120 actual,
2121 expected,
2122 `parsing ${u} and expected ${inspect(expected)} but got ${inspect(actual)}`
2123 );
2124 assert.deepStrictEqual(
2125 spaced,
2126 expected,
2127 `expected ${inspect(expected)}, got ${inspect(spaced)}`
2128 );
2129 
2130 expected = parseTests[u].href;
2131 actual = url.format(parseTests[u]);
2132 
2133 assert.strictEqual(
2134 actual,
2135 expected,
2136 `format(${u}) == ${u}\nactual:${actual}`
2137 );
2138 }
2139 }
2140 
2141 // parse result should equal new url.Url()
2142 {
2143 const parsed = url
2144 .parse('http://nodejs.org/')
2145 .resolveObject('jAvascript:alert(1);a=\x27@white-listed.com\x27');
2146 
2147 const expected = Object.assign(new url.Url(), {
2148 protocol: 'javascript:',
2149 slashes: null,
2150 auth: null,
2151 host: null,
2152 port: null,
2153 hostname: null,
2154 hash: null,
2155 search: null,
2156 query: null,
2157 pathname: "alert(1);a='@white-listed.com'",
2158 path: "alert(1);a='@white-listed.com'",
2159 href: "javascript:alert(1);a='@white-listed.com'",
2160 });
2161 
2162 assert.deepStrictEqual(parsed, expected);
2163 }
2164 },
2165};
2166 
2167// Ref: https://github.com/nodejs/node/blob/e92446536ed4e268c9eef6ae6f911e384c98eecf/test/parallel/test-url-parse-invalid-input.js
2168export const urlParseInvalidInput = {
2169 async test() {
2170 // https://github.com/joyent/node/issues/568
2171 [
2172 [undefined, 'undefined'],
2173 [null, 'object'],
2174 [true, 'boolean'],
2175 [false, 'boolean'],
2176 [0.0, 'number'],
2177 [0, 'number'],
2178 [[], 'object'],
2179 [{}, 'object'],
2180 [() => {}, 'function'],
2181 [Symbol('foo'), 'symbol'],
2182 ].forEach(([val, type]) => {
2183 assert.throws(
2184 () => {
2185 url.parse(val);
2186 },
2187 {
2188 code: 'ERR_INVALID_ARG_TYPE',
2189 name: 'TypeError',
2190 }
2191 );
2192 });
2193 
2194 assert.throws(
2195 () => {
2196 url.parse('http://%E0%A4%A@fail');
2197 },
2198 (e) => {
2199 // The error should be a URIError.
2200 if (!(e instanceof URIError)) return false;
2201 
2202 // The error should be from the JS engine and not from Node.js.
2203 // JS engine errors do not have the `code` property.
2204 return e.code === undefined;
2205 }
2206 );
2207 
2208 assert.throws(
2209 () => {
2210 url.parse('http://[127.0.0.1\x00c8763]:8000/');
2211 },
2212 { code: 'ERR_INVALID_URL', input: 'http://[127.0.0.1\x00c8763]:8000/' }
2213 );
2214 
2215 {
2216 // An array of Unicode code points whose Unicode NFKD contains a "bad
2217 // character".
2218 const badIDNA = (() => {
2219 const BAD_CHARS = '#%/:?@[\\]^|';
2220 const out = [];
2221 for (let i = 0x80; i < 0x110000; i++) {
2222 const cp = String.fromCodePoint(i);
2223 for (const badChar of BAD_CHARS) {
2224 if (cp.normalize('NFKD').includes(badChar)) {
2225 out.push(cp);
2226 }
2227 }
2228 }
2229 return out;
2230 })();
2231 
2232 // The generation logic above should at a minimum produce these two
2233 // characters.
2234 assert(badIDNA.includes('℀'));
2235 assert(badIDNA.includes('@'));
2236 
2237 for (const badCodePoint of badIDNA) {
2238 const badURL = `http://fail${badCodePoint}fail.com/`;
2239 assert.throws(
2240 () => {
2241 url.parse(badURL);
2242 },
2243 (e) => e.code === 'ERR_INVALID_URL',
2244 `parsing ${badURL}`
2245 );
2246 }
2247 
2248 assert.throws(
2249 () => {
2250 url.parse('http://\u00AD/bad.com/');
2251 },
2252 (e) => e.code === 'ERR_INVALID_URL',
2253 'parsing http://\u00AD/bad.com/'
2254 );
2255 }
2256 },
2257};