asset.rs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. // Copyright 2019-2023 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. use crate::path::SafePathBuf;
  5. use crate::scope::FsScope;
  6. use crate::window::UriSchemeProtocolHandler;
  7. use http::{header::*, status::StatusCode, Request, Response};
  8. use http_range::HttpRange;
  9. use rand::RngCore;
  10. use std::{borrow::Cow, io::SeekFrom};
  11. use tauri_utils::debug_eprintln;
  12. use tauri_utils::mime_type::MimeType;
  13. use tokio::fs::File;
  14. use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
  15. pub fn get(scope: FsScope, window_origin: String) -> UriSchemeProtocolHandler {
  16. Box::new(
  17. move |request, responder| match get_response(request, &scope, &window_origin) {
  18. Ok(response) => responder.respond(response),
  19. Err(e) => responder.respond(
  20. http::Response::builder()
  21. .status(http::StatusCode::BAD_REQUEST)
  22. .header(CONTENT_TYPE, mime::TEXT_PLAIN.essence_str())
  23. .body(e.to_string().as_bytes().to_vec())
  24. .unwrap(),
  25. ),
  26. },
  27. )
  28. }
  29. fn get_response(
  30. request: Request<Vec<u8>>,
  31. scope: &FsScope,
  32. window_origin: &str,
  33. ) -> Result<Response<Cow<'static, [u8]>>, Box<dyn std::error::Error>> {
  34. // skip leading `/`
  35. let path = percent_encoding::percent_decode(request.uri().path()[1..].as_bytes())
  36. .decode_utf8_lossy()
  37. .to_string();
  38. if let Err(e) = SafePathBuf::new(path.clone().into()) {
  39. debug_eprintln!("asset protocol path \"{}\" is not valid: {}", path, e);
  40. return Response::builder()
  41. .status(403)
  42. .body(Vec::new().into())
  43. .map_err(Into::into);
  44. }
  45. if !scope.is_allowed(&path) {
  46. debug_eprintln!("asset protocol not configured to allow the path: {}", path);
  47. return Response::builder()
  48. .status(403)
  49. .body(Vec::new().into())
  50. .map_err(Into::into);
  51. }
  52. let mut resp = Response::builder().header("Access-Control-Allow-Origin", window_origin);
  53. let (mut file, len, mime_type, read_bytes) = crate::async_runtime::safe_block_on(async move {
  54. let mut file = File::open(&path).await?;
  55. // get file length
  56. let len = {
  57. let old_pos = file.stream_position().await?;
  58. let len = file.seek(SeekFrom::End(0)).await?;
  59. file.seek(SeekFrom::Start(old_pos)).await?;
  60. len
  61. };
  62. // get file mime type
  63. let (mime_type, read_bytes) = {
  64. let nbytes = len.min(8192);
  65. let mut magic_buf = Vec::with_capacity(nbytes as usize);
  66. let old_pos = file.stream_position().await?;
  67. (&mut file).take(nbytes).read_to_end(&mut magic_buf).await?;
  68. file.seek(SeekFrom::Start(old_pos)).await?;
  69. (
  70. MimeType::parse(&magic_buf, &path),
  71. // return the `magic_bytes` if we read the whole file
  72. // to avoid reading it again later if this is not a range request
  73. if len < 8192 { Some(magic_buf) } else { None },
  74. )
  75. };
  76. Ok::<(File, u64, String, Option<Vec<u8>>), anyhow::Error>((file, len, mime_type, read_bytes))
  77. })?;
  78. resp = resp.header(CONTENT_TYPE, &mime_type);
  79. // handle 206 (partial range) http requests
  80. let response = if let Some(range_header) = request
  81. .headers()
  82. .get("range")
  83. .and_then(|r| r.to_str().map(|r| r.to_string()).ok())
  84. {
  85. resp = resp.header(ACCEPT_RANGES, "bytes");
  86. let not_satisfiable = || {
  87. Response::builder()
  88. .status(StatusCode::RANGE_NOT_SATISFIABLE)
  89. .header(CONTENT_RANGE, format!("bytes */{len}"))
  90. .body(vec![].into())
  91. .map_err(Into::into)
  92. };
  93. // parse range header
  94. let ranges = if let Ok(ranges) = HttpRange::parse(&range_header, len) {
  95. ranges
  96. .iter()
  97. // map the output to spec range <start-end>, example: 0-499
  98. .map(|r| (r.start, r.start + r.length - 1))
  99. .collect::<Vec<_>>()
  100. } else {
  101. return not_satisfiable();
  102. };
  103. /// The Maximum bytes we send in one range
  104. const MAX_LEN: u64 = 1000 * 1024;
  105. // single-part range header
  106. if ranges.len() == 1 {
  107. let &(start, mut end) = ranges.first().unwrap();
  108. // check if a range is not satisfiable
  109. //
  110. // this should be already taken care of by the range parsing library
  111. // but checking here again for extra assurance
  112. if start >= len || end >= len || end < start {
  113. return not_satisfiable();
  114. }
  115. // adjust end byte for MAX_LEN
  116. end = start + (end - start).min(len - start).min(MAX_LEN - 1);
  117. // calculate number of bytes needed to be read
  118. let nbytes = end + 1 - start;
  119. let buf = crate::async_runtime::safe_block_on(async move {
  120. let mut buf = Vec::with_capacity(nbytes as usize);
  121. file.seek(SeekFrom::Start(start)).await?;
  122. file.take(nbytes).read_to_end(&mut buf).await?;
  123. Ok::<Vec<u8>, anyhow::Error>(buf)
  124. })?;
  125. resp = resp.header(CONTENT_RANGE, format!("bytes {start}-{end}/{len}"));
  126. resp = resp.header(CONTENT_LENGTH, end + 1 - start);
  127. resp = resp.status(StatusCode::PARTIAL_CONTENT);
  128. resp.body(buf.into())
  129. } else {
  130. let ranges = ranges
  131. .iter()
  132. .filter_map(|&(start, mut end)| {
  133. // filter out unsatisfiable ranges
  134. //
  135. // this should be already taken care of by the range parsing library
  136. // but checking here again for extra assurance
  137. if start >= len || end >= len || end < start {
  138. None
  139. } else {
  140. // adjust end byte for MAX_LEN
  141. end = start + (end - start).min(len - start).min(MAX_LEN - 1);
  142. Some((start, end))
  143. }
  144. })
  145. .collect::<Vec<_>>();
  146. let boundary = random_boundary();
  147. let boundary_sep = format!("\r\n--{boundary}\r\n");
  148. let boundary_closer = format!("\r\n--{boundary}\r\n");
  149. resp = resp.header(
  150. CONTENT_TYPE,
  151. format!("multipart/byteranges; boundary={boundary}"),
  152. );
  153. let buf = crate::async_runtime::safe_block_on(async move {
  154. // multi-part range header
  155. let mut buf = Vec::new();
  156. for (end, start) in ranges {
  157. // a new range is being written, write the range boundary
  158. buf.write_all(boundary_sep.as_bytes()).await?;
  159. // write the needed headers `Content-Type` and `Content-Range`
  160. buf
  161. .write_all(format!("{CONTENT_TYPE}: {mime_type}\r\n").as_bytes())
  162. .await?;
  163. buf
  164. .write_all(format!("{CONTENT_RANGE}: bytes {start}-{end}/{len}\r\n").as_bytes())
  165. .await?;
  166. // write the separator to indicate the start of the range body
  167. buf.write_all("\r\n".as_bytes()).await?;
  168. // calculate number of bytes needed to be read
  169. let nbytes = end + 1 - start;
  170. let mut local_buf = Vec::with_capacity(nbytes as usize);
  171. file.seek(SeekFrom::Start(start)).await?;
  172. (&mut file).take(nbytes).read_to_end(&mut local_buf).await?;
  173. buf.extend_from_slice(&local_buf);
  174. }
  175. // all ranges have been written, write the closing boundary
  176. buf.write_all(boundary_closer.as_bytes()).await?;
  177. Ok::<Vec<u8>, anyhow::Error>(buf)
  178. })?;
  179. resp.body(buf.into())
  180. }
  181. } else {
  182. // avoid reading the file if we already read it
  183. // as part of mime type detection
  184. let buf = if let Some(b) = read_bytes {
  185. b
  186. } else {
  187. crate::async_runtime::safe_block_on(async move {
  188. let mut local_buf = Vec::with_capacity(len as usize);
  189. file.read_to_end(&mut local_buf).await?;
  190. Ok::<Vec<u8>, anyhow::Error>(local_buf)
  191. })?
  192. };
  193. resp = resp.header(CONTENT_LENGTH, len);
  194. resp.body(buf.into())
  195. };
  196. response.map_err(Into::into)
  197. }
  198. fn random_boundary() -> String {
  199. let mut x = [0_u8; 30];
  200. rand::thread_rng().fill_bytes(&mut x);
  201. (x[..])
  202. .iter()
  203. .map(|&x| format!("{x:x}"))
  204. .fold(String::new(), |mut a, x| {
  205. a.push_str(x.as_str());
  206. a
  207. })
  208. }