From 03fc92c8304efe897a09de6e82da2fea7f69ecab Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Sun, 6 Feb 2022 23:54:47 -0300 Subject: [PATCH] fix(core): resolve request instead of panicking on asset protocol (#3347) --- .changes/fix-asset-protocol-panicking.md | 5 ++ core/tauri/src/manager.rs | 95 ++++++++++++++++-------- examples/api/src-tauri/tauri.conf.json | 2 +- 3 files changed, 69 insertions(+), 33 deletions(-) create mode 100644 .changes/fix-asset-protocol-panicking.md diff --git a/.changes/fix-asset-protocol-panicking.md b/.changes/fix-asset-protocol-panicking.md new file mode 100644 index 000000000..332a9861e --- /dev/null +++ b/.changes/fix-asset-protocol-panicking.md @@ -0,0 +1,5 @@ +--- +"tauri": patch +--- + +Resolve `asset` protocol HTTP request instead of panicking if the file does not exist or cannot be read. diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index 8da0d8aa8..5dc65e8c7 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -519,34 +519,56 @@ impl WindowManager { .to_string(); if !asset_scope.is_allowed(&path) { - return HttpResponseBuilder::new() - .status(403) - .body(Vec::with_capacity(0)); + #[cfg(debug_assertions)] + eprintln!("asset protocol not configured to allow the path: {}", path); + return HttpResponseBuilder::new().status(403).body(Vec::new()); } - let path_for_data = path.clone(); + let path_ = path.clone(); let mut response = HttpResponseBuilder::new().header("Access-Control-Allow-Origin", &window_origin); // handle 206 (partial range) http request - if let Some(range) = request.headers().get("range").cloned() { - let mut status_code = 200; - let path_for_data = path_for_data.clone(); + if let Some(range) = request + .headers() + .get("range") + .and_then(|r| r.to_str().map(|r| r.to_string()).ok()) + { let (headers, status_code, data) = crate::async_runtime::safe_block_on(async move { let mut headers = HashMap::new(); let mut buf = Vec::new(); - let mut file = tokio::fs::File::open(path_for_data.clone()).await.unwrap(); + // open the file + let mut file = match tokio::fs::File::open(path_.clone()).await { + Ok(file) => file, + Err(e) => { + #[cfg(debug_assertions)] + eprintln!("Failed to open asset: {}", e); + return (headers, 404, buf); + } + }; // Get the file size - let file_size = file.metadata().await.unwrap().len(); + let file_size = match file.metadata().await { + Ok(metadata) => metadata.len(), + Err(e) => { + #[cfg(debug_assertions)] + eprintln!("Failed to read asset metadata: {}", e); + return (headers, 404, buf); + } + }; // parse the range - let range = - crate::runtime::http::HttpRange::parse(range.to_str().unwrap(), file_size).unwrap(); + let range = match crate::runtime::http::HttpRange::parse(&range, file_size) { + Ok(r) => r, + Err(e) => { + #[cfg(debug_assertions)] + eprintln!("Failed to parse range: {:?}", e); + return (headers, 400, buf); + } + }; // FIXME: Support multiple ranges // let support only 1 range for now - let first_range = range.first(); - if let Some(range) = first_range { + let status_code = if let Some(range) = range.first() { let mut real_length = range.length; // prevent max_length; // specially on webview2 @@ -559,8 +581,6 @@ impl WindowManager { // last byte we are reading, the length of the range include the last byte // who should be skipped on the header let last_byte = range.start + real_length - 1; - // partial content - status_code = 206; headers.insert("Connection", "Keep-Alive".into()); headers.insert("Accept-Ranges", "bytes".into()); @@ -570,12 +590,22 @@ impl WindowManager { format!("bytes {}-{}/{}", range.start, last_byte, file_size), ); - file - .seek(std::io::SeekFrom::Start(range.start)) - .await - .unwrap(); - file.take(real_length).read_to_end(&mut buf).await.unwrap(); - } + if let Err(e) = file.seek(std::io::SeekFrom::Start(range.start)).await { + #[cfg(debug_assertions)] + eprintln!("Failed to seek file to {}: {}", range.start, e); + return (headers, 422, buf); + } + + if let Err(e) = file.take(real_length).read_to_end(&mut buf).await { + #[cfg(debug_assertions)] + eprintln!("Failed read file: {}", e); + return (headers, 422, buf); + } + // partial content + 206 + } else { + 200 + }; (headers, status_code, buf) }); @@ -584,19 +614,20 @@ impl WindowManager { response = response.header(k, v); } - if !data.is_empty() { - let mime_type = MimeType::parse(&data, &path); - return response.mimetype(&mime_type).status(status_code).body(data); - } - } - - if let Ok(data) = - crate::async_runtime::safe_block_on(async move { tokio::fs::read(path_for_data).await }) - { let mime_type = MimeType::parse(&data, &path); - response.mimetype(&mime_type).body(data) + response.mimetype(&mime_type).status(status_code).body(data) } else { - response.status(404).body(Vec::new()) + match crate::async_runtime::safe_block_on(async move { tokio::fs::read(path_).await }) { + Ok(data) => { + let mime_type = MimeType::parse(&data, &path); + response.mimetype(&mime_type).body(data) + } + Err(e) => { + #[cfg(debug_assertions)] + eprintln!("Failed to read file: {}", e); + response.status(404).body(Vec::new()) + } + } } }); } diff --git a/examples/api/src-tauri/tauri.conf.json b/examples/api/src-tauri/tauri.conf.json index 5bf35308c..612a21735 100644 --- a/examples/api/src-tauri/tauri.conf.json +++ b/examples/api/src-tauri/tauri.conf.json @@ -101,7 +101,7 @@ }, "protocol": { "asset": true, - "assetScope": ["$RESOURCE/**"] + "assetScope": ["$RESOURCE/**", "$APP/**"] }, "http": { "scope": ["https://jsonplaceholder.typicode.com/todos/*"]