@@ -77,40 +77,11 @@ fn serve(req: Request) -> Result<Response> {
7777 . map ( |h| h. to_str ( ) )
7878 . unwrap_or ( Ok ( "" ) ) ?;
7979
80- let path = match path {
81- "/" => "index.html" ,
82- _ => path,
83- } ;
84-
85- // read from the fallback path if the variable exists
86- let body = match FileServer :: read ( path, & enc) {
87- // requested file was found
88- Ok ( b) => Some ( b) ,
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- }
112- }
113- }
80+ // resolve the requested path and then try to read the file
81+ // None should indicate that the file does not exist after attempting fallback paths
82+ let body = match FileServer :: resolve ( path) {
83+ Some ( path) => FileServer :: read ( & path, & enc) . ok ( ) ,
84+ None => None ,
11485 } ;
11586
11687 let etag = FileServer :: get_etag ( body. clone ( ) ) ;
@@ -119,9 +90,40 @@ fn serve(req: Request) -> Result<Response> {
11990
12091struct FileServer ;
12192impl FileServer {
93+ /// Resolve the request path to a file path.
94+ /// Returns `None` if the path does not exist.
95+ fn resolve ( req_path : & str ) -> Option < PathBuf > {
96+ // fallback to index.html if the path is empty
97+ let mut path = if req_path. is_empty ( ) {
98+ PathBuf :: from ( DIRECTORY_FALLBACK_PATH )
99+ } else {
100+ PathBuf :: from ( req_path)
101+ } ;
102+
103+ // if the path is a directory, try to read the fallback file relative to the directory
104+ if path. is_dir ( ) {
105+ path. push ( DIRECTORY_FALLBACK_PATH ) ;
106+ }
107+
108+ // if still haven't found a file, override with the user-configured fallback path
109+ if !path. exists ( ) {
110+ if let Ok ( fallback_path) = std:: env:: var ( FALLBACK_PATH_ENV ) {
111+ path = PathBuf :: from ( fallback_path) ;
112+ }
113+ }
114+
115+ // return the path if it exists
116+ if path. exists ( ) {
117+ Some ( path)
118+ } else {
119+ None
120+ }
121+ }
122+
122123 /// Open the file given its path and return its content and content type header.
123- fn read ( path : & str , encoding : & ContentEncoding ) -> Result < Bytes > {
124- let mut file = File :: open ( path) . with_context ( || anyhow ! ( "cannot open {}" , path) ) ?;
124+ fn read ( path : & PathBuf , encoding : & ContentEncoding ) -> Result < Bytes > {
125+ let mut file =
126+ File :: open ( path) . with_context ( || anyhow ! ( "cannot open {}" , path. display( ) ) ) ?;
125127 let mut buf = vec ! [ ] ;
126128 match encoding {
127129 ContentEncoding :: Brotli => {
@@ -318,10 +320,22 @@ mod tests {
318320
319321 #[ test]
320322 fn test_serve_index ( ) {
323+ // Test against path with trailing slash
324+ let req = spin_http:: Request {
325+ method : spin_http:: Method :: Get ,
326+ uri : "http://thisistest.com" . to_string ( ) ,
327+ headers : vec ! [ ( PATH_INFO_HEADER . to_string( ) , "./" . to_string( ) ) ] ,
328+ params : vec ! [ ] ,
329+ body : None ,
330+ } ;
331+ let rsp = <super :: SpinHttp as spin_http:: SpinHttp >:: handle_http_request ( req) ;
332+ assert_eq ! ( rsp. status, 200 ) ;
333+
334+ // Test against empty path
321335 let req = spin_http:: Request {
322336 method : spin_http:: Method :: Get ,
323337 uri : "http://thisistest.com" . to_string ( ) ,
324- headers : vec ! [ ( PATH_INFO_HEADER . to_string( ) , "/ " . to_string( ) ) ] ,
338+ headers : vec ! [ ( PATH_INFO_HEADER . to_string( ) , "" . to_string( ) ) ] ,
325339 params : vec ! [ ] ,
326340 body : None ,
327341 } ;
0 commit comments