Skip to content

Commit 91613a9

Browse files
committed
feat(electron-updater): abort download
Close #1150
1 parent ae036c6 commit 91613a9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+495
-417
lines changed

.babelrc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[
66
"transform-async-to-module-method",
77
{
8-
module: "bluebird-lst-c",
8+
module: "bluebird-lst",
99
method: "coroutine"
1010
}
1111
],
@@ -27,7 +27,7 @@
2727
[
2828
"transform-async-to-module-method",
2929
{
30-
module: "bluebird-lst-c",
30+
module: "bluebird-lst",
3131
method: "coroutine"
3232
}
3333
],

.idea/dictionaries/develar.xml

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/runConfigurations/compile.xml

Lines changed: 0 additions & 12 deletions
This file was deleted.

.idea/runConfigurations/lint.xml

Lines changed: 0 additions & 12 deletions
This file was deleted.

.idea/runConfigurations/test.xml

Lines changed: 0 additions & 12 deletions
This file was deleted.

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@
2626
"7zip-bin": "^2.0.4",
2727
"archiver": "^1.3.0",
2828
"asar-electron-builder": "^0.13.5",
29-
"aws-sdk": "^2.13.0",
30-
"bluebird-lst-c": "^1.0.6",
29+
"aws-sdk": "^2.15.0",
30+
"bluebird-lst": "^1.0.1",
3131
"chalk": "^1.1.3",
3232
"chromium-pickle-js": "^0.2.0",
3333
"cuint": "^0.2.2",
3434
"debug": "^2.6.1",
3535
"electron-download-tf": "3.2.0",
3636
"electron-macos-sign": "~1.6.0",
37-
"fs-extra-p": "^3.1.0",
37+
"fs-extra-p": "^4.0.1",
3838
"hosted-git-info": "^2.2.0",
3939
"ini": "^1.3.4",
4040
"is-ci": "^1.0.10",
@@ -80,7 +80,7 @@
8080
"lerna": "2.0.0-beta.37",
8181
"path-sort": "^0.1.0",
8282
"source-map-support": "^0.4.11",
83-
"ts-babel": "^1.3.6",
83+
"ts-babel": "^1.3.7",
8484
"tslint": "^4.4.2",
8585
"typescript": "^2.2.0",
8686
"typescript-json-schema": "^0.9.0",

packages/electron-builder-http/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"//": "client can also install js-yaml to load yaml",
1414
"dependencies": {
1515
"debug": "2.6.1",
16-
"fs-extra-p": "^3.1.0"
16+
"fs-extra-p": "^4.0.1"
1717
},
1818
"typings": "./out/electron-builder-http.d.ts"
1919
}
Lines changed: 80 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,108 @@
1+
import BluebirdPromise from "bluebird-lst"
12
import { EventEmitter } from "events"
2-
import BluebirdPromise from "bluebird-lst-c"
33

