(
root: PathBuf,
mount_prefix: Option<&str>,
fallback_to_index: bool,
method: Method,
uri: Uri,
access_token: Option<String>,
)
| 23 | } |
| 24 | |
| 25 | async fn serve_static_inner( |
| 26 | root: PathBuf, |
| 27 | mount_prefix: Option<&str>, |
| 28 | fallback_to_index: bool, |
| 29 | method: Method, |
| 30 | uri: Uri, |
| 31 | access_token: Option<String>, |
| 32 | ) -> Result<Response<Body>, StatusCode> { |
| 33 | if method != Method::GET && method != Method::HEAD { |
| 34 | return Err(StatusCode::METHOD_NOT_ALLOWED); |
| 35 | } |
| 36 | |
| 37 | let mut path = uri.path(); |
| 38 | if let Some(prefix) = mount_prefix { |
| 39 | path = path |
| 40 | .strip_prefix(prefix) |
| 41 | .ok_or(StatusCode::NOT_FOUND)? |
| 42 | .trim_start_matches('/'); |
| 43 | } |
| 44 | let Some(relative_path) = safe_relative_path(path) else { |
| 45 | return Err(StatusCode::BAD_REQUEST); |
| 46 | }; |
| 47 | |
| 48 | let requested_path = root.join(&relative_path); |
| 49 | let file_path = match tokio::fs::metadata(&requested_path).await { |
| 50 | Ok(metadata) if metadata.is_file() => requested_path, |
| 51 | _ if fallback_to_index => root.join("index.html"), |
| 52 | _ => return Err(StatusCode::NOT_FOUND), |
| 53 | }; |
| 54 | |
| 55 | let bytes = tokio::fs::read(&file_path) |
| 56 | .await |
| 57 | .map_err(|_| StatusCode::NOT_FOUND)?; |
| 58 | let content_type = content_type_for_path(&file_path); |
| 59 | let body = if method == Method::HEAD { |
| 60 | Body::empty() |
| 61 | } else { |
| 62 | Body::from(bytes) |
| 63 | }; |
| 64 | |
| 65 | let mut response = Response::builder() |
| 66 | .status(StatusCode::OK) |
| 67 | .header(header::CONTENT_TYPE, content_type) |
| 68 | .header( |
| 69 | header::CACHE_CONTROL, |
| 70 | "no-store, no-cache, must-revalidate, max-age=0", |
| 71 | ) |
| 72 | .header(header::PRAGMA, "no-cache") |
| 73 | .header(header::EXPIRES, "0") |
| 74 | .body(body) |
| 75 | .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; |
| 76 | response.headers_mut().insert( |
| 77 | header::ACCESS_CONTROL_ALLOW_ORIGIN, |
| 78 | "*".parse().map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?, |
| 79 | ); |
| 80 | if content_type.starts_with("text/html") { |
| 81 | response.headers_mut().insert( |
| 82 | HeaderName::from_static("clear-site-data"), |
no test coverage detected