@@ -474,9 +474,8 @@ impl<'a> Parser<'a> {
474474 let host = HostInternal :: None ;
475475 let port = None ;
476476 let remaining = if let Some ( input) = input. split_prefix ( '/' ) {
477- let path_start = self . serialization . len ( ) ;
478477 self . serialization . push ( '/' ) ;
479- self . parse_path ( scheme_type, & mut false , path_start, input)
478+ self . parse_path ( scheme_type, & mut false , path_start as usize , input)
480479 } else {
481480 self . parse_cannot_be_a_base_path ( input)
482481 } ;
@@ -1354,9 +1353,50 @@ impl<'a> Parser<'a> {
13541353 host_end : u32 ,
13551354 host : HostInternal ,
13561355 port : Option < u16 > ,
1357- path_start : u32 ,
1356+ mut path_start : u32 ,
13581357 remaining : Input < ' _ > ,
13591358 ) -> ParseResult < Url > {
1359+ // Special case for anarchist URL's with a leading empty path segment
1360+ // This prevents web+demo:/.//not-a-host/ or web+demo:/path/..//not-a-host/,
1361+ // when parsed and then serialized, from ending up as web+demo://not-a-host/
1362+ // (they end up as web+demo:/.//not-a-host/).
1363+ //
1364+ // If url’s host is null, url does not have an opaque path,
1365+ // url’s path’s size is greater than 1, and url’s path[0] is the empty string,
1366+ // then append U+002F (/) followed by U+002E (.) to output.
1367+ let scheme_end_as_usize = scheme_end as usize ;
1368+ let path_start_as_usize = path_start as usize ;
1369+ if path_start_as_usize == scheme_end_as_usize + 1 {
1370+ // Anarchist URL
1371+ if self . serialization [ path_start_as_usize..] . starts_with ( "//" ) {
1372+ // Case 1: The base URL did not have an empty path segment, but the resulting one does
1373+ // Insert the "/." prefix
1374+ self . serialization . insert_str ( path_start_as_usize, "/." ) ;
1375+ path_start += 2 ;
1376+ }
1377+ assert ! ( !self . serialization[ scheme_end_as_usize..] . starts_with( "://" ) ) ;
1378+ } else if path_start_as_usize == scheme_end_as_usize + 3
1379+ && & self . serialization [ scheme_end_as_usize..path_start_as_usize] == ":/."
1380+ {
1381+ // Anarchist URL with leading empty path segment
1382+ // The base URL has a "/." between the host and the path
1383+ assert_eq ! ( self . serialization. as_bytes( ) [ path_start_as_usize] , b'/' ) ;
1384+ if self
1385+ . serialization
1386+ . as_bytes ( )
1387+ . get ( path_start_as_usize + 1 )
1388+ . copied ( )
1389+ != Some ( b'/' )
1390+ {
1391+ // Case 2: The base URL had an empty path segment, but the resulting one does not
1392+ // Remove the "/." prefix
1393+ self . serialization
1394+ . replace_range ( scheme_end_as_usize..path_start_as_usize, ":" ) ;
1395+ path_start -= 2 ;
1396+ }
1397+ assert ! ( !self . serialization[ scheme_end_as_usize..] . starts_with( "://" ) ) ;
1398+ }
1399+
13601400 let ( query_start, fragment_start) =
13611401 self . parse_query_and_fragment ( scheme_type, scheme_end, remaining) ?;
13621402 Ok ( Url {
0 commit comments