3
3
use crate :: {
4
4
db:: Pool ,
5
5
repositories:: RepositoryStatsUpdater ,
6
- storage:: rustdoc_archive_path,
6
+ storage:: { rustdoc_archive_path, PathNotFoundError } ,
7
7
utils,
8
8
web:: {
9
9
cache:: CachePolicy , crate_details:: CrateDetails , csp:: Csp , error:: Nope , file:: File ,
10
10
match_version, metrics:: RenderingTimesRecorder , parse_url_with_params, redirect_base,
11
11
report_error, MatchSemver , MetaData ,
12
12
} ,
13
- Config , Metrics , Storage ,
13
+ Config , Metrics , Storage , RUSTDOC_STATIC_STORAGE_PREFIX ,
14
14
} ;
15
15
use anyhow:: { anyhow, Context } ;
16
16
use iron:: {
@@ -766,14 +766,51 @@ pub fn download_handler(req: &mut Request) -> IronResult<Response> {
766
766
) ) )
767
767
}
768
768
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
+
769
803
/// Serves shared web resources used by rustdoc-generated documentation.
770
804
///
771
805
/// This includes common `css` and `js` files that only change when the compiler is updated, but are
772
806
/// otherwise the same for all crates documented with that compiler. Those have a custom handler to
773
807
/// 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 ;
775
812
776
- impl Handler for SharedResourceHandler {
813
+ impl Handler for LegacySharedResourceHandler {
777
814
fn handle ( & self , req : & mut Request ) -> IronResult < Response > {
778
815
let path = req. url . path ( ) ;
779
816
let filename = path. last ( ) . unwrap ( ) ; // unwrap is fine: vector is non-empty
@@ -796,6 +833,29 @@ impl Handler for SharedResourceHandler {
796
833
}
797
834
}
798
835
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
+
799
859
#[ cfg( test) ]
800
860
mod test {
801
861
use crate :: { test:: * , web:: cache:: CachePolicy , Config } ;
0 commit comments