Skip to content

Commit 0afe286

Browse files
committed
feat(WebDAV): Retry upload if the file size is unexpected
fixes #2246 Signed-off-by: Marcel Klehr <mklehr@gmx.net>
1 parent 4a1a07e commit 0afe286

1 file changed

Lines changed: 52 additions & 8 deletions

File tree

src/lib/adapters/WebDav.ts

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ declare const IS_BROWSER: boolean
2020

2121
const LOCK_INTERVAL = 2 * 60 * 1000 // Lock every 2mins while syncing
2222
const LOCK_TIMEOUT = 15 * 60 * 1000 // Override lock 0.25h after last time lock has been set
23+
const PUT_FILE_SIZE_RETRIES = 2
2324
export default class WebDavAdapter extends CachingAdapter {
2425
private lockingInterval: any
2526
private lockingPromise: Promise<any>
@@ -220,13 +221,7 @@ export default class WebDavAdapter extends CachingAdapter {
220221
if (response.status === 200) {
221222
let xmlDocText = response.data
222223
if (IS_BROWSER) {
223-
let fileSize = null
224-
try {
225-
fileSize = await this.getFileSize(fullUrl)
226-
} catch (e) {
227-
console.warn(e)
228-
Logger.log('Error getting file size: ' + e.message)
229-
}
224+
const fileSize = await this.getRemoteFileSizeOrNull(fullUrl)
230225

231226
if (fileSize === null || Number.isNaN(fileSize)) {
232227
throw new FileSizeUnknown()
@@ -378,14 +373,63 @@ export default class WebDavAdapter extends CachingAdapter {
378373
const ciphertext = await Crypto.encryptAES(this.server.passphrase, xbel, salt)
379374
xbel = JSON.stringify({ciphertext, salt})
380375
}
381-
await this.uploadFile(fullUrl, this.server.bookmark_file_type === 'xbel' ? 'application/xml' : 'text/html', xbel)
376+
await this.uploadBookmarkFile(fullUrl, this.server.bookmark_file_type === 'xbel' ? 'application/xml' : 'text/html', xbel)
382377
} else {
383378
Logger.log('No changes to the server version necessary')
384379
}
385380

386381
await this.freeLock()
387382
}
388383

384+
async getRemoteFileSizeOrNull(url) {
385+
try {
386+
return await this.getFileSize(url)
387+
} catch (e) {
388+
if (e instanceof CancelledSyncError) {
389+
throw e
390+
}
391+
console.warn(e)
392+
Logger.log('Error getting file size: ' + e.message)
393+
return null
394+
}
395+
}
396+
397+
getContentByteLength(data) {
398+
return new TextEncoder().encode(data).length
399+
}
400+
401+
async verifyUploadedFileSize(url, expectedByteLength) {
402+
const fileSize = await this.getRemoteFileSizeOrNull(url)
403+
404+
if (fileSize === null || Number.isNaN(fileSize)) {
405+
throw new FileSizeUnknown()
406+
}
407+
408+
if (fileSize !== expectedByteLength) {
409+
Logger.log('Uploaded file size mismatch: ' + fileSize + ' != ' + expectedByteLength)
410+
throw new FileSizeMismatch()
411+
}
412+
}
413+
414+
async uploadBookmarkFile(url, content_type, data) {
415+
const expectedByteLength = this.getContentByteLength(data)
416+
417+
for (let attempt = 0; attempt <= PUT_FILE_SIZE_RETRIES; attempt++) {
418+
try {
419+
await this.uploadFile(url, content_type, data)
420+
await this.verifyUploadedFileSize(url, expectedByteLength)
421+
return
422+
} catch (e) {
423+
const isLastAttempt = attempt === PUT_FILE_SIZE_RETRIES
424+
const shouldRetry = e instanceof FileSizeMismatch || e instanceof FileSizeUnknown
425+
if (!shouldRetry || isLastAttempt) {
426+
throw e
427+
}
428+
Logger.log('Uploaded file size verification failed. Retrying upload (' + (attempt + 2) + '/' + (PUT_FILE_SIZE_RETRIES + 1) + ')')
429+
}
430+
}
431+
}
432+
389433
async uploadFile(url, content_type, data) {
390434
if (IS_BROWSER) {
391435
return this.uploadFileWeb(url, content_type, data)

0 commit comments

Comments
 (0)