fix(cloud-files): Timeout-Ursachen im FETCH_DATA-Callback beheben
- HTTP-Client bekommt 60s-Timeout (statt unendlich) - Bei Send-/Netzwerkfehler wird CfExecute immer mit Failure-Status abgeschlossen, damit Explorer nicht ins OS-Timeout laeuft - Wenn Server kein Range unterstuetzt (200 statt 206), wird aus dem Full-Body der angeforderte Bereich herausgeschnitten und die tatsaechliche Laenge an CfExecute uebergeben - Fehler werden in <mount>\.minicloud-cloudfiles.log geschrieben, damit man das Problem bei Timeout ueberhaupt sehen kann Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
204dbb6ab5
commit
6274567219
|
|
@ -171,19 +171,40 @@ unsafe extern "system" fn on_fetch_data(
|
|||
// HTTPS-Download im separaten Thread (Callback darf nicht blockieren).
|
||||
let ctx = ctx_snapshot();
|
||||
std::thread::spawn(move || {
|
||||
let _ = transfer_range(connection_key, transfer_key, file_id, offset, length, ctx);
|
||||
if let Err(e) = transfer_range(connection_key, transfer_key, file_id, offset, length, &ctx)
|
||||
{
|
||||
log_err(&ctx.mount_point, &format!(
|
||||
"fetch file_id={file_id} offset={offset} len={length} FAILED: {e}"
|
||||
));
|
||||
// Garantiert Fehler-Completion, damit Windows nicht in Timeout laeuft.
|
||||
let _ = complete_transfer(connection_key, transfer_key, None, offset, length);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn log_err(mount: &Path, msg: &str) {
|
||||
use std::io::Write;
|
||||
let log = mount.join(".minicloud-cloudfiles.log");
|
||||
if let Ok(mut f) = std::fs::OpenOptions::new().create(true).append(true).open(&log) {
|
||||
let _ = writeln!(f, "[{}] {}", chrono::Utc::now().to_rfc3339(), msg);
|
||||
}
|
||||
}
|
||||
|
||||
fn transfer_range(
|
||||
connection_key: CF::CF_CONNECTION_KEY,
|
||||
transfer_key: i64,
|
||||
file_id: i64,
|
||||
offset: i64,
|
||||
length: u64,
|
||||
ctx: CloudContext,
|
||||
ctx: &CloudContext,
|
||||
) -> Result<(), String> {
|
||||
let client = reqwest::blocking::Client::new();
|
||||
if ctx.server_url.is_empty() || ctx.access_token.is_empty() {
|
||||
return Err("CloudContext nicht gesetzt (Server/Token leer)".into());
|
||||
}
|
||||
let client = reqwest::blocking::Client::builder()
|
||||
.timeout(std::time::Duration::from_secs(60))
|
||||
.build()
|
||||
.map_err(|e| format!("client: {e}"))?;
|
||||
let url = format!(
|
||||
"{}/api/files/{}/download",
|
||||
ctx.server_url.trim_end_matches('/'),
|
||||
|
|
@ -195,22 +216,26 @@ fn transfer_range(
|
|||
.bearer_auth(&ctx.access_token)
|
||||
.header("Range", &range)
|
||||
.send()
|
||||
.map_err(|e| format!("download: {e}"))?;
|
||||
.map_err(|e| format!("send: {e}"))?;
|
||||
let status = resp.status();
|
||||
if !status.is_success() && status.as_u16() != 206 {
|
||||
let _ = complete_transfer(connection_key, transfer_key, None, offset, length);
|
||||
return Err(format!("HTTP {}", status));
|
||||
}
|
||||
let bytes = resp
|
||||
.bytes()
|
||||
.map_err(|e: reqwest::Error| e.to_string())?;
|
||||
complete_transfer(
|
||||
connection_key,
|
||||
transfer_key,
|
||||
Some(&bytes),
|
||||
offset,
|
||||
length,
|
||||
)
|
||||
let bytes = resp.bytes().map_err(|e: reqwest::Error| e.to_string())?;
|
||||
// Wenn Server kein Range unterstuetzt und volle Datei liefert,
|
||||
// aus dem Body den angeforderten Bereich ausschneiden.
|
||||
let slice: &[u8] = if status.as_u16() == 206 {
|
||||
&bytes[..]
|
||||
} else {
|
||||
let start = offset as usize;
|
||||
let end = (start + length as usize).min(bytes.len());
|
||||
if start >= bytes.len() {
|
||||
&[]
|
||||
} else {
|
||||
&bytes[start..end]
|
||||
}
|
||||
};
|
||||
complete_transfer(connection_key, transfer_key, Some(slice), offset, slice.len() as u64)
|
||||
}
|
||||
|
||||
fn complete_transfer(
|
||||
|
|
|
|||
Loading…
Reference in New Issue