@@ -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 ,
@@ -217,7 +217,7 @@ impl<'i> Input<'i> {
217
217
pub fn with_log ( original_input : & ' i str , vfn : Option < & dyn Fn ( SyntaxViolation ) > ) -> Self {
218
218
let input = original_input. trim_matches ( c0_control_or_space) ;
219
219
if let Some ( vfn) = vfn {
220
- if input. len ( ) < original_input. len ( ) {
220
+ if input. len ( ) != original_input. len ( ) {
221
221
vfn ( SyntaxViolation :: C0SpaceIgnored )
222
222
}
223
223
if input. chars ( ) . any ( |c| matches ! ( c, '\t' | '\n' | '\r' ) ) {
@@ -858,11 +858,13 @@ impl<'a> Parser<'a> {
858
858
self . serialization . push ( '/' ) ;
859
859
self . serialization . push ( '/' ) ;
860
860
// authority state
861
+ let before_authority = self . serialization . len ( ) ;
861
862
let ( username_end, remaining) = self . parse_userinfo ( input, scheme_type) ?;
863
+ let has_authority = before_authority != self . serialization . len ( ) ;
862
864
// host state
863
865
let host_start = to_u32 ( self . serialization . len ( ) ) ?;
864
866
let ( host_end, host, port, remaining) =
865
- self . parse_host_and_port ( remaining, scheme_end, scheme_type) ?;
867
+ self . parse_host_and_port ( remaining, scheme_end, scheme_type, has_authority ) ?;
866
868
// path state
867
869
let path_start = to_u32 ( self . serialization . len ( ) ) ?;
868
870
let remaining = self . parse_path_start ( scheme_type, & mut true , remaining) ;
@@ -906,7 +908,18 @@ impl<'a> Parser<'a> {
906
908
}
907
909
let ( mut userinfo_char_count, remaining) = match last_at {
908
910
None => return Ok ( ( to_u32 ( self . serialization . len ( ) ) ?, input) ) ,
909
- Some ( ( 0 , remaining) ) => return Ok ( ( to_u32 ( self . serialization . len ( ) ) ?, remaining) ) ,
911
+ Some ( ( 0 , remaining) ) => {
912
+ // Otherwise, if one of the following is true
913
+ // c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
914
+ // url is special and c is U+005C (\)
915
+ // If @ flag is set and buffer is the empty string, validation error, return failure.
916
+ if let ( Some ( c) , _) = remaining. split_first ( ) {
917
+ if c == '/' || c == '?' || c == '#' || scheme_type. is_special ( ) && c == '\\' {
918
+ return Err ( ParseError :: EmptyHost ) ;
919
+ }
920
+ }
921
+ return Ok ( ( to_u32 ( self . serialization . len ( ) ) ?, remaining) ) ;
922
+ }
910
923
Some ( x) => x,
911
924
} ;
912
925
@@ -948,10 +961,26 @@ impl<'a> Parser<'a> {
948
961
input : Input < ' i > ,
949
962
scheme_end : u32 ,
950
963
scheme_type : SchemeType ,
964
+ has_authority : bool ,
951
965
) -> ParseResult < ( u32 , HostInternal , Option < u16 > , Input < ' i > ) > {
952
966
let ( host, remaining) = Parser :: parse_host ( input, scheme_type) ?;
953
967
write ! ( & mut self . serialization, "{}" , host) . unwrap ( ) ;
954
968
let host_end = to_u32 ( self . serialization . len ( ) ) ?;
969
+ if let Host :: Domain ( h) = & host {
970
+ if h. is_empty ( ) {
971
+ // Port with an empty host
972
+ if remaining. starts_with ( ":" ) {
973
+ return Err ( ParseError :: EmptyHost ) ;
974
+ }
975
+ if scheme_type. is_special ( ) {
976
+ return Err ( ParseError :: EmptyHost ) ;
977
+ }
978
+ if !scheme_type. is_special ( ) && has_authority {
979
+ return Err ( ParseError :: EmptyHost ) ;
980
+ }
981
+ }
982
+ } ;
983
+
955
984
let ( port, remaining) = if let Some ( remaining) = remaining. split_prefix ( ':' ) {
956
985
let scheme = || default_port ( & self . serialization [ ..scheme_end as usize ] ) ;
957
986
Parser :: parse_port ( remaining, scheme, self . context ) ?
@@ -1018,10 +1047,41 @@ impl<'a> Parser<'a> {
1018
1047
Ok ( ( host, input) )
1019
1048
}
1020
1049
1021
- pub ( crate ) fn parse_file_host < ' i > (
1050
+ pub fn get_file_host < ' i > ( input : Input < ' i > ) -> ParseResult < ( Host < String > , Input ) > {
1051
+ let ( _, host_str, remaining) = Parser :: file_host ( input) ?;
1052
+ let host = match Host :: parse ( & host_str) ? {
1053
+ Host :: Domain ( ref d) if d == "localhost" => Host :: Domain ( "" . to_string ( ) ) ,
1054
+ host => host,
1055
+ } ;
1056
+ Ok ( ( host, remaining) )
1057
+ }
1058
+
1059
+ fn parse_file_host < ' i > (
1022
1060
& mut self ,
1023
1061
input : Input < ' i > ,
1024
1062
) -> ParseResult < ( bool , HostInternal , Input < ' i > ) > {
1063
+ let has_host;
1064
+ let ( _, host_str, remaining) = Parser :: file_host ( input) ?;
1065
+ let host = if host_str. is_empty ( ) {
1066
+ has_host = false ;
1067
+ HostInternal :: None
1068
+ } else {
1069
+ match Host :: parse ( & host_str) ? {
1070
+ Host :: Domain ( ref d) if d == "localhost" => {
1071
+ has_host = false ;
1072
+ HostInternal :: None
1073
+ }
1074
+ host => {
1075
+ write ! ( & mut self . serialization, "{}" , host) . unwrap ( ) ;
1076
+ has_host = true ;
1077
+ host. into ( )
1078
+ }
1079
+ }
1080
+ } ;
1081
+ Ok ( ( has_host, host, remaining) )
1082
+ }
1083
+
1084
+ pub fn file_host < ' i > ( input : Input < ' i > ) -> ParseResult < ( bool , String , Input < ' i > ) > {
1025
1085
// Undo the Input abstraction here to avoid allocating in the common case
1026
1086
// where the host part of the input does not contain any tab or newline
1027
1087
let input_str = input. chars . as_str ( ) ;
@@ -1050,20 +1110,9 @@ impl<'a> Parser<'a> {
1050
1110
}
1051
1111
}
1052
1112
if is_windows_drive_letter ( host_str) {
1053
- return Ok ( ( false , HostInternal :: None , input) ) ;
1113
+ return Ok ( ( false , "" . to_string ( ) , input) ) ;
1054
1114
}
1055
- let host = if host_str. is_empty ( ) {
1056
- HostInternal :: None
1057
- } else {
1058
- match Host :: parse ( host_str) ? {
1059
- Host :: Domain ( ref d) if d == "localhost" => HostInternal :: None ,
1060
- host => {
1061
- write ! ( & mut self . serialization, "{}" , host) . unwrap ( ) ;
1062
- host. into ( )
1063
- }
1064
- }
1065
- } ;
1066
- Ok ( ( true , host, remaining) )
1115
+ Ok ( ( true , host_str. to_string ( ) , remaining) )
1067
1116
}
1068
1117
1069
1118
pub fn parse_port < P > (
@@ -1503,7 +1552,7 @@ fn c0_control_or_space(ch: char) -> bool {
1503
1552
1504
1553
/// https://infra.spec.whatwg.org/#ascii-tab-or-newline
1505
1554
#[ inline]
1506
- pub fn ascii_tab_or_new_line ( ch : char ) -> bool {
1555
+ fn ascii_tab_or_new_line ( ch : char ) -> bool {
1507
1556
matches ! ( ch, '\t' | '\r' | '\n' )
1508
1557
}
1509
1558
0 commit comments