@@ -126,6 +126,44 @@ test('hostname normalization decodes only current unreserved escapes', (t) => {
126126 t . end ( )
127127} )
128128
129+ test ( 'scheme-relative hostname normalization folds decoded ASCII case' , ( t ) => {
130+ const encodedHost = '//%41.com'
131+ const normalizedHost = fastURI . normalize ( encodedHost )
132+ const encodedMetadata = '//%4Detadata.internal/private'
133+ const literalMetadata = '//metadata.internal/private'
134+
135+ t . equal ( fastURI . parse ( encodedHost ) . host , 'a.com' , 'parse folds an encoded uppercase host letter' )
136+ t . equal ( normalizedHost , '//a.com' , 'normalize emits a lowercase host' )
137+ t . equal ( fastURI . normalize ( normalizedHost ) , normalizedHost , 'host normalization is idempotent' )
138+ t . equal ( fastURI . equal ( encodedHost , '//a.com' ) , true , 'encoded and literal host spellings compare equal' )
139+ t . equal ( fastURI . equal ( encodedMetadata , literalMetadata ) , true , 'encoded metadata host compares equal' )
140+ t . equal (
141+ fastURI . resolve ( 'x://trusted.example/' , encodedMetadata ) ,
142+ fastURI . resolve ( 'x://trusted.example/' , literalMetadata ) ,
143+ 'encoded and literal metadata hosts resolve identically'
144+ )
145+ t . equal (
146+ fastURI . normalize ( '//example.com%2fpath' ) ,
147+ '//example.com%2Fpath' ,
148+ 'reserved host escapes remain encoded with uppercase hex'
149+ )
150+ t . equal ( fastURI . normalize ( '//%2541.com' ) , '//%2541.com' , 'nested host escapes are not decoded twice' )
151+ t . equal ( fastURI . equal ( '//User@%41.com/path' , '//[email protected] /path' ) , false , 'userinfo remains case-sensitive' ) 152+ t . equal ( fastURI . equal ( '//%41.com/Path' , '//a.com/path' ) , false , 'path remains case-sensitive' )
153+ t . equal ( fastURI . equal ( '//%41.com/?Token=Value' , '//a.com/?token=value' ) , false , 'query remains case-sensitive' )
154+ t . end ( )
155+ } )
156+
157+ test ( 'host normalization applies to schemes that skip path normalization' , ( t ) => {
158+ const encodedHost = 'mailto://%41.com'
159+ const literalHost = 'mailto://a.com'
160+
161+ t . equal ( fastURI . parse ( encodedHost ) . host , fastURI . parse ( literalHost ) . host , 'parse results have consistent hosts' )
162+ t . equal ( fastURI . normalize ( encodedHost ) , fastURI . normalize ( literalHost ) , 'normalize results are consistent' )
163+ t . equal ( fastURI . equal ( encodedHost , literalHost ) , true , 'encoded and literal hosts compare equal' )
164+ t . end ( )
165+ } )
166+
129167test ( 'host conversion failures are not treated as comparable URLs' , ( t ) => {
130168 const malformedHost = 'http://trusted.test%2540evil.test/'
131169
0 commit comments