Skip to content

Commit 210dd08

Browse files
committed
feat(kotlin): pass http cacache dir in initializeNative
feat(kotlin): make sure cacache dir is writable Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
1 parent 275985c commit 210dd08

File tree

3 files changed

+77
-37
lines changed

3 files changed

+77
-37
lines changed

stremio-core-kotlin/src/commonMain/rust/env/fetch.rs

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::{
22
convert::TryFrom,
3-
env::{self, VarError},
4-
path::PathBuf,
3+
fs,
4+
path::{Path, PathBuf},
55
time::Duration,
66
};
77

@@ -15,9 +15,27 @@ use serde::{Deserialize, Serialize};
1515
use serde_json::Deserializer;
1616
use stremio_core::runtime::{EnvError, EnvFutureExt, TryEnvFuture};
1717

18+
use crate::CACHE_DIR;
19+
1820
static CLIENT_WITH_CACHE: OnceCell<ClientWithMiddleware> = OnceCell::new();
1921
static CLIENT_WITHOUT_CACHE: OnceCell<ClientWithMiddleware> = OnceCell::new();
2022

23+
/// 1. Creates all directories for the path if they don't exist
24+
/// 2. writes a test file to see if the app has write permission
25+
/// 3. deletes the file to clean up and check delete permission
26+
fn ensure_cache_dir_permissions(path: &Path) -> std::io::Result<()> {
27+
// Ensure directory exists
28+
fs::create_dir_all(path)?;
29+
30+
// Attempt to create a temp file
31+
let test_file = path.join(".cache_permission_test");
32+
fs::write(&test_file, b"test")?;
33+
34+
// Attempt to delete it
35+
fs::remove_file(&test_file)?;
36+
Ok(())
37+
}
38+
2139
pub fn fetch<IN: Serialize + Send + 'static, OUT: for<'de> Deserialize<'de> + Send + 'static>(
2240
request: Request<IN>,
2341
) -> TryEnvFuture<OUT> {
@@ -33,48 +51,49 @@ pub fn fetch<IN: Serialize + Send + 'static, OUT: for<'de> Deserialize<'de> + Se
3351
Err(error) => return future::err(EnvError::Fetch(error.to_string())).boxed_env(),
3452
};
3553

