@@ -61,30 +61,32 @@ pub enum Request {
61
61
} ,
62
62
}
63
63
64
- fn split_off_query_fragment ( raw : RcStr ) -> ( Pattern , Vc < RcStr > , Vc < RcStr > ) {
65
- let Some ( ( raw, query) ) = raw. split_once ( '?' ) else {
66
- if let Some ( ( raw, fragment) ) = raw. split_once ( '#' ) {
67
- return (
68
- Pattern :: Constant ( raw. into ( ) ) ,
69
- Vc :: < RcStr > :: default ( ) ,
70
- Vc :: cell ( fragment. into ( ) ) ,
71
- ) ;
64
+ /// Splits a string like `foo?bar#baz` into `(Pattern::Constant('foo'), '?bar', '#baz')`
65
+ ///
66
+ /// If the hash or query portion are missing they will be empty strings otherwise they will be
67
+ /// non-empty along with their prepender characters
68
+ fn split_off_query_fragment ( mut raw : & str ) -> ( Pattern , RcStr , RcStr ) {
69
+ // Per the URI spec fragments can contain `?` characters, so we should trim it off first
70
+ // https://datatracker.ietf.org/doc/html/rfc3986#section-3.5
71
+
72
+ let hash = match raw. as_bytes ( ) . iter ( ) . position ( |& b| b == b'#' ) {
73
+ Some ( pos) => {
74
+ let hash = RcStr :: from ( & raw [ pos..] ) ;
75
+ raw = & raw [ ..pos] ;
76
+ hash
72
77
}
73
-
74
- return (
75
- Pattern :: Constant ( raw) ,
76
- Vc :: < RcStr > :: default ( ) ,
77
- Vc :: < RcStr > :: default ( ) ,
78
- ) ;
78
+ None => RcStr :: default ( ) ,
79
79
} ;
80
80
81
- let ( query, fragment) = query. split_once ( '#' ) . unwrap_or ( ( query, "" ) ) ;
82
-
83
- (
84
- Pattern :: Constant ( raw. into ( ) ) ,
85
- Vc :: cell ( format ! ( "?{query}" ) . into ( ) ) ,
86
- Vc :: cell ( format ! ( "#{fragment}" ) . into ( ) ) ,
87
- )
81
+ let query = match raw. as_bytes ( ) . iter ( ) . position ( |& b| b == b'?' ) {
82
+ Some ( pos) => {
83
+ let query = RcStr :: from ( & raw [ pos..] ) ;
84
+ raw = & raw [ ..pos] ;
85
+ query
86
+ }
87
+ None => RcStr :: default ( ) ,
88
+ } ;
89
+ ( Pattern :: Constant ( RcStr :: from ( raw) ) , query, hash)
88
90
}
89
91
90
92
lazy_static ! {
@@ -164,12 +166,12 @@ impl Request {
164
166
}
165
167
166
168
if r. starts_with ( '/' ) {
167
- let ( path, query, fragment) = split_off_query_fragment ( r) ;
169
+ let ( path, query, fragment) = split_off_query_fragment ( & r) ;
168
170
169
171
return Ok ( Request :: ServerRelative {
170
172
path,
171
- query : query . to_resolved ( ) . await ? ,
172
- fragment : fragment . to_resolved ( ) . await ? ,
173
+ query : ResolvedVc :: cell ( query ) ,
174
+ fragment : ResolvedVc :: cell ( fragment ) ,
173
175
} ) ;
174
176
}
175
177
@@ -180,23 +182,23 @@ impl Request {
180
182
}
181
183
182
184
if r. starts_with ( "./" ) || r. starts_with ( "../" ) || r == "." || r == ".." {
183
- let ( path, query, fragment) = split_off_query_fragment ( r) ;
185
+ let ( path, query, fragment) = split_off_query_fragment ( & r) ;
184
186
185
187
return Ok ( Request :: Relative {
186
188
path,
187
189
force_in_lookup_dir : false ,
188
- query : query . to_resolved ( ) . await ? ,
189
- fragment : fragment . to_resolved ( ) . await ? ,
190
+ query : ResolvedVc :: cell ( query ) ,
191
+ fragment : ResolvedVc :: cell ( fragment ) ,
190
192
} ) ;
191
193
}
192
194
193
195
if WINDOWS_PATH . is_match ( & r) {
194
- let ( path, query, fragment) = split_off_query_fragment ( r) ;
196
+ let ( path, query, fragment) = split_off_query_fragment ( & r) ;
195
197
196
198
return Ok ( Request :: Windows {
197
199
path,
198
- query : query . to_resolved ( ) . await ? ,
199
- fragment : fragment . to_resolved ( ) . await ? ,
200
+ query : ResolvedVc :: cell ( query ) ,
201
+ fragment : ResolvedVc :: cell ( fragment ) ,
200
202
} ) ;
201
203
}
202
204
@@ -227,13 +229,13 @@ impl Request {
227
229
. captures ( & r)
228
230
. and_then ( |caps| caps. get ( 1 ) . zip ( caps. get ( 2 ) ) )
229
231
{
230
- let ( path, query, fragment) = split_off_query_fragment ( path. as_str ( ) . into ( ) ) ;
232
+ let ( path, query, fragment) = split_off_query_fragment ( path. as_str ( ) ) ;
231
233
232
234
return Ok ( Request :: Module {
233
235
module : module. as_str ( ) . into ( ) ,
234
236
path,
235
- query : query . to_resolved ( ) . await ? ,
236
- fragment : fragment . to_resolved ( ) . await ? ,
237
+ query : ResolvedVc :: cell ( query ) ,
238
+ fragment : ResolvedVc :: cell ( fragment ) ,
237
239
} ) ;
238
240
}
239
241
@@ -817,3 +819,63 @@ pub async fn stringify_data_uri(
817
819
data. await ?
818
820
) )
819
821
}
822
+
823
+ #[ cfg( test) ]
824
+ mod tests {
825
+ use super :: * ;
826
+
827
+ #[ test]
828
+ fn test_split_query_fragment ( ) {
829
+ assert_eq ! (
830
+ (
831
+ Pattern :: Constant ( "foo" . into( ) ) ,
832
+ RcStr :: default ( ) ,
833
+ RcStr :: default ( )
834
+ ) ,
835
+ split_off_query_fragment( "foo" )
836
+ ) ;
837
+ // These two cases are a bit odd, but it is important to treat `import './foo?'` differently
838
+ // from `import './foo'`, ditto for fragments.
839
+ assert_eq ! (
840
+ (
841
+ Pattern :: Constant ( "foo" . into( ) ) ,
842
+ RcStr :: from( "?" ) ,
843
+ RcStr :: default ( )
844
+ ) ,
845
+ split_off_query_fragment( "foo?" )
846
+ ) ;
847
+ assert_eq ! (
848
+ (
849
+ Pattern :: Constant ( "foo" . into( ) ) ,
850
+ RcStr :: default ( ) ,
851
+ RcStr :: from( "#" )
852
+ ) ,
853
+ split_off_query_fragment( "foo#" )
854
+ ) ;
855
+ assert_eq ! (
856
+ (
857
+ Pattern :: Constant ( "foo" . into( ) ) ,
858
+ RcStr :: from( "?bar=baz" ) ,
859
+ RcStr :: default ( )
860
+ ) ,
861
+ split_off_query_fragment( "foo?bar=baz" )
862
+ ) ;
863
+ assert_eq ! (
864
+ (
865
+ Pattern :: Constant ( "foo" . into( ) ) ,
866
+ RcStr :: default ( ) ,
867
+ RcStr :: from( "#stuff?bar=baz" )
868
+ ) ,
869
+ split_off_query_fragment( "foo#stuff?bar=baz" )
870
+ ) ;
871
+
872
+ assert_eq ! (
873
+ (
874
+ Pattern :: Constant ( "foo" . into( ) ) ,
875
+ RcStr :: from( "?bar=baz" ) ,
876
+ RcStr :: from( "#stuff" )
877
+ ) ,
878
+ split_off_query_fragment( "foo?bar=baz#stuff" )
879
+ ) ;
880
+ }
881
+ }
0 commit comments