From 5ad747533ba2b17f945b5661595d96ffb1a06ba5 Mon Sep 17 00:00:00 2001 From: Zorg Date: Sat, 30 Apr 2022 12:21:04 -0700 Subject: [PATCH] Fall back to regular update if delta update fails to download Also log out the download failure when a delta update fails to download (such as a 404 error for example). Additionally, add a check to make sure that top-level appcast items cannot be delta update items. --- Sparkle/SPUCoreBasedUpdateDriver.m | 37 ++++++++++++++++-------- Sparkle/SUAppcastDriver.m | 7 ++++- Tests/Resources/testappcast_channels.xml | 6 ++++ Tests/SUAppcastTest.swift | 2 +- 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/Sparkle/SPUCoreBasedUpdateDriver.m b/Sparkle/SPUCoreBasedUpdateDriver.m index 2066e079bc..80ef655302 100644 --- a/Sparkle/SPUCoreBasedUpdateDriver.m +++ b/Sparkle/SPUCoreBasedUpdateDriver.m @@ -13,6 +13,7 @@ #import "SPUInstallerDriver.h" #import "SPUDownloadDriver.h" #import "SULog.h" +#import "SULog+NSError.h" #import "SUErrors.h" #import "SPUResumableUpdate.h" #import "SPUDownloadedUpdate.h" @@ -247,18 +248,25 @@ - (void)extractUpdate:(SPUDownloadedUpdate *)downloadedUpdate - (void)downloadDriverDidFailToDownloadFileWithError:(NSError *)error { - if ([self.updaterDelegate respondsToSelector:@selector((updater:failedToDownloadUpdate:error:))]) { - NSError *errorToReport = [error.userInfo objectForKey:NSUnderlyingErrorKey]; - if (errorToReport == nil) { - errorToReport = error; + if ([self.updateItem isDeltaUpdate]) { + SULog(SULogLevelError, @"Failed to download delta update. Falling back to regular update..."); + SULogError(error); + + [self fallBackAndDownloadRegularUpdate]; + } else { + if ([self.updaterDelegate respondsToSelector:@selector((updater:failedToDownloadUpdate:error:))]) { + NSError *errorToReport = [error.userInfo objectForKey:NSUnderlyingErrorKey]; + if (errorToReport == nil) { + errorToReport = error; + } + + [self.updaterDelegate updater:self.updater + failedToDownloadUpdate:self.updateItem + error:errorToReport]; } - [self.updaterDelegate updater:self.updater - failedToDownloadUpdate:self.updateItem - error:errorToReport]; + [self.delegate coreDriverIsRequestingAbortUpdateWithError:error]; } - - [self.delegate coreDriverIsRequestingAbortUpdateWithError:error]; } - (void)installerDidStartInstallingWithApplicationTerminated:(BOOL)applicationTerminated @@ -335,15 +343,13 @@ - (void)basicDriverIsRequestingAbortUpdateWithError:(nullable NSError *)error [self.delegate basicDriverIsRequestingAbortUpdateWithError:error]; } -- (void)installerDidFailToApplyDeltaUpdate +- (void)fallBackAndDownloadRegularUpdate { SUAppcastItem *secondaryUpdateItem = self.secondaryUpdateItem; assert(secondaryUpdateItem != nil); BOOL backgroundDownload = self.downloadDriver.inBackground; - [self clearDownloadedUpdate]; - // Fall back to the non-delta update. Note that we don't want to trigger another update was found event. self.updateItem = secondaryUpdateItem; self.secondaryUpdateItem = nil; @@ -351,6 +357,13 @@ - (void)installerDidFailToApplyDeltaUpdate [self downloadUpdateFromAppcastItem:secondaryUpdateItem secondaryAppcastItem:nil inBackground:backgroundDownload]; } +- (void)installerDidFailToApplyDeltaUpdate +{ + [self clearDownloadedUpdate]; + + [self fallBackAndDownloadRegularUpdate]; +} + - (void)abortUpdateAndShowNextUpdateImmediately:(BOOL)shouldShowUpdateImmediately error:(nullable NSError *)error { [self.installerDriver abortInstall]; diff --git a/Sparkle/SUAppcastDriver.m b/Sparkle/SUAppcastDriver.m index e2a9e25f8b..22ee448c97 100644 --- a/Sparkle/SUAppcastDriver.m +++ b/Sparkle/SUAppcastDriver.m @@ -168,7 +168,6 @@ - (SUAppcastItem *)retrieveBestAppcastItemFromAppcast:(SUAppcast *)appcast versi regularItemFromDelegate = nil; delegateOptedOutOfSelection = NO; } else { - assert(!candidateItem.deltaUpdate); if (candidateItem.deltaUpdate) { // Client would have to go out of their way to examine the .deltaUpdates to return one // We need them to give us a regular update item back instead.. @@ -281,6 +280,12 @@ + (SUAppcast *)filterAppcast:(SUAppcast *)appcast forMacOSAndAllowedChannels:(NS return NO; } + // Delta updates cannot be top-level entries + BOOL isDeltaUpdate = [item isDeltaUpdate]; + if (isDeltaUpdate) { + return NO; + } + NSString *channel = item.channel; if (channel == nil) { // Item is on the default channel diff --git a/Tests/Resources/testappcast_channels.xml b/Tests/Resources/testappcast_channels.xml index 065c3f1c69..25270b579f 100644 --- a/Tests/Resources/testappcast_channels.xml +++ b/Tests/Resources/testappcast_channels.xml @@ -54,5 +54,11 @@ 6.0 + + + Version 7.0 + 7.0 + + diff --git a/Tests/SUAppcastTest.swift b/Tests/SUAppcastTest.swift index a1e921db5b..07dac6c467 100644 --- a/Tests/SUAppcastTest.swift +++ b/Tests/SUAppcastTest.swift @@ -91,7 +91,7 @@ class SUAppcastTest: XCTestCase { let stateResolver = SPUAppcastItemStateResolver(hostVersion: hostVersion, applicationVersionComparator: versionComparator, standardVersionComparator: versionComparator) let appcast = try SUAppcast(xmlData: testData, relativeTo: nil, stateResolver: stateResolver) - XCTAssertEqual(5, appcast.items.count) + XCTAssertEqual(6, appcast.items.count) do { let filteredAppcast = SUAppcastDriver.filterAppcast(appcast, forMacOSAndAllowedChannels: ["beta", "nightly"])