blob: 4cdc40567c705e93148c729748321c233fed910b [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use rouille::{Request, Response};
/// Returns a tuple consisting of (PATH_PREFIX_TO_REMOVE_FROM_REQUEST, PATH_RELATIVE_TO_FUCHSIA_ROOT)
/// of the location of the file specified by the url.
/// This is used in the case where asset files are not contained in the
/// configured static directory.
fn translate_hardcoded_path(url: &str) -> Option<(String, String)> {
match url {
"/static/js/third_party/d3.js" => {
Some(("/static/js/third_party/".to_string(), "/scripts/third_party/d3".to_string()))
}
_ => None,
}
}
/// Serves up static files from the local fs based on a path relative to $FUCHSIA_DIR.
pub struct Visualizer {
fuchsia_root: String,
static_root: String,
}
impl Visualizer {
/// Takes two String paths: the fuchsia root directory, and the relative path
/// from the fuchsia root directory to the static assets directory.
pub fn new(fuchsia_path: String, mut path: String) -> Self {
if !path.ends_with("/") {
path = format!("{}/", path);
}
let static_root = format!("{}{}", fuchsia_path, path);
Self { fuchsia_root: fuchsia_path, static_root: static_root }
}
// If the file specified cannot be found because it does not exist, see
// if there is an index.html at the specified path and serve that instead.
pub fn serve_path_or_index(&self, request: &Request) -> Response {
if let Some((prefix_to_remove, path)) = translate_hardcoded_path(&request.url()) {
// Hardcoded paths are relative to fuchsia_root.
let prefix_stripped_req = request
.remove_prefix(&prefix_to_remove)
.expect("Expected to be able to remove prefix, but was unable to.");
rouille::match_assets(&prefix_stripped_req, &format!("{}{}", self.fuchsia_root, path))
} else {
let response = rouille::match_assets(request, &self.static_root);
// Looking for a *.html file if we were unable to find a matching asset.
if response.status_code == 404 {
let mut modified_url = request.url();
if modified_url.is_empty() || modified_url == "/" {
modified_url = "/index.html".to_string();
} else {
modified_url = format!("{}.html", request.url());
}
let new_request = Request::fake_http_from(
*request.remote_addr(),
request.method(),
modified_url,
request
.headers()
.map(|(key, val)| (key.to_string(), val.to_string()))
.collect(),
vec![],
); // Since this should be a GET request for an index.html, we never care about the body.
rouille::match_assets(&new_request, &self.static_root)
} else {
response
}
}
}
}
#[cfg(test)]
mod tests {
use {super::*, std::fs::File, std::io::Write, tempfile::tempdir};
fn setup_directory() -> String {
let dir = tempdir().unwrap();
let mut foo = File::create(dir.path().join("foo.html")).unwrap();
foo.write(b"Hello world!").unwrap();
let mut index = File::create(dir.path().join("index.html")).unwrap();
index.write(b"This is an index file.").unwrap();
dir.into_path().into_os_string().into_string().unwrap()
}
#[test]
fn serve_path_or_index_reads_existing_file() {
let tmp_dir = setup_directory();
let viz = Visualizer::new("".to_string(), tmp_dir.clone());
let response =
viz.serve_path_or_index(&Request::fake_http("GET", "foo.html", vec![], vec![]));
assert_eq!(response.status_code, 200);
let mut buffer = Vec::new();
let (mut reader, _) = response.data.into_reader_and_size();
reader.read_to_end(&mut buffer).unwrap();
assert_eq!(buffer, b"Hello world!");
}
#[test]
fn serve_path_or_index_reads_existing_file_with_rewrite() {
let tmp_dir = setup_directory();
let viz = Visualizer::new("".to_string(), tmp_dir.clone());
let response = viz.serve_path_or_index(&Request::fake_http("GET", "foo", vec![], vec![]));
assert_eq!(response.status_code, 200);
let mut buffer = Vec::new();
let (mut reader, _) = response.data.into_reader_and_size();
reader.read_to_end(&mut buffer).unwrap();
assert_eq!(buffer, b"Hello world!");
}
#[test]
fn serve_path_or_index_defaults_to_index_file() {
let tmp_dir = setup_directory();
let viz = Visualizer::new("".to_string(), tmp_dir.clone());
let response = viz.serve_path_or_index(&Request::fake_http("GET", "", vec![], vec![]));
assert_eq!(response.status_code, 200);
let mut buffer = Vec::new();
let (mut reader, _) = response.data.into_reader_and_size();
reader.read_to_end(&mut buffer).unwrap();
assert_eq!(buffer, b"This is an index file.");
}
#[test]
fn serve_path_or_index_fails_on_missing_file() {
let tmp_dir = setup_directory();
let viz = Visualizer::new("".to_string(), tmp_dir.clone());
let response =
viz.serve_path_or_index(&Request::fake_http("GET", "bar.html", vec![], vec![]));
assert_eq!(response.status_code, 404);
}
}