From d6551011861714962e3ab3bbd900e19d738dfb51 Mon Sep 17 00:00:00 2001 From: beyondkmp Date: Wed, 30 Apr 2025 14:24:11 +0800 Subject: [PATCH 1/3] fix(differentialDownloader): improve error handling and timeout management in multipleRangeDownloader --- .../multipleRangeDownloader.ts | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/packages/electron-updater/src/differentialDownloader/multipleRangeDownloader.ts b/packages/electron-updater/src/differentialDownloader/multipleRangeDownloader.ts index c8973c875ab..59f014f8be6 100644 --- a/packages/electron-updater/src/differentialDownloader/multipleRangeDownloader.ts +++ b/packages/electron-updater/src/differentialDownloader/multipleRangeDownloader.ts @@ -89,30 +89,50 @@ function doExecuteTasks(differentialDownloader: DifferentialDownloader, options: const requestOptions = differentialDownloader.createRequestOptions() requestOptions.headers!.Range = ranges.substring(0, ranges.length - 2) + + let timeoutId: NodeJS.Timeout | null = null + + const wrappedResolve = () => { + if (timeoutId !== null) { + clearTimeout(timeoutId) + timeoutId = null + } + resolve() + } + + const wrappedReject = (error: Error) => { + if (timeoutId !== null) { + clearTimeout(timeoutId) + timeoutId = null + } + reject(error) + } + const request = differentialDownloader.httpExecutor.createRequest(requestOptions, response => { - if (!checkIsRangesSupported(response, reject)) { + if (!checkIsRangesSupported(response, wrappedReject)) { return } const contentType = safeGetHeader(response, "content-type") const m = /^multipart\/.+?(?:; boundary=(?:(?:"(.+)")|(?:([^\s]+))))$/i.exec(contentType) if (m == null) { - reject(new Error(`Content-Type "multipart/byteranges" is expected, but got "${contentType}"`)) + wrappedReject(new Error(`Content-Type "multipart/byteranges" is expected, but got "${contentType}"`)) return } - const dicer = new DataSplitter(out, options, partIndexToTaskIndex, m[1] || m[2], partIndexToLength, resolve) - dicer.on("error", reject) + const dicer = new DataSplitter(out, options, partIndexToTaskIndex, m[1] || m[2], partIndexToLength, wrappedResolve) + dicer.on("error", wrappedReject) response.pipe(dicer) response.on("end", () => { - setTimeout(() => { + timeoutId = setTimeout(() => { + timeoutId = null request.abort() reject(new Error("Response ends without calling any handlers")) - }, 10000) + }, 30000) }) }) - differentialDownloader.httpExecutor.addErrorAndTimeoutHandlers(request, reject) + differentialDownloader.httpExecutor.addErrorAndTimeoutHandlers(request, wrappedReject) request.end() } From 93b59c71c7914ce56a81e56163521b0b7d34c327 Mon Sep 17 00:00:00 2001 From: beyondkmp Date: Wed, 30 Apr 2025 14:32:23 +0800 Subject: [PATCH 2/3] add changeset --- .changeset/bright-laws-dance.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/bright-laws-dance.md diff --git a/.changeset/bright-laws-dance.md b/.changeset/bright-laws-dance.md new file mode 100644 index 00000000000..aba9b2fc406 --- /dev/null +++ b/.changeset/bright-laws-dance.md @@ -0,0 +1,5 @@ +--- +"electron-updater": patch +--- + +fix(differentialDownloader): improve error handling and timeout management in multipleRangeDownloader From d47e037b8e736766ea2fbb1d2d88739906bc56fd Mon Sep 17 00:00:00 2001 From: beyondkmp Date: Thu, 1 May 2025 17:07:28 +0800 Subject: [PATCH 3/3] delete wrapperReject --- .../multipleRangeDownloader.ts | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/packages/electron-updater/src/differentialDownloader/multipleRangeDownloader.ts b/packages/electron-updater/src/differentialDownloader/multipleRangeDownloader.ts index 59f014f8be6..5b3bba09484 100644 --- a/packages/electron-updater/src/differentialDownloader/multipleRangeDownloader.ts +++ b/packages/electron-updater/src/differentialDownloader/multipleRangeDownloader.ts @@ -100,28 +100,20 @@ function doExecuteTasks(differentialDownloader: DifferentialDownloader, options: resolve() } - const wrappedReject = (error: Error) => { - if (timeoutId !== null) { - clearTimeout(timeoutId) - timeoutId = null - } - reject(error) - } - const request = differentialDownloader.httpExecutor.createRequest(requestOptions, response => { - if (!checkIsRangesSupported(response, wrappedReject)) { + if (!checkIsRangesSupported(response, reject)) { return } const contentType = safeGetHeader(response, "content-type") const m = /^multipart\/.+?(?:; boundary=(?:(?:"(.+)")|(?:([^\s]+))))$/i.exec(contentType) if (m == null) { - wrappedReject(new Error(`Content-Type "multipart/byteranges" is expected, but got "${contentType}"`)) + reject(new Error(`Content-Type "multipart/byteranges" is expected, but got "${contentType}"`)) return } const dicer = new DataSplitter(out, options, partIndexToTaskIndex, m[1] || m[2], partIndexToLength, wrappedResolve) - dicer.on("error", wrappedReject) + dicer.on("error", reject) response.pipe(dicer) response.on("end", () => { @@ -132,7 +124,7 @@ function doExecuteTasks(differentialDownloader: DifferentialDownloader, options: }, 30000) }) }) - differentialDownloader.httpExecutor.addErrorAndTimeoutHandlers(request, wrappedReject) + differentialDownloader.httpExecutor.addErrorAndTimeoutHandlers(request, reject) request.end() }