44
export class CancellationToken extends EventEmitter {
5+
private parentCancelHandler: any | null = null
6+
57
private _cancelled: boolean
68
get cancelled(): boolean {
79
return this._cancelled || (this._parent != null && this._parent.cancelled)
810
}
911

1012
private _parent: CancellationToken | null
1113
set parent(value: CancellationToken) {
14+
this.removeParentCancelHandler()
15+
1216
this._parent = value
17+
this.parentCancelHandler = () => this.cancel()
18+
this._parent.onCancel(this.parentCancelHandler)
1319
}
1420

1521
// babel cannot compile ... correctly for super calls
16-
constructor() {
22+
constructor(parent?: CancellationToken) {
1723
super()
1824

1925
this._cancelled = false
26+
if (parent != null) {
27+
this.parent = parent
28+
}
2029
}
2130

2231
cancel() {
2332
this._cancelled = true
2433
this.emit("cancel")
2534
}
2635

27-
onCancel(handler: () => any) {
28-
this.once("cancel", handler)
36+
private onCancel(handler: () => any) {
37+
if (this.cancelled) {
38+
handler()
39+
}
40+
else {
41+
this.once("cancel", handler)
42+
}
2943
}
3044

31-
trackPromise(promise: BluebirdPromise<any>): BluebirdPromise<any> {
32-
const handler = () => promise.cancel()
33-
this.onCancel(handler)
34-
// it is important to return promise, otherwise will be unhandled rejection error on reject
35-
return promise.finally(() => this.removeListener("cancel", handler))
45+
createPromise<R>(callback: (resolve: (thenableOrResult?: R) => void, reject: (error?: any) => void, onCancel: (callback: () => void) => void) => void): Promise<R> {
46+
if (this.cancelled) {
47+
return BluebirdPromise.reject(new CancellationError())
48+
}
49+
50+
let cancelHandler: (() => void) | null = null
51+
return new BluebirdPromise((resolve, reject) => {
52+
let addedCancelHandler: (() => void) | null = null
53+
54+
cancelHandler = () => {
55+
try {
56+
if (addedCancelHandler != null) {
57+
addedCancelHandler()
58+
addedCancelHandler = null
59+
}
60+
}
61+
finally {
62+
reject(new CancellationError())
63+
}
64+
}
65+
66+
if (this.cancelled) {
67+
cancelHandler()
68+
return
69+
}
70+
71+
this.onCancel(cancelHandler)
72+
73+
callback(resolve, reject, (callback: () => void) => {
74+
addedCancelHandler = callback
75+
})
76+
})
77+
.finally(() => {
78+
if (cancelHandler != null) {
79+
this.removeListener("cancel", cancelHandler)
80+
cancelHandler = null
81+
}
82+
})
83+
}
84+
85+
private removeParentCancelHandler() {
86+
const parent = this._parent
87+
if (parent != null && this.parentCancelHandler != null) {
88+
parent.removeListener("cancel", this.parentCancelHandler)
89+
this.parentCancelHandler = null
90+
}
91+
}
92+
93+
dispose() {
94+
try {
95+
this.removeParentCancelHandler()
96+
}
97+
finally {
98+
this.removeAllListeners()
99+
this._parent = null
100+
}
101+
}
102+
}
103+
104+
export class CancellationError extends Error {
105+
constructor() {
106+
super("Cancelled")
36107
}
37108
}

packages/electron-builder-http/src/httpExecutor.ts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { createHash } from "crypto"
2-
import { Transform } from "stream"
2+
import _debug from "debug"
3+
import { EventEmitter } from "events"
34
import { createWriteStream } from "fs-extra-p"
45
import { RequestOptions } from "http"
5-
import { parse as parseUrl } from "url"
6-
import _debug from "debug"
7-
import { ProgressCallbackTransform, ProgressInfo } from "./ProgressCallbackTransform"
86
import { safeLoad } from "js-yaml"
9-
import { EventEmitter } from "events"
107
import { Socket } from "net"
8+
import { Transform } from "stream"
9+
import { parse as parseUrl } from "url"
1110
import { CancellationToken } from "./CancellationToken"
11+
import { ProgressCallbackTransform, ProgressInfo } from "./ProgressCallbackTransform"
1212

1313
export interface RequestHeaders {
1414
[key: string]: any
@@ -149,7 +149,7 @@ export abstract class HttpExecutor<REQUEST_OPTS, REQUEST> {
149149

150150
protected abstract doRequest(options: any, callback: (response: any) => void): any
151151

152-
protected doDownload(requestOptions: any, destination: string, redirectCount: number, options: DownloadOptions, callback: (error: Error | null) => void) {
152+
protected doDownload(requestOptions: any, destination: string, redirectCount: number, options: DownloadOptions, callback: (error: Error | null) => void, onCancel: (callback: () => void) => void) {
153153
const request = this.doRequest(requestOptions, (response: Electron.IncomingMessage) => {
154154
if (response.statusCode >= 400) {
155155
callback(new Error(`Cannot download "${requestOptions.protocol || "https"}://${requestOptions.hostname}/${requestOptions.path}", status ${response.statusCode}: ${response.statusMessage}`))
@@ -164,18 +164,19 @@ export abstract class HttpExecutor<REQUEST_OPTS, REQUEST> {
164164
hostname: parsedUrl.hostname,
165165
path: parsedUrl.path,
166166
port: parsedUrl.port == null ? undefined : parsedUrl.port
167-
}), destination, redirectCount++, options, callback)
167+
}), destination, redirectCount++, options, callback, onCancel)
168168
}
169169
else {
170170
callback(new Error(`Too many redirects (> ${this.maxRedirects})`))
171171
}
172172
return
173173
}
174174

175-
configurePipes(options, response, destination, callback)
175+
configurePipes(options, response, destination, callback, options.cancellationToken)
176176
})
177177
this.addTimeOutHandler(request, callback)
178178
request.on("error", callback)
179+
onCancel(() => request.abort())
179180
request.end()
180181
}
181182

@@ -240,7 +241,7 @@ function safeGetHeader(response: any, headerKey: string) {
240241
}
241242
}
242243

243-
function configurePipes(options: DownloadOptions, response: any, destination: string, callback: (error: Error | null) => void) {
244+
function configurePipes(options: DownloadOptions, response: any, destination: string, callback: (error: Error | null) => void, cancellationToken: CancellationToken) {
244245
if (!checkSha2(safeGetHeader(response, "X-Checksum-Sha2"), options.sha2, callback)) {
245246
return
246247
}
@@ -262,11 +263,17 @@ function configurePipes(options: DownloadOptions, response: any, destination: st
262263

263264
let lastStream = response
264265
for (const stream of streams) {
265-
stream.on("error", callback)
266+
stream.on("error", (error: Error) => {
267+
if (!cancellationToken.cancelled) {
268+
callback(error)
269+
}
270+
})
266271
lastStream = lastStream.pipe(stream)
267272
}
268273

269-
fileOut.on("finish", () => (<any>fileOut.close)(callback))
274+
fileOut.on("finish", () => {
275+
(<any>fileOut.close)(callback)
276+
})
270277
}
271278

272279
export function configureRequestOptions(options: RequestOptions, token?: string | null, method?: "GET" | "DELETE" | "PUT"): RequestOptions {

packages/electron-builder-publisher/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
"out"
1212
],
1313
"dependencies": {
14-
"fs-extra-p": "^3.1.0",
14+
"fs-extra-p": "^4.0.1",
1515
"mime": "^1.3.4",
16-
"bluebird-lst-c": "^1.0.6",
16+
"bluebird-lst": "^1.0.1",
1717
"electron-builder-http": "~0.0.0-semantic-release",
1818
"electron-builder-util": "~0.0.0-semantic-release",
1919
"chalk": "^1.1.3",

0 commit comments

Comments
 (0)