diff --git a/core/dr-sync-nextcloud/src/lib.rs b/core/dr-sync-nextcloud/src/lib.rs index 8db855e..7cda5e9 100644 --- a/core/dr-sync-nextcloud/src/lib.rs +++ b/core/dr-sync-nextcloud/src/lib.rs @@ -637,11 +637,23 @@ pub fn http_client(user_agent: &str) -> Result { // webpki-roots set is still installed alongside. .tls_certs_only(extra_roots()) // A request that hangs forever is indistinguishable from a worker that - // died, and cost a long time to tell apart once. Connect and total - // timeouts turn that into an error the UI can show. Generous enough for - // a slow phone on mobile data; the login poll has its own deadline. + // died, and cost a long time to tell apart once. These turn that into + // an error the UI can show. .connect_timeout(std::time::Duration::from_secs(15)) - .timeout(std::time::Duration::from_secs(60)) + // **Inactivity, not duration.** This was a 60-second *total* timeout, + // which is not a hang detector at all — it is a floor on link speed. + // A face shard runs to 25 MB, so it demanded a sustained 425 KB/s or + // the transfer failed; and having failed it was retried on the next + // pass, and failed again, for ever. A tablet on ordinary wifi could + // therefore never finish adopting a library's faces, and nothing said + // why: each attempt looked like a network blip rather than an + // arithmetic impossibility. + // + // `read_timeout` fires when *no bytes arrive* for the given period, + // which is the condition actually worth failing on. A slow transfer + // that is still moving now finishes, however long it takes, while a + // connection that has genuinely died is still caught in a minute. + .read_timeout(std::time::Duration::from_secs(60)) .build() .map_err(|e| RemoteError::Network(e.to_string())) }