Skip to content

Commit 8341f51

Browse files
author
Justin Pflueger
committed
add a function to resolve the requested path with exists checks
Signed-off-by: Justin Pflueger <[email protected]>
1 parent 656ae1f commit 8341f51

File tree

1 file changed

+48
-35
lines changed

1 file changed

+48
-35
lines changed

src/lib.rs

Lines changed: 48 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -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.as_str(), &enc).ok(),
84+
None => None,
11485
};
11586

11687
let etag = FileServer::get_etag(body.clone());
@@ -119,6 +90,36 @@ fn serve(req: Request) -> Result<Response> {
11990

12091
struct FileServer;
12192
impl 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<String> {
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+
// 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.to_str().unwrap().to_string())
118+
} else {
119+
None
120+
}
121+
}
122+
122123
/// Open the file given its path and return its content and content type header.
123124
fn read(path: &str, encoding: &ContentEncoding) -> Result<Bytes> {
124125
let mut file = File::open(path).with_context(|| anyhow!("cannot open {}", path))?;
@@ -318,10 +319,22 @@ mod tests {
318319

319320
#[test]
320321
fn test_serve_index() {
322+
// Test against path with trailing slash
323+
let req = spin_http::Request {
324+
method: spin_http::Method::Get,
325+
uri: "http://thisistest.com".to_string(),
326+
headers: vec![(PATH_INFO_HEADER.to_string(), "./".to_string())],
327+
params: vec![],
328+
body: None,
329+
};
330+
let rsp = <super::SpinHttp as spin_http::SpinHttp>::handle_http_request(req);
331+
assert_eq!(rsp.status, 200);
332+
333+
// Test against empty path
321334
let req = spin_http::Request {
322335
method: spin_http::Method::Get,
323336
uri: "http://thisistest.com".to_string(),
324-
headers: vec![(PATH_INFO_HEADER.to_string(), "/".to_string())],
337+
headers: vec![(PATH_INFO_HEADER.to_string(), "".to_string())],
325338
params: vec![],
326339
body: None,
327340
};

0 commit comments

Comments
 (0)