36-
let client = match env::var("TMPDIR") {
37-
Ok(tmpdir) => {
38-
CLIENT_WITH_CACHE.get_or_init(|| {
39-
let tmpdir_path = PathBuf::from(tmpdir);
40-
let cacache_path = tmpdir_path.join("http-cacache");
41-
let connection_timeout = Duration::from_secs(30);
54+
let fut = async {
55+
// should always be set as we set it on initializeNative
56+
let client = CACHE_DIR.get().cloned().flatten().and_then(|cache_dir| {
57+
let cacache_path = PathBuf::from(cache_dir);
58+
59+
match ensure_cache_dir_permissions(&cacache_path) {
60+
Ok(_) => {
4261

43-
tracing::info!(tmpdir_path = %tmpdir_path.display(), cacache_path = %cacache_path.display(), ?connection_timeout, "Client Cacache middleware will be initialized...");
62+
let cacache_manager = CACacheManager { path: cacache_path.clone() };
63+
64+
Some(CLIENT_WITH_CACHE.get_or_init(|| {
65+
let connection_timeout = Duration::from_secs(30);
66+
67+
tracing::info!(cacache_path = %cacache_path.display(), ?connection_timeout, "Client CACache middleware will be initialized...");
68+
ClientBuilder::new(
69+
Client::builder()
70+
.connect_timeout(connection_timeout)
71+
.use_rustls_tls()
72+
.build()
73+
.unwrap_or_default(),
74+
)
75+
.with(Cache(HttpCache {
76+
mode: CacheMode::Default,
77+
manager: cacache_manager,
78+
options: HttpCacheOptions::default(),
79+
}))
80+
.build()
81+
}))
82+
},
83+
Err(_) => None,
84+
}
85+
}).unwrap_or_else(|| { CLIENT_WITHOUT_CACHE.get_or_init(|| {
4486
ClientBuilder::new(
4587
Client::builder()
46-
.connect_timeout(connection_timeout)
88+
.connect_timeout(Duration::from_secs(30))
4789
.use_rustls_tls()
4890
.build()
4991
.unwrap_or_default(),
5092
)
51-
.with(Cache(HttpCache::<CACacheManager> {
52-
mode: CacheMode::Default,
53-
manager: CACacheManager {
54-
path: cacache_path,
55-
},
56-
options: HttpCacheOptions::default(),
57-
}))
5893
.build()
5994
})
60-
}
61-
Err(err) => CLIENT_WITHOUT_CACHE.get_or_init(|| {
62-
// we log the error only once when initializing the Client.
63-
if let VarError::NotUnicode(_) = &err {
64-
tracing::error!(?err, "TMPDIR env. variable is not a valid Unicode, no Client Cacache middleware will be used");
65-
}
66-
ClientBuilder::new(
67-
Client::builder()
68-
.connect_timeout(Duration::from_secs(30))
69-
.use_rustls_tls()
70-
.build()
71-
.unwrap_or_default(),
72-
)
73-
.build()
74-
})
75-
};
95+
});
7696

77-
let fut = async {
7897
let resp = client
7998
.execute(request)
8099
.map_err(|error| EnvError::Fetch(error.to_string()))

stremio-core-kotlin/src/commonMain/rust/stremio_core_kotlin.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ use std::{io::Cursor, os::raw::c_void, sync::RwLock};
66
use enclose::enclose;
77
use futures::{future, StreamExt};
88
use jni::{
9-
objects::{JClass, JObject},
9+
objects::{JClass, JObject, JString},
1010
sys::{jbyteArray, jint, jobject, JNI_VERSION_1_6},
1111
JNIEnv, JavaVM,
1212
};
1313

14-
use once_cell::sync::Lazy;
14+
use once_cell::sync::{Lazy, OnceCell};
1515
use prost::Message;
1616

1717
use stremio_core::{
@@ -47,6 +47,9 @@ pub mod subscriber;
4747
static RUNTIME: Lazy<RwLock<Option<Loadable<Runtime<AndroidEnv, AndroidModel>, EnvError>>>> =
4848
Lazy::new(Default::default);
4949

50+
/// The CACache directory on [`Java_com_stremio_core_Core_initializeNative`] of `stremio-core-kotlin`.
51+
pub static CACHE_DIR: OnceCell<Option<String>> = OnceCell::new();
52+
5053
/// Initialize panic hook to send data to Kotlin
5154
#[no_mangle]
5255
pub unsafe extern "C" fn JNI_OnLoad(_: JavaVM, _: *mut c_void) -> jint {
@@ -94,11 +97,30 @@ pub unsafe extern "C" fn Java_com_stremio_core_Core_initializeNative(
9497
env: JNIEnv,
9598
_class: JClass,
9699
storage: JObject,
100+
cache_dir: JString,
97101
) -> jobject {
98102
if RUNTIME.read().expect("RUNTIME read failed").is_some() {
99103
return JObject::null().into_inner();
100104
};
101105

106+
{
107+
let cache_dir: Option<String> = match env.get_string(cache_dir) {
108+
Ok(java_str) => Some(java_str.into()),
109+
// if null, disable cache
110+
Err(jni::errors::Error::NullPtr(_)) => None,
111+
// on any other error, log it and continue
112+
Err(err) => {
113+
error!(?err, "The passed cache dir failed to be converted to a string, no caching will be used for requests");
114+
None
115+
}
116+
};
117+
118+
// Set the cache directory only once on initialization!
119+
CACHE_DIR
120+
.set(cache_dir)
121+
.expect("Cache directory should be set only once!");
122+
}
123+
102124
let init_result = AndroidEnv::exec_sync(AndroidEnv::init(&env, storage));
103125
match init_result {
104126
Ok(_) => {

stremio-core-kotlin/src/commonMain/rust/stremio_core_kotlin/subscriber.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
//! A `tracing` subscriber for Kotlin.
2-
//!
32
43
use core::fmt::{self, Write};
54

0 commit comments

Comments
 (0)