Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0131d8d4fd | |||
| d2ca3f1918 | |||
| 812b40b42f | |||
| b7ec3f4ad3 |
@@ -23,19 +23,36 @@ class SftpProvider(private val account: CloudAccount, private val credentialStor
|
|||||||
private val password = creds["password"]?.jsonPrimitive?.content
|
private val password = creds["password"]?.jsonPrimitive?.content
|
||||||
private val privateKey = creds["private_key"]?.jsonPrimitive?.content
|
private val privateKey = creds["private_key"]?.jsonPrimitive?.content
|
||||||
|
|
||||||
private fun <T> withSftp(block: (SFTPClient) -> T): T {
|
// Persistent SSH connection reused across all operations in the provider's lifetime.
|
||||||
|
// Each call to withSftp checks liveness and reconnects if the connection dropped.
|
||||||
|
// This eliminates the per-operation connect/auth/disconnect cycle that caused
|
||||||
|
// 100+ SSH handshakes during a recursive directory listing + file-transfer sync,
|
||||||
|
// leading to connection timeouts on large folder trees (e.g. 69 subdirectories).
|
||||||
|
private var sshClient: SSHClient? = null
|
||||||
|
|
||||||
|
private fun getOrCreateSsh(): SSHClient {
|
||||||
|
val existing = sshClient
|
||||||
|
if (existing != null && existing.isConnected && existing.isAuthenticated) return existing
|
||||||
val ssh = SSHClient()
|
val ssh = SSHClient()
|
||||||
ssh.addHostKeyVerifier(TofuHostKeyVerifier(credentialStore))
|
ssh.addHostKeyVerifier(TofuHostKeyVerifier(credentialStore))
|
||||||
ssh.connect(host, port)
|
ssh.connect(host, port)
|
||||||
try {
|
if (!privateKey.isNullOrBlank()) {
|
||||||
if (!privateKey.isNullOrBlank()) {
|
ssh.authPublickey(username, ssh.loadKeys(privateKey, null, null))
|
||||||
ssh.authPublickey(username, ssh.loadKeys(privateKey, null, null))
|
} else {
|
||||||
} else {
|
ssh.authPassword(username, password ?: "")
|
||||||
ssh.authPassword(username, password ?: "")
|
}
|
||||||
}
|
sshClient = ssh
|
||||||
return ssh.newSFTPClient().use(block)
|
return ssh
|
||||||
} finally {
|
}
|
||||||
ssh.disconnect()
|
|
||||||
|
private fun <T> withSftp(block: (SFTPClient) -> T): T {
|
||||||
|
return try {
|
||||||
|
getOrCreateSsh().newSFTPClient().use(block)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// Connection may have gone stale — reset and retry once with a fresh connection.
|
||||||
|
runCatching { sshClient?.disconnect() }
|
||||||
|
sshClient = null
|
||||||
|
getOrCreateSsh().newSFTPClient().use(block)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ open class WebDavProvider(protected val account: CloudAccount) : CloudProvider {
|
|||||||
val pass = creds["password"]?.jsonPrimitive?.content ?: ""
|
val pass = creds["password"]?.jsonPrimitive?.content ?: ""
|
||||||
OkHttpClient.Builder()
|
OkHttpClient.Builder()
|
||||||
.connectTimeout(15, TimeUnit.SECONDS)
|
.connectTimeout(15, TimeUnit.SECONDS)
|
||||||
.readTimeout(30, TimeUnit.SECONDS)
|
.readTimeout(5, TimeUnit.MINUTES)
|
||||||
|
.writeTimeout(5, TimeUnit.MINUTES)
|
||||||
.addInterceptor { chain ->
|
.addInterceptor { chain ->
|
||||||
val req = chain.request().newBuilder()
|
val req = chain.request().newBuilder()
|
||||||
.header("Authorization", Credentials.basic(user, pass))
|
.header("Authorization", Credentials.basic(user, pass))
|
||||||
@@ -149,7 +150,12 @@ open class WebDavProvider(protected val account: CloudAccount) : CloudProvider {
|
|||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val req = Request.Builder().url(url(remotePath)).method("MKCOL", null).build()
|
val req = Request.Builder().url(url(remotePath)).method("MKCOL", null).build()
|
||||||
client.newCall(req).execute().use { resp ->
|
client.newCall(req).execute().use { resp ->
|
||||||
if (!resp.isSuccessful && resp.code != 405) throw Exception("MKCOL HTTP ${resp.code}")
|
// 405 = directory already exists (most servers)
|
||||||
|
// 423 = Locked — SFTPGo returns this when the dir exists and has a lock;
|
||||||
|
// treat as "already there", not a failure, so uploads inside it proceed.
|
||||||
|
if (!resp.isSuccessful && resp.code != 405 && resp.code != 423) {
|
||||||
|
throw Exception("MKCOL HTTP ${resp.code}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,2 +1,2 @@
|
|||||||
VERSION_NAME=1.0.70
|
VERSION_NAME=1.0.73
|
||||||
VERSION_CODE=70
|
VERSION_CODE=73
|
||||||
|
|||||||
Reference in New Issue
Block a user