fix: properly canonicalize assets

This commit is contained in:
Jonathan Kelley committed 2022-12-31 02:24:18 -05:00
1 parent 3a34cc12f3
commit 341ccf6de6
3 files changed
+53 -71

No files matched your search

+2 -1
View File
@@ -8,7 +8,8 @@ fn app(cx: Scope) -> Element {
cx.render(rsx! { cx.render(rsx! {
div { div {
"This should show an image:" "This should show an image:"
img { src: "examples/assets/logo.png", } img { src: "examples/assets/logo.png" }
img { src: "/Users/jonkelley/Desktop/blitz.png" }
} }
}) })
} }
+50 -62
View File
@@ -25,73 +25,62 @@ fn module_loader(root_name: &str) -> String {
pub(super) fn desktop_handler( pub(super) fn desktop_handler(
request: &Request<Vec<u8>>, request: &Request<Vec<u8>>,
asset_root: Option<PathBuf>,
custom_head: Option<String>, custom_head: Option<String>,
custom_index: Option<String>, custom_index: Option<String>,
root_name: &str, root_name: &str,
) -> Result<Response<Vec<u8>>> { ) -> Result<Response<Vec<u8>>> {
// Any content that uses the `dioxus://` scheme will be shuttled through this handler as a "special case". // If the request is for the root, we'll serve the index.html file.
// For now, we only serve two pieces of content which get included as bytes into the final binary. if request.uri().path() == "/" {
let path = request.uri().to_string().replace("dioxus://", "");
// all assets should be called from index.html
let trimmed = path.trim_start_matches("index.html/");
if trimmed.is_empty() {
// If a custom index is provided, just defer to that, expecting the user to know what they're doing. // If a custom index is provided, just defer to that, expecting the user to know what they're doing.
// we'll look for the closing </body> tag and insert our little module loader there. // we'll look for the closing </body> tag and insert our little module loader there.
if let Some(custom_index) = custom_index { let body = match custom_index {
let rendered = custom_index Some(custom_index) => custom_index
.replace("</body>", &format!("{}</body>", module_loader(root_name))) .replace("</body>", &format!("{}</body>", module_loader(root_name)))
.into_bytes(); .into_bytes(),
Response::builder()
.header("Content-Type", "text/html") None => {
.body(rendered) // Otherwise, we'll serve the default index.html and apply a custom head if that's specified.
.map_err(From::from) let mut template = include_str!("./index.html").to_string();
} else {
// Otherwise, we'll serve the default index.html and apply a custom head if that's specified. if let Some(custom_head) = custom_head {
let mut template = include_str!("./index.html").to_string(); template = template.replace("<!-- CUSTOM HEAD -->", &custom_head);
if let Some(custom_head) = custom_head { }
template = template.replace("<!-- CUSTOM HEAD -->", &custom_head);
template
.replace("<!-- MODULE LOADER -->", &module_loader(root_name))
.into_bytes()
} }
template = template.replace("<!-- MODULE LOADER -->", &module_loader(root_name)); };
Response::builder() return Response::builder()
.header("Content-Type", "text/html") .header("Content-Type", "text/html")
.body(template.into_bytes()) .body(body)
.map_err(From::from) .map_err(From::from);
}
} else if trimmed == "index.js" {
Response::builder()
.header("Content-Type", "text/javascript")
.body(dioxus_interpreter_js::INTERPRETER_JS.as_bytes().to_vec())
.map_err(From::from)
} else {
let asset_root = asset_root
.unwrap_or_else(|| get_asset_root().unwrap_or_else(|| Path::new(".").to_path_buf()))
.canonicalize()?;
let asset = asset_root.join(trimmed).canonicalize()?;
if !asset.starts_with(asset_root) {
return Response::builder()
.status(StatusCode::FORBIDDEN)
.body(String::from("Forbidden").into_bytes())
.map_err(From::from);
}
if !asset.exists() {
return Response::builder()
.status(StatusCode::NOT_FOUND)
.body(String::from("Not Found").into_bytes())
.map_err(From::from);
}
Response::builder()
.header("Content-Type", get_mime_from_path(trimmed)?)
.body(std::fs::read(asset)?)
.map_err(From::from)
} }
// Else, try to serve a file from the filesystem.
let path = PathBuf::from(request.uri().path().trim_start_matches("/"));
// If the path is relative, we'll try to serve it from the assets directory.
let mut asset = get_asset_root()
.unwrap_or_else(|| Path::new(".").to_path_buf())
.join(&path);
if !asset.exists() {
asset = PathBuf::from("/").join(path);
}
if asset.exists() {
return Response::builder()
.header("Content-Type", get_mime_from_path(&asset)?)
.body(std::fs::read(asset)?)
.map_err(From::from);
}
Response::builder()
.status(StatusCode::NOT_FOUND)
.body(String::from("Not Found").into_bytes())
.map_err(From::from)
} }
#[allow(unreachable_code)] #[allow(unreachable_code)]
@@ -128,12 +117,12 @@ fn get_asset_root() -> Option<PathBuf> {
} }
/// Get the mime type from a path-like string /// Get the mime type from a path-like string
fn get_mime_from_path(trimmed: &str) -> Result<&str> { fn get_mime_from_path(trimmed: &PathBuf) -> Result<&'static str> {
if trimmed.ends_with(".svg") { if trimmed.ends_with(".svg") {
return Ok("image/svg+xml"); return Ok("image/svg+xml");
} }
let res = match infer::get_from_path(trimmed)?.map(|f| f.mime_type()) { let res = match infer::get_from_path(trimmed.clone())?.map(|f| f.mime_type()) {
Some(t) if t == "text/plain" => get_mime_by_ext(trimmed), Some(t) if t == "text/plain" => get_mime_by_ext(trimmed),
Some(f) => f, Some(f) => f,
None => get_mime_by_ext(trimmed), None => get_mime_by_ext(trimmed),
@@ -143,9 +132,8 @@ fn get_mime_from_path(trimmed: &str) -> Result<&str> {
} }
/// Get the mime type from a URI using its extension /// Get the mime type from a URI using its extension
fn get_mime_by_ext(trimmed: &str) -> &str { fn get_mime_by_ext(trimmed: &PathBuf) -> &'static str {
let suffix = trimmed.split('.').last(); match trimmed.extension().and_then(|e| e.to_str()) {
match suffix {
Some("bin") => "application/octet-stream", Some("bin") => "application/octet-stream",
Some("css") => "text/css", Some("css") => "text/css",
Some("csv") => "text/csv", Some("csv") => "text/csv",
+1 -8
View File
@@ -18,7 +18,6 @@ pub fn build(
let window = builder.build(event_loop).unwrap(); let window = builder.build(event_loop).unwrap();
let file_handler = cfg.file_drop_handler.take(); let file_handler = cfg.file_drop_handler.take();
let custom_head = cfg.custom_head.clone(); let custom_head = cfg.custom_head.clone();
let resource_dir = cfg.resource_dir.clone();
let index_file = cfg.custom_index.clone(); let index_file = cfg.custom_index.clone();
let root_name = cfg.root_name.clone(); let root_name = cfg.root_name.clone();
@@ -46,13 +45,7 @@ pub fn build(
} }
}) })
.with_custom_protocol(String::from("dioxus"), move |r| { .with_custom_protocol(String::from("dioxus"), move |r| {
protocol::desktop_handler( protocol::desktop_handler(r, custom_head.clone(), index_file.clone(), &root_name)
r,
resource_dir.clone(),
custom_head.clone(),
index_file.clone(),
&root_name,
)
}) })
.with_file_drop_handler(move |window, evet| { .with_file_drop_handler(move |window, evet| {
file_handler file_handler