1'use strict';
2const common = require('../common');
3const assert = require('assert');
4const url = require('url');
5
6if (!common.hasIntl)
7  common.skip('missing Intl');
8
9// Formatting tests to verify that it'll format slightly wonky content to a
10// valid URL.
11const 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};
267for (const u in formatTests) {
268  const expect = formatTests[u].href;
269  delete formatTests[u].href;
270  const actual = url.format(u);
271  const actualObj = url.format(formatTests[u]);
272  assert.strictEqual(actual, expect,
273                     `wonky format(${u}) == ${expect}\nactual:${actual}`);
274  assert.strictEqual(actualObj, expect,
275                     `wonky format(${JSON.stringify(formatTests[u])}) == ${
276                       expect}\nactual: ${actualObj}`);
277}
278