33use crate :: {
44 db:: Pool ,
55 repositories:: RepositoryStatsUpdater ,
6- storage:: rustdoc_archive_path,
6+ storage:: { rustdoc_archive_path, PathNotFoundError } ,
77 utils,
88 web:: {
99 cache:: CachePolicy , crate_details:: CrateDetails , csp:: Csp , error:: Nope , file:: File ,
1010 match_version, metrics:: RenderingTimesRecorder , parse_url_with_params, redirect_base,
1111 report_error, MatchSemver , MetaData ,
1212 } ,
13- Config , Metrics , Storage ,
13+ Config , Metrics , Storage , RUSTDOC_STATIC_STORAGE_PREFIX ,
1414} ;
1515use anyhow:: { anyhow, Context } ;
1616use iron:: {
@@ -766,14 +766,51 @@ pub fn download_handler(req: &mut Request) -> IronResult<Response> {
766766 ) ) )
767767}
768768
769+ /// Serves shared resources used by rustdoc-generated documentation.
770+ ///
771+ /// This serves files from S3, and is pointed to by the `--static-root-path` flag to rustdoc.
772+ pub fn static_asset_handler ( req : & mut Request ) -> IronResult < Response > {
773+ let storage = extension ! ( req, Storage ) ;
774+ let config = extension ! ( req, Config ) ;
775+
776+ let filename = req. url . path ( ) [ 2 ..] . join ( "/" ) ;
777+ let storage_path = format ! ( "{}{}" , RUSTDOC_STATIC_STORAGE_PREFIX , filename) ;
778+
779+ // Prevent accessing static files outside the root. This could happen if the path
780+ // contains `/` or `..`. The check doesn't outright prevent those strings to be present
781+ // to allow accessing files in subdirectories.
782+ let canonical_path =
783+ std:: fs:: canonicalize ( & storage_path) . map_err ( |_| Nope :: ResourceNotFound ) ?;
784+ let canonical_root =
785+ std:: fs:: canonicalize ( & storage_path) . map_err ( |_| Nope :: ResourceNotFound ) ?;
786+
787+ if !canonical_path. starts_with ( canonical_root) {
788+ return Err ( Nope :: ResourceNotFound . into ( ) ) ;
789+ }
790+
791+ match File :: from_path ( storage, & storage_path, config) {
792+ Ok ( file) => Ok ( file. serve ( ) ) ,
793+ Err ( err) if err. downcast_ref :: < PathNotFoundError > ( ) . is_some ( ) => {
794+ Err ( Nope :: ResourceNotFound . into ( ) )
795+ }
796+ Err ( err) => {
797+ utils:: report_error ( & err) ;
798+ Err ( Nope :: InternalServerError . into ( ) )
799+ }
800+ }
801+ }
802+
769803/// Serves shared web resources used by rustdoc-generated documentation.
770804///
771805/// This includes common `css` and `js` files that only change when the compiler is updated, but are
772806/// otherwise the same for all crates documented with that compiler. Those have a custom handler to
773807/// deduplicate them and save space.
774- pub struct SharedResourceHandler ;
808+ ///
809+ /// This handler considers only the last component of the request path, and looks for a matching file
810+ /// in the Storage root.
811+ pub struct LegacySharedResourceHandler ;
775812
776- impl Handler for SharedResourceHandler {
813+ impl Handler for LegacySharedResourceHandler {
777814 fn handle ( & self , req : & mut Request ) -> IronResult < Response > {
778815 let path = req. url . path ( ) ;
779816 let filename = path. last ( ) . unwrap ( ) ; // unwrap is fine: vector is non-empty
@@ -796,6 +833,29 @@ impl Handler for SharedResourceHandler {
796833 }
797834}
798835
836+ /// Serves shared web resources used by rustdoc-generated documentation.
837+ ///
838+ /// Rustdoc has certain JS, CSS, font and image files that are required for all
839+ /// documentation it generates, and these don't change often. We make one copy
840+ /// of these per rustdoc release and serve them from a common location.
841+ ///
842+ /// This handler considers the whole path, and looks for a file at that path in
843+ /// the Storage.
844+ pub struct SharedResourceHandler ;
845+
846+ impl Handler for SharedResourceHandler {
847+ fn handle ( & self , req : & mut Request ) -> IronResult < Response > {
848+ let storage = extension ! ( req, Storage ) ;
849+ let config = extension ! ( req, Config ) ;
850+
851+ let storage_path = format ! ( "/{}" , req. url. path( ) . join( "/" ) ) ;
852+ match File :: from_path ( storage, & storage_path, config) {
853+ Ok ( file) => Ok ( file. serve ( ) ) ,
854+ Err ( _) => Err ( Nope :: ResourceNotFound . into ( ) ) ,
855+ }
856+ }
857+ }
858+
799859#[ cfg( test) ]
800860mod test {
801861 use crate :: { test:: * , web:: cache:: CachePolicy , Config } ;
0 commit comments