11//! rustdoc handler
22
33use crate :: {
4+ RUSTDOC_STATIC_STORAGE_PREFIX ,
45 db:: Pool ,
56 repositories:: RepositoryStatsUpdater ,
6- storage:: rustdoc_archive_path,
7+ storage:: { rustdoc_archive_path, PathNotFoundError } ,
78 utils,
89 web:: {
910 cache:: CachePolicy , crate_details:: CrateDetails , csp:: Csp , error:: Nope , file:: File ,
@@ -766,14 +767,49 @@ pub fn download_handler(req: &mut Request) -> IronResult<Response> {
766767 ) ) )
767768}
768769
770+ /// Serves shared resources used by rustdoc-generated documentation.
771+ ///
772+ /// This serves files from S3, and is pointed to by the `--static-root-path` flag to rustdoc.
773+ pub fn static_asset_handler ( req : & mut Request ) -> IronResult < Response > {
774+ let storage = extension ! ( req, Storage ) ;
775+ let config = extension ! ( req, Config ) ;
776+
777+ let filename = req. url . path ( ) [ 2 ..] . join ( "/" ) ;
778+ let storage_path = format ! ( "{}{}" , RUSTDOC_STATIC_STORAGE_PREFIX , filename) ;
779+
780+ // Prevent accessing static files outside the root. This could happen if the path
781+ // contains `/` or `..`. The check doesn't outright prevent those strings to be present
782+ // to allow accessing files in subdirectories.
783+ let canonical_path = std:: fs:: canonicalize ( & storage_path) . map_err ( |_| Nope :: ResourceNotFound ) ?;
784+ let canonical_root = std:: fs:: canonicalize ( & storage_path) . map_err ( |_| Nope :: ResourceNotFound ) ?;
785+
786+ if !canonical_path. starts_with ( canonical_root) {
787+ return Err ( Nope :: ResourceNotFound . into ( ) ) ;
788+ }
789+
790+ match File :: from_path ( storage, & storage_path, config) {
791+ Ok ( file) => Ok ( file. serve ( ) ) ,
792+ Err ( err) if err. downcast_ref :: < PathNotFoundError > ( ) . is_some ( ) => {
793+ Err ( Nope :: ResourceNotFound . into ( ) )
794+ }
795+ Err ( err) => {
796+ utils:: report_error ( & err) ;
797+ Err ( Nope :: InternalServerError . into ( ) )
798+ }
799+ }
800+ }
801+
769802/// Serves shared web resources used by rustdoc-generated documentation.
770803///
771804/// This includes common `css` and `js` files that only change when the compiler is updated, but are
772805/// otherwise the same for all crates documented with that compiler. Those have a custom handler to
773806/// deduplicate them and save space.
774- pub struct SharedResourceHandler ;
807+ ///
808+ /// This handler considers only the last component of the request path, and looks for a matching file
809+ /// in the Storage root.
810+ pub struct LegacySharedResourceHandler ;
775811
776- impl Handler for SharedResourceHandler {
812+ impl Handler for LegacySharedResourceHandler {
777813 fn handle ( & self , req : & mut Request ) -> IronResult < Response > {
778814 let path = req. url . path ( ) ;
779815 let filename = path. last ( ) . unwrap ( ) ; // unwrap is fine: vector is non-empty
@@ -796,6 +832,29 @@ impl Handler for SharedResourceHandler {
796832 }
797833}
798834
835+ /// Serves shared web resources used by rustdoc-generated documentation.
836+ ///
837+ /// Rustdoc has certain JS, CSS, font and image files that are required for all
838+ /// documentation it generates, and these don't change often. We make one copy
839+ /// of these per rustdoc release and serve them from a common location.
840+ ///
841+ /// This handler considers the whole path, and looks for a file at that path in
842+ /// the Storage.
843+ pub struct SharedResourceHandler ;
844+
845+ impl Handler for SharedResourceHandler {
846+ fn handle ( & self , req : & mut Request ) -> IronResult < Response > {
847+ let storage = extension ! ( req, Storage ) ;
848+ let config = extension ! ( req, Config ) ;
849+
850+ let storage_path = format ! ( "/{}" , req. url. path( ) . join( "/" ) ) ;
851+ match File :: from_path ( storage, & storage_path, config) {
852+ Ok ( file) => Ok ( file. serve ( ) ) ,
853+ Err ( _) => Err ( Nope :: ResourceNotFound . into ( ) ) ,
854+ }
855+ }
856+ }
857+
799858#[ cfg( test) ]
800859mod test {
801860 use crate :: { test:: * , web:: cache:: CachePolicy , Config } ;
0 commit comments