Catch out 507 HTTP error when using WebDAV

Nextcloud has a bug that lets us write chunked transfers over quota:
https://github.com/nextcloud/server/issues/7993

However, when we upload small files, we can get the proper 507 response and thus detect out of space situations and warn the user about them.
This commit is contained in:
Torsten Grote 2024-05-09 14:28:03 -03:00 committed by Chirayu Desai
parent 066b147315
commit bb562a4cb2
7 changed files with 34 additions and 5 deletions

View file

@ -32,7 +32,7 @@ class KoinInstrumentationTestApp : App() {
single { spyk(BackupNotificationManager(context)) }
single { spyk(FullBackup(get(), get(), get(), get(), get())) }
single { spyk(KVBackup(get(), get(), get(), get(), get())) }
single { spyk(KVBackup(get(), get(), get(), get(), get(), get())) }
single { spyk(InputFactory()) }
single { spyk(FullRestore(get(), get(), get(), get(), get())) }

View file

@ -9,6 +9,7 @@ import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
import androidx.annotation.WorkerThread
import at.bitfire.dav4jvm.exception.HttpException
import java.io.IOException
abstract class StorageProperties<T> {
@ -37,5 +38,12 @@ abstract class StorageProperties<T> {
}
fun Exception.isOutOfSpace(): Boolean {
return this is IOException && message?.contains("No space left on device") == true
return when (this) {
is IOException -> message?.contains("No space left on device") == true ||
(cause as? HttpException)?.code == 507
is HttpException -> code == 507
else -> false
}
}

View file

@ -382,6 +382,7 @@ internal class BackupCoordinator(
onPackageBackedUp(packageInfo, BackupType.FULL, size)
} catch (e: Exception) {
Log.e(TAG, "Error calling onPackageBackedUp for $packageName", e)
if (e.isOutOfSpace()) nm.onInsufficientSpaceError()
result = TRANSPORT_PACKAGE_REJECTED
}
result

View file

@ -19,6 +19,7 @@ val backupModule = module {
KVBackup(
pluginManager = get(),
settingsManager = get(),
nm = get(),
inputFactory = get(),
crypto = get(),
dbManager = get(),

View file

@ -14,7 +14,9 @@ import com.stevesoltys.seedvault.crypto.Crypto
import com.stevesoltys.seedvault.header.VERSION
import com.stevesoltys.seedvault.header.getADForKV
import com.stevesoltys.seedvault.plugins.StoragePluginManager
import com.stevesoltys.seedvault.plugins.isOutOfSpace
import com.stevesoltys.seedvault.settings.SettingsManager
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
import java.io.IOException
import java.util.zip.GZIPOutputStream
@ -34,6 +36,7 @@ private val TAG = KVBackup::class.java.simpleName
internal class KVBackup(
private val pluginManager: StoragePluginManager,
private val settingsManager: SettingsManager,
private val nm: BackupNotificationManager,
private val inputFactory: InputFactory,
private val crypto: Crypto,
private val dbManager: KvDbManager,
@ -214,6 +217,7 @@ internal class KVBackup(
TRANSPORT_OK
} catch (e: IOException) {
Log.e(TAG, "Error uploading DB", e)
if (e.isOutOfSpace()) nm.onInsufficientSpaceError()
TRANSPORT_ERROR
} finally {
this.state = null

View file

@ -63,8 +63,14 @@ internal class CoordinatorIntegrationTest : TransportTest() {
@Suppress("Deprecation")
private val legacyPlugin = mockk<LegacyStoragePlugin>()
private val backupPlugin = mockk<StoragePlugin<*>>()
private val kvBackup =
KVBackup(storagePluginManager, settingsManager, inputFactory, cryptoImpl, dbManager)
private val kvBackup = KVBackup(
pluginManager = storagePluginManager,
settingsManager = settingsManager,
nm = notificationManager,
inputFactory = inputFactory,
crypto = cryptoImpl,
dbManager = dbManager,
)
private val fullBackup = FullBackup(
pluginManager = storagePluginManager,
settingsManager = settingsManager,

View file

@ -14,6 +14,7 @@ import com.stevesoltys.seedvault.header.VERSION
import com.stevesoltys.seedvault.header.getADForKV
import com.stevesoltys.seedvault.plugins.StoragePlugin
import com.stevesoltys.seedvault.plugins.StoragePluginManager
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
import io.mockk.CapturingSlot
import io.mockk.Runs
import io.mockk.coEvery
@ -34,10 +35,18 @@ import kotlin.random.Random
internal class KVBackupTest : BackupTest() {
private val pluginManager = mockk<StoragePluginManager>()
private val notificationManager = mockk<BackupNotificationManager>()
private val dataInput = mockk<BackupDataInput>()
private val dbManager = mockk<KvDbManager>()
private val backup = KVBackup(pluginManager, settingsManager, inputFactory, crypto, dbManager)
private val backup = KVBackup(
pluginManager = pluginManager,
settingsManager = settingsManager,
nm = notificationManager,
inputFactory = inputFactory,
crypto = crypto,
dbManager = dbManager
)
private val db = mockk<KVDb>()
private val plugin = mockk<StoragePlugin<*>>()