@@ -88,7 +88,6 @@ public void Ctor_Uri_Null()
8888 [ InlineData ( "http" , "host" , "http" , "host" ) ]
8989 [ InlineData ( "HTTP" , "host" , "http" , "host" ) ]
9090 [ InlineData ( "http" , "[::1]" , "http" , "[::1]" ) ]
91- [ InlineData ( "https" , "::1]" , "https" , "[::1]]" ) ]
9291 [ InlineData ( "http" , "::1" , "http" , "[::1]" ) ]
9392 [ InlineData ( "http1:http2" , "host" , "http1" , "host" ) ]
9493 [ InlineData ( "http" , "" , "http" , "" ) ]
@@ -107,7 +106,6 @@ public void Ctor_String_String(string? schemeName, string? hostName, string expe
107106 [ InlineData ( "http" , "host" , 0 , "http" , "host" ) ]
108107 [ InlineData ( "HTTP" , "host" , 20 , "http" , "host" ) ]
109108 [ InlineData ( "http" , "[::1]" , 40 , "http" , "[::1]" ) ]
110- [ InlineData ( "https" , "::1]" , 60 , "https" , "[::1]]" ) ]
111109 [ InlineData ( "http" , "::1" , 80 , "http" , "[::1]" ) ]
112110 [ InlineData ( "http1:http2" , "host" , 100 , "http1" , "host" ) ]
113111 [ InlineData ( "http" , "" , 120 , "http" , "" ) ]
@@ -126,7 +124,6 @@ public void Ctor_String_String_Int(string? scheme, string? host, int port, strin
126124 [ InlineData ( "http" , "host" , 0 , "/path" , "http" , "host" , "/path" ) ]
127125 [ InlineData ( "HTTP" , "host" , 20 , "/path1/path2" , "http" , "host" , "/path1/path2" ) ]
128126 [ InlineData ( "http" , "[::1]" , 40 , "/" , "http" , "[::1]" , "/" ) ]
129- [ InlineData ( "https" , "::1]" , 60 , "/path1/" , "https" , "[::1]]" , "/path1/" ) ]
130127 [ InlineData ( "http" , "::1" , 80 , null , "http" , "[::1]" , "/" ) ]
131128 [ InlineData ( "http1:http2" , "host" , 100 , "path1" , "http1" , "host" , "path1" ) ]
132129 [ InlineData ( "http" , "" , 120 , "path1/path2" , "http" , "" , "path1/path2" ) ]
@@ -145,7 +142,6 @@ public void Ctor_String_String_Int_String(string? schemeName, string? hostName,
145142 [ InlineData ( "http" , "host" , 0 , "/path" , "?query#fragment" , "http" , "host" , "/path" , "?query" , "#fragment" ) ]
146143 [ InlineData ( "HTTP" , "host" , 20 , "/path1/path2" , "?query&query2=value#fragment" , "http" , "host" , "/path1/path2" , "?query&query2=value" , "#fragment" ) ]
147144 [ InlineData ( "http" , "[::1]" , 40 , "/" , "#fragment?query" , "http" , "[::1]" , "/" , "" , "#fragment?query" ) ]
148- [ InlineData ( "https" , "::1]" , 60 , "/path1/" , "?query" , "https" , "[::1]]" , "/path1/" , "?query" , "" ) ]
149145 [ InlineData ( "http" , "::1" , 80 , null , "#fragment" , "http" , "[::1]" , "/" , "" , "#fragment" ) ]
150146 [ InlineData ( "http" , "" , 120 , "path1/path2" , "?#" , "http" , "" , "path1/path2" , "" , "" ) ]
151147 [ InlineData ( "" , "host" , 140 , "path1/path2/path3/" , "?" , "" , "host" , "path1/path2/path3/" , "" , "" ) ]
@@ -368,6 +364,7 @@ public static IEnumerable<object[]> ToString_TestData()
368364 yield return new object [ ] { new UriBuilder ( ) { Host = "host" , Query = "query" } , "http://host/?query" } ;
369365 yield return new object [ ] { new UriBuilder ( ) { Host = "host" , Fragment = "fragment" } , "http://host/#fragment" } ;
370366 yield return new object [ ] { new UriBuilder ( ) { Host = "host" , Query = "query" , Fragment = "fragment" } , "http://host/?query#fragment" } ;
367+ yield return new object [ ] { new UriBuilder ( ) { Host = "::%foo" } , "http://[::%foo]/" } ;
371368 }
372369
373370 [ Theory ]
@@ -401,6 +398,51 @@ public void ToString_EncodingUserInfo(string username, string password, string e
401398 Assert . Equal ( password , uriBuilder . Password ) ;
402399 }
403400
401+ public static IEnumerable < object [ ] > InvalidHostStrings_TestData ( )
402+ {
403+ // Bool indicates whether the exception is expected to be thrown by the Host setter (true) or when creating the Uri (false)
404+
405+ // Presence of ':' is treated as a likely IPv6 address and the input is transformed into [host:80], which is invalid
406+ yield return [ "host:" , false ] ;
407+ yield return [ "host:80" , false ] ;
408+
409+ // Trailing space after IPv6 address
410+ yield return [ "[::] " , true ] ;
411+
412+ // Invalid characters escape into other Uri components
413+ yield return [ "host/path" , true ] ;
414+ yield return [ "host?query" , true ] ;
415+ yield return [ "host#fragment" , true ] ;
416+ yield return [ "user@host" , true ] ;
417+
418+ yield return [ "[::]/path" , true ] ;
419+ yield return [ "::]/path" , true ] ;
420+ yield return [ "::/path" , true ] ;
421+ yield return [ "::?query" , true ] ;
422+ yield return [ "::#fragment" , true ] ;
423+ yield return [ "[0::%/64]/path" , true ] ;
424+
425+ yield return [ "[host]" , true ] ;
426+ yield return [ "[127.0.0.1]" , true ] ;
427+ }
428+
429+ [ Theory ]
430+ [ MemberData ( nameof ( InvalidHostStrings_TestData ) ) ]
431+ public void Host_Set_InvalidHostStrings_ThrowsArgumentException ( string invalidHost , bool caughtByHostSetter )
432+ {
433+ var builder = new UriBuilder ( ) ;
434+
435+ if ( caughtByHostSetter )
436+ {
437+ AssertExtensions . Throws < ArgumentException > ( "value" , ( ) => builder . Host = invalidHost ) ;
438+ }
439+ else
440+ {
441+ builder . Host = invalidHost ;
442+ Assert . Throws < UriFormatException > ( ( ) => builder . Uri ) ;
443+ }
444+ }
445+
404446 private static void VerifyUriBuilder ( UriBuilder uriBuilder , string scheme , string userName , string password , string host , int port , string path , string query , string fragment )
405447 {
406448 Assert . Equal ( scheme , uriBuilder . Scheme ) ;
0 commit comments