1package purell
2
3import (
4	"fmt"
5	"net/url"
6	"testing"
7	"unicode"
8)
9
10type testCase struct {
11	nm     string
12	src    string
13	flgs   NormalizationFlags
14	res    string
15	parsed bool
16}
17
18var (
19	cases = [...]*testCase{
20		{
21			"LowerScheme",
22			"HTTP://www.SRC.ca",
23			FlagLowercaseScheme,
24			"http://www.SRC.ca",
25			false,
26		},
27		{
28			"LowerScheme2",
29			"http://www.SRC.ca",
30			FlagLowercaseScheme,
31			"http://www.SRC.ca",
32			false,
33		},
34		{
35			"LowerHost",
36			"HTTP://www.SRC.ca/",
37			FlagLowercaseHost,
38			"http://www.src.ca/", // Since Go1.1, scheme is automatically lowercased
39			false,
40		},
41		{
42			"UpperEscapes",
43			`http://www.whatever.com/Some%aa%20Special%8Ecases/`,
44			FlagUppercaseEscapes,
45			"http://www.whatever.com/Some%AA%20Special%8Ecases/",
46			false,
47		},
48		{
49			"UnnecessaryEscapes",
50			`http://www.toto.com/%41%42%2E%44/%32%33%52%2D/%5f%7E`,
51			FlagDecodeUnnecessaryEscapes,
52			"http://www.toto.com/AB.D/23R-/_~",
53			false,
54		},
55		{
56			"RemoveDefaultPort",
57			"HTTP://www.SRC.ca:80/",
58			FlagRemoveDefaultPort,
59			"http://www.SRC.ca/", // Since Go1.1, scheme is automatically lowercased
60			false,
61		},
62		{
63			"RemoveDefaultPort2",
64			"HTTP://www.SRC.ca:80",
65			FlagRemoveDefaultPort,
66			"http://www.SRC.ca", // Since Go1.1, scheme is automatically lowercased
67			false,
68		},
69		{
70			"RemoveDefaultPort3",
71			"HTTP://www.SRC.ca:8080",
72			FlagRemoveDefaultPort,
73			"http://www.SRC.ca:8080", // Since Go1.1, scheme is automatically lowercased
74			false,
75		},
76		{
77			"Safe",
78			"HTTP://www.SRC.ca:80/to%1ato%8b%ee/OKnow%41%42%43%7e",
79			FlagsSafe,
80			"http://www.src.ca/to%1Ato%8B%EE/OKnowABC~",
81			false,
82		},
83		{
84			"BothLower",
85			"HTTP://www.SRC.ca:80/to%1ato%8b%ee/OKnow%41%42%43%7e",
86			FlagLowercaseHost | FlagLowercaseScheme,
87			"http://www.src.ca:80/to%1Ato%8B%EE/OKnowABC~",
88			false,
89		},
90		{
91			"RemoveTrailingSlash",
92			"HTTP://www.SRC.ca:80/",
93			FlagRemoveTrailingSlash,
94			"http://www.SRC.ca:80", // Since Go1.1, scheme is automatically lowercased
95			false,
96		},
97		{
98			"RemoveTrailingSlash2",
99			"HTTP://www.SRC.ca:80/toto/titi/",
100			FlagRemoveTrailingSlash,
101			"http://www.SRC.ca:80/toto/titi", // Since Go1.1, scheme is automatically lowercased
102			false,
103		},
104		{
105			"RemoveTrailingSlash3",
106			"HTTP://www.SRC.ca:80/toto/titi/fin/?a=1",
107			FlagRemoveTrailingSlash,
108			"http://www.SRC.ca:80/toto/titi/fin?a=1", // Since Go1.1, scheme is automatically lowercased
109			false,
110		},
111		{
112			"AddTrailingSlash",
113			"HTTP://www.SRC.ca:80",
114			FlagAddTrailingSlash,
115			"http://www.SRC.ca:80/", // Since Go1.1, scheme is automatically lowercased
116			false,
117		},
118		{
119			"AddTrailingSlash2",
120			"HTTP://www.SRC.ca:80/toto/titi.html",
121			FlagAddTrailingSlash,
122			"http://www.SRC.ca:80/toto/titi.html/", // Since Go1.1, scheme is automatically lowercased
123			false,
124		},
125		{
126			"AddTrailingSlash3",
127			"HTTP://www.SRC.ca:80/toto/titi/fin?a=1",
128			FlagAddTrailingSlash,
129			"http://www.SRC.ca:80/toto/titi/fin/?a=1", // Since Go1.1, scheme is automatically lowercased
130			false,
131		},
132		{
133			"RemoveDotSegments",
134			"HTTP://root/a/b/./../../c/",
135			FlagRemoveDotSegments,
136			"http://root/c/", // Since Go1.1, scheme is automatically lowercased
137			false,
138		},
139		{
140			"RemoveDotSegments2",
141			"HTTP://root/../a/b/./../c/../d",
142			FlagRemoveDotSegments,
143			"http://root/a/d", // Since Go1.1, scheme is automatically lowercased
144			false,
145		},
146		{
147			"UsuallySafe",
148			"HTTP://www.SRC.ca:80/to%1ato%8b%ee/./c/d/../OKnow%41%42%43%7e/?a=b#test",
149			FlagsUsuallySafeGreedy,
150			"http://www.src.ca/to%1Ato%8B%EE/c/OKnowABC~?a=b#test",
151			false,
152		},
153		{
154			"RemoveDirectoryIndex",
155			"HTTP://root/a/b/c/default.aspx",
156			FlagRemoveDirectoryIndex,
157			"http://root/a/b/c/", // Since Go1.1, scheme is automatically lowercased
158			false,
159		},
160		{
161			"RemoveDirectoryIndex2",
162			"HTTP://root/a/b/c/default#a=b",
163			FlagRemoveDirectoryIndex,
164			"http://root/a/b/c/default#a=b", // Since Go1.1, scheme is automatically lowercased
165			false,
166		},
167		{
168			"RemoveFragment",
169			"HTTP://root/a/b/c/default#toto=tata",
170			FlagRemoveFragment,
171			"http://root/a/b/c/default", // Since Go1.1, scheme is automatically lowercased
172			false,
173		},
174		{
175			"ForceHTTP",
176			"https://root/a/b/c/default#toto=tata",
177			FlagForceHTTP,
178			"http://root/a/b/c/default#toto=tata",
179			false,
180		},
181		{
182			"RemoveDuplicateSlashes",
183			"https://root/a//b///c////default#toto=tata",
184			FlagRemoveDuplicateSlashes,
185			"https://root/a/b/c/default#toto=tata",
186			false,
187		},
188		{
189			"RemoveDuplicateSlashes2",
190			"https://root//a//b///c////default#toto=tata",
191			FlagRemoveDuplicateSlashes,
192			"https://root/a/b/c/default#toto=tata",
193			false,
194		},
195		{
196			"RemoveWWW",
197			"https://www.root/a/b/c/",
198			FlagRemoveWWW,
199			"https://root/a/b/c/",
200			false,
201		},
202		{
203			"RemoveWWW2",
204			"https://WwW.Root/a/b/c/",
205			FlagRemoveWWW,
206			"https://Root/a/b/c/",
207			false,
208		},
209		{
210			"AddWWW",
211			"https://Root/a/b/c/",
212			FlagAddWWW,
213			"https://www.Root/a/b/c/",
214			false,
215		},
216		{
217			"SortQuery",
218			"http://root/toto/?b=4&a=1&c=3&b=2&a=5",
219			FlagSortQuery,
220			"http://root/toto/?a=1&a=5&b=2&b=4&c=3",
221			false,
222		},
223		{
224			"RemoveEmptyQuerySeparator",
225			"http://root/toto/?",
226			FlagRemoveEmptyQuerySeparator,
227			"http://root/toto/",
228			false,
229		},
230		{
231			"Unsafe",
232			"HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid",
233			FlagsUnsafeGreedy,
234			"http://root.com/toto/tE%1F/a/c?a=4&w=1&w=2&z=3",
235			false,
236		},
237		{
238			"Safe2",
239			"HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid",
240			FlagsSafe,
241			"https://www.root.com/toto/tE%1F///a/./b/../c/?z=3&w=2&a=4&w=1#invalid",
242			false,
243		},
244		{
245			"UsuallySafe2",
246			"HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid",
247			FlagsUsuallySafeGreedy,
248			"https://www.root.com/toto/tE%1F///a/c?z=3&w=2&a=4&w=1#invalid",
249			false,
250		},
251		{
252			"AddTrailingSlashBug",
253			"http://src.ca/",
254			FlagsAllNonGreedy,
255			"http://www.src.ca/",
256			false,
257		},
258		{
259			"SourceModified",
260			"HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid",
261			FlagsUnsafeGreedy,
262			"http://root.com/toto/tE%1F/a/c?a=4&w=1&w=2&z=3",
263			true,
264		},
265		{
266			"IPv6-1",
267			"http://[2001:db8:1f70::999:de8:7648:6e8]/test",
268			FlagsSafe | FlagRemoveDotSegments,
269			"http://[2001:db8:1f70::999:de8:7648:6e8]/test",
270			false,
271		},
272		{
273			"IPv6-2",
274			"http://[::ffff:192.168.1.1]/test",
275			FlagsSafe | FlagRemoveDotSegments,
276			"http://[::ffff:192.168.1.1]/test",
277			false,
278		},
279		{
280			"IPv6-3",
281			"http://[::ffff:192.168.1.1]:80/test",
282			FlagsSafe | FlagRemoveDotSegments,
283			"http://[::ffff:192.168.1.1]/test",
284			false,
285		},
286		{
287			"IPv6-4",
288			"htTps://[::fFff:192.168.1.1]:443/test",
289			FlagsSafe | FlagRemoveDotSegments,
290			"https://[::ffff:192.168.1.1]/test",
291			false,
292		},
293		{
294			"FTP",
295			"ftp://user:pass@ftp.foo.net/foo/bar",
296			FlagsSafe | FlagRemoveDotSegments,
297			"ftp://user:pass@ftp.foo.net/foo/bar",
298			false,
299		},
300		{
301			"Standard-1",
302			"http://www.foo.com:80/foo",
303			FlagsSafe | FlagRemoveDotSegments,
304			"http://www.foo.com/foo",
305			false,
306		},
307		{
308			"Standard-2",
309			"http://www.foo.com:8000/foo",
310			FlagsSafe | FlagRemoveDotSegments,
311			"http://www.foo.com:8000/foo",
312			false,
313		},
314		{
315			"Standard-3",
316			"http://www.foo.com/%7ebar",
317			FlagsSafe | FlagRemoveDotSegments,
318			"http://www.foo.com/~bar",
319			false,
320		},
321		{
322			"Standard-4",
323			"http://www.foo.com/%7Ebar",
324			FlagsSafe | FlagRemoveDotSegments,
325			"http://www.foo.com/~bar",
326			false,
327		},
328		{
329			"Standard-5",
330			"http://USER:pass@www.Example.COM/foo/bar",
331			FlagsSafe | FlagRemoveDotSegments,
332			"http://USER:pass@www.example.com/foo/bar",
333			false,
334		},
335		{
336			"Standard-6",
337			"http://test.example/?a=%26&b=1",
338			FlagsSafe | FlagRemoveDotSegments,
339			"http://test.example/?a=%26&b=1",
340			false,
341		},
342		{
343			"Standard-7",
344			"http://test.example/%25/?p=%20val%20%25",
345			FlagsSafe | FlagRemoveDotSegments,
346			"http://test.example/%25/?p=%20val%20%25",
347			false,
348		},
349		{
350			"Standard-8",
351			"http://test.example/path/with a%20space+/",
352			FlagsSafe | FlagRemoveDotSegments,
353			"http://test.example/path/with%20a%20space+/",
354			false,
355		},
356		{
357			"Standard-9",
358			"http://test.example/?",
359			FlagsSafe | FlagRemoveDotSegments,
360			"http://test.example/",
361			false,
362		},
363		{
364			"Standard-10",
365			"http://a.COM/path/?b&a",
366			FlagsSafe | FlagRemoveDotSegments,
367			"http://a.com/path/?b&a",
368			false,
369		},
370		{
371			"StandardCasesAddTrailingSlash",
372			"http://test.example?",
373			FlagsSafe | FlagAddTrailingSlash,
374			"http://test.example/",
375			false,
376		},
377		{
378			"OctalIP-1",
379			"http://0123.011.0.4/",
380			FlagsSafe | FlagDecodeOctalHost,
381			"http://0123.011.0.4/",
382			false,
383		},
384		{
385			"OctalIP-2",
386			"http://0102.0146.07.0223/",
387			FlagsSafe | FlagDecodeOctalHost,
388			"http://66.102.7.147/",
389			false,
390		},
391		{
392			"OctalIP-3",
393			"http://0102.0146.07.0223.:23/",
394			FlagsSafe | FlagDecodeOctalHost,
395			"http://66.102.7.147.:23/",
396			false,
397		},
398		{
399			"OctalIP-4",
400			"http://USER:pass@0102.0146.07.0223../",
401			FlagsSafe | FlagDecodeOctalHost,
402			"http://USER:pass@66.102.7.147../",
403			false,
404		},
405		{
406			"DWORDIP-1",
407			"http://123.1113982867/",
408			FlagsSafe | FlagDecodeDWORDHost,
409			"http://123.1113982867/",
410			false,
411		},
412		{
413			"DWORDIP-2",
414			"http://1113982867/",
415			FlagsSafe | FlagDecodeDWORDHost,
416			"http://66.102.7.147/",
417			false,
418		},
419		{
420			"DWORDIP-3",
421			"http://1113982867.:23/",
422			FlagsSafe | FlagDecodeDWORDHost,
423			"http://66.102.7.147.:23/",
424			false,
425		},
426		{
427			"DWORDIP-4",
428			"http://USER:pass@1113982867../",
429			FlagsSafe | FlagDecodeDWORDHost,
430			"http://USER:pass@66.102.7.147../",
431			false,
432		},
433		{
434			"HexIP-1",
435			"http://0x123.1113982867/",
436			FlagsSafe | FlagDecodeHexHost,
437			"http://0x123.1113982867/",
438			false,
439		},
440		{
441			"HexIP-2",
442			"http://0x42660793/",
443			FlagsSafe | FlagDecodeHexHost,
444			"http://66.102.7.147/",
445			false,
446		},
447		{
448			"HexIP-3",
449			"http://0x42660793.:23/",
450			FlagsSafe | FlagDecodeHexHost,
451			"http://66.102.7.147.:23/",
452			false,
453		},
454		{
455			"HexIP-4",
456			"http://USER:pass@0x42660793../",
457			FlagsSafe | FlagDecodeHexHost,
458			"http://USER:pass@66.102.7.147../",
459			false,
460		},
461		{
462			"UnnecessaryHostDots-1",
463			"http://.www.foo.com../foo/bar.html",
464			FlagsSafe | FlagRemoveUnnecessaryHostDots,
465			"http://www.foo.com/foo/bar.html",
466			false,
467		},
468		{
469			"UnnecessaryHostDots-2",
470			"http://www.foo.com./foo/bar.html",
471			FlagsSafe | FlagRemoveUnnecessaryHostDots,
472			"http://www.foo.com/foo/bar.html",
473			false,
474		},
475		{
476			"UnnecessaryHostDots-3",
477			"http://www.foo.com.:81/foo",
478			FlagsSafe | FlagRemoveUnnecessaryHostDots,
479			"http://www.foo.com:81/foo",
480			false,
481		},
482		{
483			"UnnecessaryHostDots-4",
484			"http://www.example.com./",
485			FlagsSafe | FlagRemoveUnnecessaryHostDots,
486			"http://www.example.com/",
487			false,
488		},
489		{
490			"EmptyPort-1",
491			"http://www.thedraymin.co.uk:/main/?p=308",
492			FlagsSafe | FlagRemoveEmptyPortSeparator,
493			"http://www.thedraymin.co.uk/main/?p=308",
494			false,
495		},
496		{
497			"EmptyPort-2",
498			"http://www.src.ca:",
499			FlagsSafe | FlagRemoveEmptyPortSeparator,
500			"http://www.src.ca",
501			false,
502		},
503		{
504			"Slashes-1",
505			"http://test.example/foo/bar/.",
506			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
507			"http://test.example/foo/bar/",
508			false,
509		},
510		{
511			"Slashes-2",
512			"http://test.example/foo/bar/./",
513			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
514			"http://test.example/foo/bar/",
515			false,
516		},
517		{
518			"Slashes-3",
519			"http://test.example/foo/bar/..",
520			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
521			"http://test.example/foo/",
522			false,
523		},
524		{
525			"Slashes-4",
526			"http://test.example/foo/bar/../",
527			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
528			"http://test.example/foo/",
529			false,
530		},
531		{
532			"Slashes-5",
533			"http://test.example/foo/bar/../baz",
534			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
535			"http://test.example/foo/baz",
536			false,
537		},
538		{
539			"Slashes-6",
540			"http://test.example/foo/bar/../..",
541			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
542			"http://test.example/",
543			false,
544		},
545		{
546			"Slashes-7",
547			"http://test.example/foo/bar/../../",
548			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
549			"http://test.example/",
550			false,
551		},
552		{
553			"Slashes-8",
554			"http://test.example/foo/bar/../../baz",
555			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
556			"http://test.example/baz",
557			false,
558		},
559		{
560			"Slashes-9",
561			"http://test.example/foo/bar/../../../baz",
562			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
563			"http://test.example/baz",
564			false,
565		},
566		{
567			"Slashes-10",
568			"http://test.example/foo/bar/../../../../baz",
569			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
570			"http://test.example/baz",
571			false,
572		},
573		{
574			"Slashes-11",
575			"http://test.example/./foo",
576			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
577			"http://test.example/foo",
578			false,
579		},
580		{
581			"Slashes-12",
582			"http://test.example/../foo",
583			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
584			"http://test.example/foo",
585			false,
586		},
587		{
588			"Slashes-13",
589			"http://test.example/foo.",
590			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
591			"http://test.example/foo.",
592			false,
593		},
594		{
595			"Slashes-14",
596			"http://test.example/.foo",
597			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
598			"http://test.example/.foo",
599			false,
600		},
601		{
602			"Slashes-15",
603			"http://test.example/foo..",
604			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
605			"http://test.example/foo..",
606			false,
607		},
608		{
609			"Slashes-16",
610			"http://test.example/..foo",
611			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
612			"http://test.example/..foo",
613			false,
614		},
615		{
616			"Slashes-17",
617			"http://test.example/./../foo",
618			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
619			"http://test.example/foo",
620			false,
621		},
622		{
623			"Slashes-18",
624			"http://test.example/./foo/.",
625			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
626			"http://test.example/foo/",
627			false,
628		},
629		{
630			"Slashes-19",
631			"http://test.example/foo/./bar",
632			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
633			"http://test.example/foo/bar",
634			false,
635		},
636		{
637			"Slashes-20",
638			"http://test.example/foo/../bar",
639			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
640			"http://test.example/bar",
641			false,
642		},
643		{
644			"Slashes-21",
645			"http://test.example/foo//",
646			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
647			"http://test.example/foo/",
648			false,
649		},
650		{
651			"Slashes-22",
652			"http://test.example/foo///bar//",
653			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
654			"http://test.example/foo/bar/",
655			false,
656		},
657		{
658			"Relative",
659			"foo/bar",
660			FlagsAllGreedy,
661			"foo/bar",
662			false,
663		},
664		{
665			"Relative-1",
666			"./../foo",
667			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
668			"foo",
669			false,
670		},
671		{
672			"Relative-2",
673			"./foo/bar/../baz/../bang/..",
674			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
675			"foo/",
676			false,
677		},
678		{
679			"Relative-3",
680			"foo///bar//",
681			FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
682			"foo/bar/",
683			false,
684		},
685		{
686			"Relative-4",
687			"www.youtube.com",
688			FlagsUsuallySafeGreedy,
689			"www.youtube.com",
690			false,
691		},
692		{
693			"Issue-#24",
694			"///foo///bar///",
695			FlagRemoveDuplicateSlashes | FlagRemoveTrailingSlash,
696			"/foo/bar",
697			false,
698		},
699		/*&testCase{
700			"UrlNorm-5",
701			"http://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%A3%E3%82%BF%E3%83%94%E3%83%A9%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%91%E3%83%B3",
702			FlagsSafe | FlagRemoveDotSegments,
703			"http://ja.wikipedia.org/wiki/\xe3\x82\xad\xe3\x83\xa3\xe3\x82\xbf\xe3\x83\x94\xe3\x83\xa9\xe3\x83\xbc\xe3\x82\xb8\xe3\x83\xa3\xe3\x83\x91\xe3\x83\xb3",
704			false,
705		},
706		&testCase{
707			"UrlNorm-1",
708			"http://test.example/?a=%e3%82%82%26",
709			FlagsAllGreedy,
710			"http://test.example/?a=\xe3\x82\x82%26",
711			false,
712		},*/
713	}
714)
715
716func TestRunner(t *testing.T) {
717	for _, tc := range cases {
718		runCase(tc, t)
719	}
720}
721
722func runCase(tc *testCase, t *testing.T) {
723	t.Logf("running %s...", tc.nm)
724	if tc.parsed {
725		u, e := url.Parse(tc.src)
726		if e != nil {
727			t.Errorf("%s - FAIL : %s", tc.nm, e)
728			return
729		} else {
730			NormalizeURL(u, tc.flgs)
731			if s := u.String(); s != tc.res {
732				t.Errorf("%s - FAIL expected '%s', got '%s'", tc.nm, tc.res, s)
733			}
734		}
735	} else {
736		if s, e := NormalizeURLString(tc.src, tc.flgs); e != nil {
737			t.Errorf("%s - FAIL : %s", tc.nm, e)
738		} else if s != tc.res {
739			t.Errorf("%s - FAIL expected '%s', got '%s'", tc.nm, tc.res, s)
740		}
741	}
742}
743
744func TestDecodeUnnecessaryEscapesAll(t *testing.T) {
745	var url = "http://host/"
746
747	for i := 0; i < 256; i++ {
748		url += fmt.Sprintf("%%%02x", i)
749	}
750	s, err := NormalizeURLString(url, FlagDecodeUnnecessaryEscapes)
751	if err != nil {
752		t.Fatalf("parse error: %s", err)
753	}
754
755	const want = "http://host/%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%20!%22%23$%25&'()*+,-./0123456789:;%3C=%3E%3F@ABCDEFGHIJKLMNOPQRSTUVWXYZ[%5C]%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D~%7F%80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF"
756	if s != want {
757		t.Errorf("DecodeUnnecessaryEscapesAll:\nwant\n%s\ngot\n%s", want, s)
758	}
759}
760
761func TestEncodeNecessaryEscapesAll(t *testing.T) {
762	const base = "http://host/"
763	var path []byte
764
765	for i := 0; i < 256; i++ {
766		// Since go1.12, url.Parse fails if the raw URL contains ASCII control characters,
767		// meaning anything < 0x20 and 0x7f (DEL), so do not add those bytes to the constructed url.
768		// See https://github.com/PuerkitoBio/purell/issues/28
769		if i != 0x25 && !unicode.IsControl(rune(i)) {
770			path = append(path, byte(i))
771		}
772	}
773	s, err := NormalizeURLString(base+string(path), FlagEncodeNecessaryEscapes)
774	if err != nil {
775		t.Fatalf("parse error: %s", err)
776	}
777
778	const want = "http://host/%20!%22#$&'()*+,-./0123456789:;%3C=%3E?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[%5C]%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D~%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF"
779	if s != want {
780		t.Errorf("EncodeNecessaryEscapesAll:\nwant\n%s\ngot\n%s", want, s)
781	}
782}
783