@@ -11,7 +11,7 @@ use crate::{
1111 crate_details:: CrateDetails ,
1212 csp:: Csp ,
1313 encode_url_path,
14- error:: { AxumNope , AxumResult } ,
14+ error:: { AxumNope , AxumResult , EscapedURI } ,
1515 extractors:: { DbConnection , Path } ,
1616 file:: File ,
1717 match_version,
@@ -362,18 +362,26 @@ pub(crate) async fn rustdoc_html_server_handler(
362362 // Pages generated by Rustdoc are not ready to be served with a CSP yet.
363363 csp. suppress ( true ) ;
364364
365+ fn get_query_and_fragment ( uri : & Uri ) -> Option < & str > {
366+ // We cannot extract the fragment (`#`) with current `Uri` API so forced to do ugly
367+ // things like that...
368+ uri. path_and_query ( )
369+ . and_then ( |path_and_query| path_and_query. as_str ( ) . splitn ( 2 , '?' ) . nth ( 1 ) )
370+ }
371+
365372 // Convenience function to allow for easy redirection
366373 #[ instrument]
367374 fn redirect (
368375 name : & str ,
369376 vers : & Version ,
370377 path : & [ & str ] ,
371378 cache_policy : CachePolicy ,
379+ uri : & Uri ,
372380 ) -> AxumResult < AxumResponse > {
373381 trace ! ( "redirect" ) ;
374- // Format and parse the redirect url
382+ let query = get_query_and_fragment ( & uri ) ;
375383 Ok ( axum_cached_redirect (
376- encode_url_path ( & format ! ( "/{}/{}/{}" , name, vers, path. join( "/" ) ) ) ,
384+ EscapedURI :: new ( & format ! ( "/{}/{}/{}" , name, vers, path. join( "/" ) ) , query ) . as_str ( ) ,
377385 cache_policy,
378386 ) ?
379387 . into_response ( ) )
@@ -391,24 +399,21 @@ pub(crate) async fn rustdoc_html_server_handler(
391399 let matched_release = match_version ( & mut conn, & params. name , & params. version )
392400 . await ?
393401 . into_exactly_named_or_else ( |corrected_name, req_version| {
402+ let query = get_query_and_fragment ( & uri) ;
394403 AxumNope :: Redirect (
395- encode_url_path ( & format ! (
396- "/{}/{}/{}" ,
397- corrected_name,
398- req_version,
399- req_path. join( "/" )
400- ) ) ,
404+ EscapedURI :: new (
405+ & format ! ( "/{}/{}/{}" , corrected_name, req_version, req_path. join( "/" ) ) ,
406+ query,
407+ ) ,
401408 CachePolicy :: NoCaching ,
402409 )
403410 } ) ?
404411 . into_canonical_req_version_or_else ( |version| {
405412 AxumNope :: Redirect (
406- encode_url_path ( & format ! (
407- "/{}/{}/{}" ,
408- & params. name,
409- version,
410- req_path. join( "/" )
411- ) ) ,
413+ EscapedURI :: new (
414+ & format ! ( "/{}/{}/{}" , & params. name, version, req_path. join( "/" ) ) ,
415+ None ,
416+ ) ,
412417 CachePolicy :: ForeverInCdn ,
413418 )
414419 } ) ?;
@@ -439,6 +444,7 @@ pub(crate) async fn rustdoc_html_server_handler(
439444 & krate. version ,
440445 & req_path[ 1 ..] ,
441446 CachePolicy :: ForeverInCdn ,
447+ & uri,
442448 ) ;
443449 }
444450
@@ -494,6 +500,7 @@ pub(crate) async fn rustdoc_html_server_handler(
494500 & krate. version ,
495501 & req_path,
496502 CachePolicy :: ForeverInCdn ,
503+ & uri,
497504 ) ;
498505 }
499506 }
@@ -2004,6 +2011,12 @@ mod test {
20042011 "/foo-ab/0.0.1/foo_ab/" ,
20052012 )
20062013 . await ?;
2014+ // `-` becomes `_` but we keep the query arguments.
2015+ web. assert_redirect_unchecked (
2016+ "/foo-ab/0.0.1/foo_ab/?search=a" ,
2017+ "/foo_ab/0.0.1/foo_ab/?search=a" ,
2018+ )
2019+ . await ?;
20072020 Ok ( ( ) )
20082021 } )
20092022 }
0 commit comments