@@ -371,23 +371,38 @@ impl<R: RegExp> UrlPattern<R> {
371
371
..Default :: default ( )
372
372
} ;
373
373
374
- let pathname = if protocol. protocol_component_matches_special_scheme ( ) {
375
- Component :: compile (
376
- processed_init. pathname . as_deref ( ) ,
377
- canonicalize_and_process:: canonicalize_pathname,
378
- parser:: Options {
379
- ignore_case : options. ignore_case ,
380
- ..parser:: Options :: pathname ( )
381
- } ,
382
- ) ?
383
- . optionally_transpose_regex_error ( report_regex_errors) ?
384
- } else {
385
- Component :: compile (
386
- processed_init. pathname . as_deref ( ) ,
387
- canonicalize_and_process:: canonicalize_an_opaque_pathname,
388
- compile_options. clone ( ) ,
389
- ) ?
390
- . optionally_transpose_regex_error ( report_regex_errors) ?
374
+ let pathname = {
375
+ // Determine if path is non-opaque using the same criteria as process_pathname_init
376
+ let protocol_is_empty = processed_init
377
+ . protocol
378
+ . as_ref ( )
379
+ . is_some_and ( |p| p. is_empty ( ) ) ;
380
+ let has_leading_slash = processed_init
381
+ . pathname
382
+ . as_ref ( )
383
+ . is_some_and ( |p| p. starts_with ( '/' ) ) ;
384
+ let is_non_opaque = protocol_is_empty
385
+ || protocol. protocol_component_matches_special_scheme ( )
386
+ || has_leading_slash;
387
+
388
+ if is_non_opaque {
389
+ Component :: compile (
390
+ processed_init. pathname . as_deref ( ) ,
391
+ canonicalize_and_process:: canonicalize_pathname,
392
+ parser:: Options {
393
+ ignore_case : options. ignore_case ,
394
+ ..parser:: Options :: pathname ( )
395
+ } ,
396
+ ) ?
397
+ . optionally_transpose_regex_error ( report_regex_errors) ?
398
+ } else {
399
+ Component :: compile (
400
+ processed_init. pathname . as_deref ( ) ,
401
+ canonicalize_and_process:: canonicalize_an_opaque_pathname,
402
+ compile_options. clone ( ) ,
403
+ ) ?
404
+ . optionally_transpose_regex_error ( report_regex_errors) ?
405
+ }
391
406
} ;
392
407
393
408
Ok ( UrlPattern {
@@ -1047,4 +1062,46 @@ mod tests {
1047
1062
. unwrap ( ) ;
1048
1063
assert ! ( pattern. has_regexp_groups( ) ) ;
1049
1064
}
1065
+
1066
+ #[ test]
1067
+ fn issue61 ( ) {
1068
+ // Test case for https://github.com/denoland/deno/issues/29935
1069
+ // Custom protocols should not escape colons and slashes in pattern pathnames
1070
+
1071
+ // Test using init with pattern components
1072
+ let pattern = <UrlPattern >:: parse (
1073
+ UrlPatternInit {
1074
+ protocol : Some ( "myhttp" . to_string ( ) ) ,
1075
+ hostname : Some ( "example.com" . to_string ( ) ) ,
1076
+ pathname : Some ( "/:directory/:file" . to_string ( ) ) ,
1077
+ ..Default :: default ( )
1078
+ } ,
1079
+ Default :: default ( ) ,
1080
+ )
1081
+ . unwrap ( ) ;
1082
+
1083
+ println ! ( "Pattern: {pattern:?}" ) ;
1084
+ println ! ( "Protocol: {}" , pattern. protocol( ) ) ;
1085
+ println ! ( "Hostname: {}" , pattern. hostname( ) ) ;
1086
+ println ! ( "Pathname: {}" , pattern. pathname( ) ) ;
1087
+
1088
+ // The pathname should be "/:directory/:file", not "%2F:directory%2F:file"
1089
+ assert_eq ! ( pattern. pathname( ) . to_string( ) , "/:directory/:file" ) ;
1090
+
1091
+ // Also test myfile:///test case - empty hostname with leading slash
1092
+ let myfile_pattern = <UrlPattern >:: parse (
1093
+ UrlPatternInit {
1094
+ protocol : Some ( "myfile" . to_string ( ) ) ,
1095
+ hostname : Some ( "" . to_string ( ) ) , // empty hostname
1096
+ pathname : Some ( "/test" . to_string ( ) ) ,
1097
+ ..Default :: default ( )
1098
+ } ,
1099
+ Default :: default ( ) ,
1100
+ )
1101
+ . unwrap ( ) ;
1102
+
1103
+ println ! ( "\n Myfile pattern pathname: {}" , myfile_pattern. pathname( ) ) ;
1104
+ // Should use non-opaque canonicalization because of leading slash
1105
+ assert_eq ! ( myfile_pattern. pathname( ) . to_string( ) , "/test" ) ;
1106
+ }
1050
1107
}
0 commit comments