@@ -10,6 +10,7 @@ use std::{
1010 fs:: File ,
1111 hash:: { Hash , Hasher } ,
1212 io:: Read ,
13+ path:: PathBuf ,
1314} ;
1415
1516/// The default value for the cache control header.
@@ -27,6 +28,8 @@ const BROTLI_ENCODING: &str = "br";
2728const PATH_INFO_HEADER : & str = "spin-path-info" ;
2829// Environment variable for the fallback path
2930const FALLBACK_PATH_ENV : & str = "FALLBACK_PATH" ;
31+ /// Directory fallback path (trying to map `/about/` -> `/about/index.html`).
32+ const DIRECTORY_FALLBACK_PATH : & str = "index.html" ;
3033
3134/// Common Content Encodings
3235#[ derive( Debug , Eq , PartialEq ) ]
@@ -83,15 +86,31 @@ fn serve(req: Request) -> Result<Response> {
8386 let body = match FileServer :: read ( path, & enc) {
8487 // requested file was found
8588 Ok ( b) => Some ( b) ,
86- Err ( e) => match std:: env:: var ( FALLBACK_PATH_ENV ) {
87- // try to read the fallback path
88- Ok ( fallback_path) => FileServer :: read ( fallback_path. as_str ( ) , & enc) . ok ( ) ,
89- // fallback path config not found
90- Err ( _) => {
91- eprintln ! ( "Cannot read file: {e:?}" ) ;
92- None
89+ Err ( e) => {
90+ // if the error is because the path points to a directory, attempt to read `index.html`
91+ // from the directory. This is because most static site generators will generate this
92+ // file structure (where `/about` should be mapped to `/about/index.html`).
93+ eprintln ! ( "Cannot find file {path}. Attempting fallback." ) ;
94+ // TODO: ideally, we would return better errors throughout the implementation.
95+ let directory_fallback = PathBuf :: from ( path) . join ( DIRECTORY_FALLBACK_PATH ) ;
96+ if e. to_string ( ) . contains ( "Is a directory" ) && directory_fallback. exists ( ) {
97+ let directory_fallback = directory_fallback
98+ . to_str ( )
99+ . context ( "cannot convert path to string" ) ?;
100+ eprintln ! ( "Attempting directory fallback {directory_fallback}" ) ;
101+ FileServer :: read ( directory_fallback, & enc) . ok ( )
102+ } else {
103+ match std:: env:: var ( FALLBACK_PATH_ENV ) {
104+ // try to read the fallback path
105+ Ok ( fallback_path) => FileServer :: read ( fallback_path. as_str ( ) , & enc) . ok ( ) ,
106+ // fallback path config not found
107+ Err ( _) => {
108+ eprintln ! ( "Cannot read file: {e:?}" ) ;
109+ None
110+ }
111+ }
93112 }
94- } ,
113+ }
95114 } ;
96115
97116 let etag = FileServer :: get_etag ( body. clone ( ) ) ;
0 commit comments