mirror of
https://github.com/Drop-OSS/drop-app.git
synced 2025-11-14 16:51:18 +10:00
chore: Add CacheError and remove unwraps from fetch_object
Signed-off-by: quexeky <git@quexeky.dev>
This commit is contained in:
24
src-tauri/src/error/cache_error.rs
Normal file
24
src-tauri/src/error/cache_error.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use http::{header::ToStrError, HeaderName};
|
||||||
|
use serde_with::SerializeDisplay;
|
||||||
|
|
||||||
|
use crate::error::remote_access_error::RemoteAccessError;
|
||||||
|
|
||||||
|
#[derive(Debug, SerializeDisplay)]
|
||||||
|
pub enum CacheError {
|
||||||
|
HeaderNotFound(HeaderName),
|
||||||
|
ParseError(ToStrError),
|
||||||
|
Remote(RemoteAccessError)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for CacheError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
CacheError::HeaderNotFound(header_name) => write!(f, "Could not find header {} in cache", header_name),
|
||||||
|
CacheError::ParseError(to_str_error) => write!(f, "Could not parse cache with error {}", to_str_error),
|
||||||
|
CacheError::Remote(remote_access_error) => write!(f, "Cache got remote access error: {}", remote_access_error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -4,3 +4,4 @@ pub mod drop_server_error;
|
|||||||
pub mod library_error;
|
pub mod library_error;
|
||||||
pub mod process_error;
|
pub mod process_error;
|
||||||
pub mod remote_access_error;
|
pub mod remote_access_error;
|
||||||
|
pub mod cache_error;
|
||||||
@ -20,6 +20,7 @@ use crate::database::scan::scan_install_dirs;
|
|||||||
use crate::process::commands::open_process_logs;
|
use crate::process::commands::open_process_logs;
|
||||||
use crate::process::process_handlers::UMU_LAUNCHER_EXECUTABLE;
|
use crate::process::process_handlers::UMU_LAUNCHER_EXECUTABLE;
|
||||||
use crate::remote::commands::auth_initiate_code;
|
use crate::remote::commands::auth_initiate_code;
|
||||||
|
use crate::remote::fetch_object::fetch_object_wrapper;
|
||||||
use crate::{database::db::DatabaseImpls, games::downloads::commands::resume_download};
|
use crate::{database::db::DatabaseImpls, games::downloads::commands::resume_download};
|
||||||
use bitcode::{Decode, Encode};
|
use bitcode::{Decode, Encode};
|
||||||
use client::commands::fetch_state;
|
use client::commands::fetch_state;
|
||||||
@ -47,7 +48,7 @@ use games::commands::{
|
|||||||
};
|
};
|
||||||
use games::downloads::commands::download_game;
|
use games::downloads::commands::download_game;
|
||||||
use games::library::{Game, update_game_configuration};
|
use games::library::{Game, update_game_configuration};
|
||||||
use log::{LevelFilter, debug, info, warn, error};
|
use log::{LevelFilter, debug, info, warn};
|
||||||
use log4rs::Config;
|
use log4rs::Config;
|
||||||
use log4rs::append::console::ConsoleAppender;
|
use log4rs::append::console::ConsoleAppender;
|
||||||
use log4rs::append::file::FileAppender;
|
use log4rs::append::file::FileAppender;
|
||||||
@ -60,7 +61,6 @@ use remote::commands::{
|
|||||||
auth_initiate, fetch_drop_object, gen_drop_url, manual_recieve_handshake, retry_connect,
|
auth_initiate, fetch_drop_object, gen_drop_url, manual_recieve_handshake, retry_connect,
|
||||||
sign_out, use_remote,
|
sign_out, use_remote,
|
||||||
};
|
};
|
||||||
use remote::fetch_object::fetch_object;
|
|
||||||
use remote::server_proto::{handle_server_proto, handle_server_proto_offline};
|
use remote::server_proto::{handle_server_proto, handle_server_proto_offline};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
@ -462,7 +462,7 @@ pub fn run() {
|
|||||||
})
|
})
|
||||||
.register_asynchronous_uri_scheme_protocol("object", move |_ctx, request, responder| {
|
.register_asynchronous_uri_scheme_protocol("object", move |_ctx, request, responder| {
|
||||||
tauri::async_runtime::spawn(async move {
|
tauri::async_runtime::spawn(async move {
|
||||||
fetch_object(request, responder).await;
|
fetch_object_wrapper(request, responder).await;
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.register_asynchronous_uri_scheme_protocol("server", |ctx, request, responder| {
|
.register_asynchronous_uri_scheme_protocol("server", |ctx, request, responder| {
|
||||||
|
|||||||
@ -7,16 +7,16 @@ use std::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
database::{db::borrow_db_checked, models::data::Database},
|
database::{db::borrow_db_checked, models::data::Database},
|
||||||
error::remote_access_error::RemoteAccessError,
|
error::{cache_error::CacheError, remote_access_error::RemoteAccessError},
|
||||||
};
|
};
|
||||||
use bitcode::{Decode, DecodeOwned, Encode};
|
use bitcode::{Decode, DecodeOwned, Encode};
|
||||||
use http::{Response, header::CONTENT_TYPE, response::Builder as ResponseBuilder};
|
use http::{header::{ToStrError, CONTENT_TYPE}, response::Builder as ResponseBuilder, Response};
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! offline {
|
macro_rules! offline {
|
||||||
($var:expr, $func1:expr, $func2:expr, $( $arg:expr ),* ) => {
|
($var:expr, $func1:expr, $func2:expr, $( $arg:expr ),* ) => {
|
||||||
|
|
||||||
async move { if $crate::borrow_db_checked().settings.force_offline || $var.lock().unwrap().status == $crate::AppStatus::Offline {
|
async move { if $crate::borrow_db_checked().settings.force_offline || $crate::state_lock!($var).status == $crate::AppStatus::Offline {
|
||||||
$func2( $( $arg ), *).await
|
$func2( $( $arg ), *).await
|
||||||
} else {
|
} else {
|
||||||
$func1( $( $arg ), *).await
|
$func1( $( $arg ), *).await
|
||||||
@ -87,19 +87,22 @@ impl ObjectCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Response<Vec<u8>>> for ObjectCache {
|
impl TryFrom<Response<Vec<u8>>> for ObjectCache {
|
||||||
fn from(value: Response<Vec<u8>>) -> Self {
|
type Error = CacheError;
|
||||||
ObjectCache {
|
|
||||||
|
fn try_from(value: Response<Vec<u8>>) -> Result<Self, Self::Error> {
|
||||||
|
Ok(ObjectCache {
|
||||||
content_type: value
|
content_type: value
|
||||||
.headers()
|
.headers()
|
||||||
.get(CONTENT_TYPE)
|
.get(CONTENT_TYPE)
|
||||||
.unwrap()
|
.ok_or(CacheError::HeaderNotFound(CONTENT_TYPE))?
|
||||||
.to_str()
|
.to_str()
|
||||||
.unwrap()
|
.map_err(CacheError::ParseError)?
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
body: value.body().clone(),
|
body: value.body().clone(),
|
||||||
expiry: get_sys_time_in_secs() + 60 * 60 * 24,
|
expiry: get_sys_time_in_secs() + 60 * 60 * 24,
|
||||||
}
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<ObjectCache> for Response<Vec<u8>> {
|
impl From<ObjectCache> for Response<Vec<u8>> {
|
||||||
|
|||||||
@ -1,15 +1,26 @@
|
|||||||
use http::{header::CONTENT_TYPE, response::Builder as ResponseBuilder};
|
use http::{header::CONTENT_TYPE, response::Builder as ResponseBuilder, Response};
|
||||||
use log::warn;
|
use log::{debug, warn};
|
||||||
use tauri::UriSchemeResponder;
|
use tauri::UriSchemeResponder;
|
||||||
|
|
||||||
use crate::{database::db::DatabaseImpls, remote::utils::DROP_CLIENT_ASYNC, DB};
|
use crate::{database::db::DatabaseImpls, error::cache_error::CacheError, remote::utils::DROP_CLIENT_ASYNC, DB};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
auth::generate_authorization_header,
|
auth::generate_authorization_header,
|
||||||
cache::{ObjectCache, cache_object, get_cached_object},
|
cache::{ObjectCache, cache_object, get_cached_object},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub async fn fetch_object(request: http::Request<Vec<u8>>, responder: UriSchemeResponder) {
|
pub async fn fetch_object_wrapper(request: http::Request<Vec<u8>>, responder: UriSchemeResponder) {
|
||||||
|
match fetch_object(request).await {
|
||||||
|
Ok(r) => responder.respond(r),
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Cache error: {}", e);
|
||||||
|
responder.respond(Response::new(Vec::new()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn fetch_object(request: http::Request<Vec<u8>>) -> Result<Response<Vec<u8>>, CacheError>
|
||||||
|
{
|
||||||
// Drop leading /
|
// Drop leading /
|
||||||
let object_id = &request.uri().path()[1..];
|
let object_id = &request.uri().path()[1..];
|
||||||
|
|
||||||
@ -17,8 +28,7 @@ pub async fn fetch_object(request: http::Request<Vec<u8>>, responder: UriSchemeR
|
|||||||
if let Ok(cache_result) = &cache_result
|
if let Ok(cache_result) = &cache_result
|
||||||
&& !cache_result.has_expired()
|
&& !cache_result.has_expired()
|
||||||
{
|
{
|
||||||
responder.respond(cache_result.into());
|
return Ok(cache_result.into());
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let header = generate_authorization_header();
|
let header = generate_authorization_header();
|
||||||
@ -26,26 +36,41 @@ pub async fn fetch_object(request: http::Request<Vec<u8>>, responder: UriSchemeR
|
|||||||
let url = format!("{}api/v1/client/object/{object_id}", DB.fetch_base_url());
|
let url = format!("{}api/v1/client/object/{object_id}", DB.fetch_base_url());
|
||||||
let response = client.get(url).header("Authorization", header).send().await;
|
let response = client.get(url).header("Authorization", header).send().await;
|
||||||
|
|
||||||
if response.is_err() {
|
match response {
|
||||||
match cache_result {
|
Ok(r) => {
|
||||||
Ok(cache_result) => responder.respond(cache_result.into()),
|
let resp_builder = ResponseBuilder::new().header(
|
||||||
Err(e) => {
|
CONTENT_TYPE,
|
||||||
warn!("{e}");
|
r.headers()
|
||||||
|
.get("Content-Type")
|
||||||
|
.expect("Failed get Content-Type header"),
|
||||||
|
);
|
||||||
|
let data = match r.bytes().await {
|
||||||
|
Ok(data) => Vec::from(data),
|
||||||
|
Err(e) => {
|
||||||
|
warn!(
|
||||||
|
"Could not get data from cache object {} with error {}",
|
||||||
|
object_id, e
|
||||||
|
);
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let resp = resp_builder.body(data).expect("Failed to build object cache response body");
|
||||||
|
if cache_result.map_or(true, |x| x.has_expired()) {
|
||||||
|
cache_object::<ObjectCache>(object_id, &resp.clone().try_into()?)
|
||||||
|
.expect("Failed to create cached object");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(resp.into());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
debug!("Object fetch failed with error {}. Attempting to download from cache", e);
|
||||||
|
return match cache_result {
|
||||||
|
Ok(cache_result) => Ok(cache_result.into()),
|
||||||
|
Err(e) => {
|
||||||
|
warn!("{e}");
|
||||||
|
Err(CacheError::Remote(e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
let response = response.unwrap();
|
|
||||||
|
|
||||||
let resp_builder = ResponseBuilder::new().header(
|
|
||||||
CONTENT_TYPE,
|
|
||||||
response.headers().get("Content-Type").unwrap(),
|
|
||||||
);
|
|
||||||
let data = Vec::from(response.bytes().await.unwrap());
|
|
||||||
let resp = resp_builder.body(data).unwrap();
|
|
||||||
if cache_result.is_err() || cache_result.unwrap().has_expired() {
|
|
||||||
cache_object::<ObjectCache>(object_id, &resp.clone().into()).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
responder.respond(resp);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,9 +50,10 @@ fn fetch_certificates() -> Vec<Certificate> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.read_to_end(&mut buf)
|
.read_to_end(&mut buf)
|
||||||
.expect(&format!(
|
.unwrap_or_else(|e| panic!(
|
||||||
"Failed to read to end of certificate file {}",
|
"Failed to read to end of certificate file {} with error {}",
|
||||||
c.path().display()
|
c.path().display(),
|
||||||
|
e
|
||||||
));
|
));
|
||||||
|
|
||||||
match Certificate::from_pem_bundle(&buf) {
|
match Certificate::from_pem_bundle(&buf) {
|
||||||
|
|||||||
Reference in New Issue
Block a user