@@ -156,7 +156,7 @@ impl fmt::Display for SyntaxViolation {
156
156
}
157
157
}
158
158
159
- #[ derive( Copy , Clone ) ]
159
+ #[ derive( Copy , Clone , PartialEq ) ]
160
160
pub enum SchemeType {
161
161
File ,
162
162
SpecialNotFile ,
@@ -852,11 +852,16 @@ impl<'a> Parser<'a> {
852
852
self . serialization . push ( '/' ) ;
853
853
self . serialization . push ( '/' ) ;
854
854
// authority state
855
+ let before_authority = self . serialization . len ( ) ;
855
856
let ( username_end, remaining) = self . parse_userinfo ( input, scheme_type) ?;
857
+ let has_authority = before_authority != self . serialization . len ( ) ;
856
858
// host state
857
859
let host_start = to_u32 ( self . serialization . len ( ) ) ?;
858
860
let ( host_end, host, port, remaining) =
859
861
self . parse_host_and_port ( remaining, scheme_end, scheme_type) ?;
862
+ if host == HostInternal :: None && has_authority {
863
+ return Err ( ParseError :: EmptyHost ) ;
864
+ }
860
865
// path state
861
866
let path_start = to_u32 ( self . serialization . len ( ) ) ?;
862
867
let remaining = self . parse_path_start ( scheme_type, & mut true , remaining) ;
@@ -900,7 +905,18 @@ impl<'a> Parser<'a> {
900
905
}
901
906
let ( mut userinfo_char_count, remaining) = match last_at {
902
907
None => return Ok ( ( to_u32 ( self . serialization . len ( ) ) ?, input) ) ,
903
- Some ( ( 0 , remaining) ) => return Ok ( ( to_u32 ( self . serialization . len ( ) ) ?, remaining) ) ,
908
+ Some ( ( 0 , remaining) ) => {
909
+ // Otherwise, if one of the following is true
910
+ // c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
911
+ // url is special and c is U+005C (\)
912
+ // If @ flag is set and buffer is the empty string, validation error, return failure.
913
+ if let ( Some ( c) , _) = remaining. split_first ( ) {
914
+ if c == '/' || c == '?' || c == '#' || scheme_type. is_special ( ) && c == '\\' {
915
+ return Err ( ParseError :: EmptyHost ) ;
916
+ }
917
+ }
918
+ return Ok ( ( to_u32 ( self . serialization . len ( ) ) ?, remaining) ) ;
919
+ }
904
920
Some ( x) => x,
905
921
} ;
906
922
@@ -946,6 +962,18 @@ impl<'a> Parser<'a> {
946
962
let ( host, remaining) = Parser :: parse_host ( input, scheme_type) ?;
947
963
write ! ( & mut self . serialization, "{}" , host) . unwrap ( ) ;
948
964
let host_end = to_u32 ( self . serialization . len ( ) ) ?;
965
+ if let Host :: Domain ( h) = & host {
966
+ if h. is_empty ( ) {
967
+ // Port with an empty host
968
+ if remaining. starts_with ( ":" ) {
969
+ return Err ( ParseError :: EmptyHost ) ;
970
+ }
971
+ if scheme_type. is_special ( ) {
972
+ return Err ( ParseError :: EmptyHost ) ;
973
+ }
974
+ }
975
+ } ;
976
+
949
977
let ( port, remaining) = if let Some ( remaining) = remaining. split_prefix ( ':' ) {
950
978
let scheme = || default_port ( & self . serialization [ ..scheme_end as usize ] ) ;
951
979
Parser :: parse_port ( remaining, scheme, self . context ) ?
@@ -962,6 +990,9 @@ impl<'a> Parser<'a> {
962
990
mut input : Input ,
963
991
scheme_type : SchemeType ,
964
992
) -> ParseResult < ( Host < String > , Input ) > {
993
+ if scheme_type. is_file ( ) {
994
+ return Parser :: get_file_host ( input) ;
995
+ }
965
996
// Undo the Input abstraction here to avoid allocating in the common case
966
997
// where the host part of the input does not contain any tab or newline
967
998
let input_str = input. chars . as_str ( ) ;
@@ -1012,10 +1043,41 @@ impl<'a> Parser<'a> {
1012
1043
Ok ( ( host, input) )
1013
1044
}
1014
1045
1015
- pub ( crate ) fn parse_file_host < ' i > (
1046
+ fn get_file_host < ' i > ( input : Input < ' i > ) -> ParseResult < ( Host < String > , Input ) > {
1047
+ let ( _, host_str, remaining) = Parser :: file_host ( input) ?;
1048
+ let host = match Host :: parse ( & host_str) ? {
1049
+ Host :: Domain ( ref d) if d == "localhost" => Host :: Domain ( "" . to_string ( ) ) ,
1050
+ host => host,
1051
+ } ;
1052
+ Ok ( ( host, remaining) )
1053
+ }
1054
+
1055
+ fn parse_file_host < ' i > (
1016
1056
& mut self ,
1017
1057
input : Input < ' i > ,
1018
1058
) -> ParseResult < ( bool , HostInternal , Input < ' i > ) > {
1059
+ let has_host;
1060
+ let ( _, host_str, remaining) = Parser :: file_host ( input) ?;
1061
+ let host = if host_str. is_empty ( ) {
1062
+ has_host = false ;
1063
+ HostInternal :: None
1064
+ } else {
1065
+ match Host :: parse ( & host_str) ? {
1066
+ Host :: Domain ( ref d) if d == "localhost" => {
1067
+ has_host = false ;
1068
+ HostInternal :: None
1069
+ }
1070
+ host => {
1071
+ write ! ( & mut self . serialization, "{}" , host) . unwrap ( ) ;
1072
+ has_host = true ;
1073
+ host. into ( )
1074
+ }
1075
+ }
1076
+ } ;
1077
+ Ok ( ( has_host, host, remaining) )
1078
+ }
1079
+
1080
+ pub fn file_host < ' i > ( input : Input < ' i > ) -> ParseResult < ( bool , String , Input < ' i > ) > {
1019
1081
// Undo the Input abstraction here to avoid allocating in the common case
1020
1082
// where the host part of the input does not contain any tab or newline
1021
1083
let input_str = input. chars . as_str ( ) ;
@@ -1044,20 +1106,9 @@ impl<'a> Parser<'a> {
1044
1106
}
1045
1107
}
1046
1108
if is_windows_drive_letter ( host_str) {
1047
- return Ok ( ( false , HostInternal :: None , input) ) ;
1109
+ return Ok ( ( false , "" . to_string ( ) , input) ) ;
1048
1110
}
1049
- let host = if host_str. is_empty ( ) {
1050
- HostInternal :: None
1051
- } else {
1052
- match Host :: parse ( host_str) ? {
1053
- Host :: Domain ( ref d) if d == "localhost" => HostInternal :: None ,
1054
- host => {
1055
- write ! ( & mut self . serialization, "{}" , host) . unwrap ( ) ;
1056
- host. into ( )
1057
- }
1058
- }
1059
- } ;
1060
- Ok ( ( true , host, remaining) )
1111
+ Ok ( ( true , host_str. to_string ( ) , remaining) )
1061
1112
}
1062
1113
1063
1114
pub fn parse_port < P > (
@@ -1492,7 +1543,7 @@ fn c0_control_or_space(ch: char) -> bool {
1492
1543
1493
1544
/// https://infra.spec.whatwg.org/#ascii-tab-or-newline
1494
1545
#[ inline]
1495
- pub fn ascii_tab_or_new_line ( ch : char ) -> bool {
1546
+ fn ascii_tab_or_new_line ( ch : char ) -> bool {
1496
1547
matches ! ( ch, '\t' | '\r' | '\n' )
1497
1548
}
1498
1549
0 commit comments