Skip to content

Commit

Permalink
feat: Adding timeout to publisher config for api requests and uploads (
Browse files Browse the repository at this point in the history
…#7028)

* Adding timeout to publish config. Adding test for validating too short of a timeout. Expect upload to throw/reject Request timed out. Adding null type in docs and updating documentation to state 2min default
  • Loading branch information
mmaietta committed Jul 28, 2022
1 parent 9498261 commit e7179b5
Show file tree
Hide file tree
Showing 15 changed files with 159 additions and 16 deletions.
8 changes: 8 additions & 0 deletions .changeset/chatty-trains-agree.md
@@ -0,0 +1,8 @@
---
"app-builder-lib": minor
"builder-util-runtime": minor
"builder-util": minor
"electron-publish": minor
---

feat: Adding timeout to publisher config for api requests and uploads
1 change: 1 addition & 0 deletions docs/api/electron-builder.md
Expand Up @@ -1304,6 +1304,7 @@ return path.join(target.outDir, <code>__${target.name}-${getArtifactArchName(arc
<li><code id="UploadTask-fileContent">fileContent</code> module:global.Buffer | “undefined”</li>
<li><strong><code id="UploadTask-arch">arch</code></strong> <a href="#Arch">Arch</a> | “undefined”</li>
<li><code id="UploadTask-safeArtifactName">safeArtifactName</code> String | “undefined”</li>
<li><code id="UploadTask-timeout">timeout</code> Number | “undefined”</li>
</ul>
<p><a name="HttpPublisher"></a></p>
<h2 id="httppublisher-%E2%87%90-publisher">HttpPublisher ⇐ <code><a href="#Publisher">Publisher</a></code></h2>
Expand Down
15 changes: 15 additions & 0 deletions docs/configuration/publish.md
Expand Up @@ -132,6 +132,9 @@ In all publish options <a href="/file-patterns#file-macros">File Macros</a> are
<li>
<p><code id="GenericServerOptions-requestHeaders">requestHeaders</code> module:http.OutgoingHttpHeaders - Any custom request headers</p>
</li>
<li>
<p><code id="GenericServerOptions-timeout">timeout</code> = <code>60000</code> Number | “undefined” - Request timeout in milliseconds. (Default is 2 minutes; O is ignored)</p>
</li>
</ul>
<h2 id="githuboptions">GithubOptions</h2>
<p><a href="https://help.github.com/articles/about-releases/">GitHub</a> options.</p>
Expand Down Expand Up @@ -179,6 +182,9 @@ Define <code>GH_TOKEN</code> environment variable.</p>
<li>
<p><code id="GithubOptions-requestHeaders">requestHeaders</code> module:http.OutgoingHttpHeaders - Any custom request headers</p>
</li>
<li>
<p><code id="GithubOptions-timeout">timeout</code> = <code>60000</code> Number | “undefined” - Request timeout in milliseconds. (Default is 2 minutes; O is ignored)</p>
</li>
</ul>
<h2 id="snapstoreoptions">SnapStoreOptions</h2>
<p><a href="https://snapcraft.io/">Snap Store</a> options.</p>
Expand All @@ -196,6 +202,9 @@ Define <code>GH_TOKEN</code> environment variable.</p>
<li>
<p><code id="SnapStoreOptions-requestHeaders">requestHeaders</code> module:http.OutgoingHttpHeaders - Any custom request headers</p>
</li>
<li>
<p><code id="SnapStoreOptions-timeout">timeout</code> = <code>60000</code> Number | “undefined” - Request timeout in milliseconds. (Default is 2 minutes; O is ignored)</p>
</li>
</ul>
<h2 id="spacesoptions">SpacesOptions</h2>
<p><a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-digitalocean-spaces">DigitalOcean Spaces</a> options.
Expand Down Expand Up @@ -228,6 +237,9 @@ Define <code>KEYGEN_TOKEN</code> environment variable.</p>
<li>
<p><code id="KeygenOptions-requestHeaders">requestHeaders</code> module:http.OutgoingHttpHeaders - Any custom request headers</p>
</li>
<li>
<p><code id="KeygenOptions-timeout">timeout</code> = <code>60000</code> Number | “undefined” - Request timeout in milliseconds. (Default is 2 minutes; O is ignored)</p>
</li>
</ul>
<h2 id="bitbucketoptions">BitbucketOptions</h2>
<p>Bitbucket options.
Expand Down Expand Up @@ -256,6 +268,9 @@ Define <code>BITBUCKET_TOKEN</code> environment variable.</p>
<li>
<p><code id="BitbucketOptions-requestHeaders">requestHeaders</code> module:http.OutgoingHttpHeaders - Any custom request headers</p>
</li>
<li>
<p><code id="BitbucketOptions-timeout">timeout</code> = <code>60000</code> Number | “undefined” - Request timeout in milliseconds. (Default is 2 minutes; O is ignored)</p>
</li>
</ul>
<h2 id="s3options">S3Options</h2>
<p><a href="https://aws.amazon.com/s3/">Amazon S3</a> options.
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -29,6 +29,7 @@
"generate-changeset": "pnpm changeset",
"generate-docs": "pnpm compile && pnpm jsdoc && pnpm jsdoc2md2html",
"generate-schema": "typescript-json-schema packages/app-builder-lib/tsconfig-scheme.json Configuration --out packages/app-builder-lib/scheme.json --noExtraProps --useTypeOfKeyword --strictNullChecks --required && node ./scripts/fix-schema.js",
"generate-all": "pnpm generate-schema && pnpm generate-docs && pnpm prettier",
"ci:test": "node ./test/out/helpers/runTests.js",
"ci:version": "pnpm changelog && changeset version && node scripts/update-package-version-export.js && pnpm run generate-schema && pnpm run generate-docs && pnpm run prettier && git add .",
"ci:publish": "pnpm compile && pnpm publish -r && changeset tag",
Expand Down
64 changes: 64 additions & 0 deletions packages/app-builder-lib/scheme.json
Expand Up @@ -381,6 +381,14 @@
"description": "Repository slug/name",
"type": "string"
},
"timeout": {
"default": 60000,
"description": "Request timeout in milliseconds. (Default is 2 minutes; O is ignored)",
"type": [
"null",
"number"
]
},
"token": {
"description": "The access token to support auto-update from private bitbucket repositories.",
"type": [
Expand Down Expand Up @@ -478,6 +486,14 @@
"$ref": "#/definitions/OutgoingHttpHeaders",
"description": "Any custom request headers"
},
"timeout": {
"default": 60000,
"description": "Request timeout in milliseconds. (Default is 2 minutes; O is ignored)",
"type": [
"null",
"number"
]
},
"updateProvider": {
"description": "The Provider to provide UpdateInfo regarding available updates. Required\nto use custom providers with electron-updater.",
"typeof": "function"
Expand Down Expand Up @@ -1358,6 +1374,14 @@
"$ref": "#/definitions/OutgoingHttpHeaders",
"description": "Any custom request headers"
},
"timeout": {
"default": 60000,
"description": "Request timeout in milliseconds. (Default is 2 minutes; O is ignored)",
"type": [
"null",
"number"
]
},
"updaterCacheDirName": {
"type": [
"null",
Expand Down Expand Up @@ -1482,6 +1506,14 @@
"$ref": "#/definitions/OutgoingHttpHeaders",
"description": "Any custom request headers"
},
"timeout": {
"default": 60000,
"description": "Request timeout in milliseconds. (Default is 2 minutes; O is ignored)",
"type": [
"null",
"number"
]
},
"token": {
"description": "The access token to support auto-update from private github repositories. Never specify it in the configuration files. Only for [setFeedURL](/auto-update#appupdatersetfeedurloptions).",
"type": [
Expand Down Expand Up @@ -1573,6 +1605,14 @@
"$ref": "#/definitions/OutgoingHttpHeaders",
"description": "Any custom request headers"
},
"timeout": {
"default": 60000,
"description": "Request timeout in milliseconds. (Default is 2 minutes; O is ignored)",
"type": [
"null",
"number"
]
},
"updaterCacheDirName": {
"type": [
"null",
Expand Down Expand Up @@ -4876,6 +4916,14 @@
"default": "STANDARD",
"description": "The type of storage to use for the object."
},
"timeout": {
"default": 60000,
"description": "Request timeout in milliseconds. (Default is 2 minutes; O is ignored)",
"type": [
"null",
"number"
]
},
"updaterCacheDirName": {
"type": [
"null",
Expand Down Expand Up @@ -5324,6 +5372,14 @@
"$ref": "#/definitions/OutgoingHttpHeaders",
"description": "Any custom request headers"
},
"timeout": {
"default": 60000,
"description": "Request timeout in milliseconds. (Default is 2 minutes; O is ignored)",
"type": [
"null",
"number"
]
},
"updaterCacheDirName": {
"type": [
"null",
Expand Down Expand Up @@ -5409,6 +5465,14 @@
"$ref": "#/definitions/OutgoingHttpHeaders",
"description": "Any custom request headers"
},
"timeout": {
"default": 60000,
"description": "Request timeout in milliseconds. (Default is 2 minutes; O is ignored)",
"type": [
"null",
"number"
]
},
"updaterCacheDirName": {
"type": [
"null",
Expand Down
4 changes: 2 additions & 2 deletions packages/app-builder-lib/src/electron/electronVersion.ts
Expand Up @@ -31,7 +31,7 @@ export async function getElectronVersionFromInstalled(projectDir: string) {
for (const name of electronPackages) {
try {
return (await readJson(path.join(projectDir, "node_modules", name, "package.json"))).version
} catch (e) {
} catch (e: any) {
if (e.code !== "ENOENT") {
log.warn({ name, error: e }, `cannot read electron version package.json`)
}
Expand All @@ -44,7 +44,7 @@ export async function getElectronPackage(projectDir: string) {
for (const name of electronPackages) {
try {
return await readJson(path.join(projectDir, "node_modules", name, "package.json"))
} catch (e) {
} catch (e: any) {
if (e.code !== "ENOENT") {
log.warn({ name, error: e }, `cannot find electron in package.json`)
}
Expand Down
2 changes: 2 additions & 0 deletions packages/app-builder-lib/src/publish/BitbucketPublisher.ts
Expand Up @@ -49,6 +49,7 @@ export class BitbucketPublisher extends HttpPublisher {
hostname: this.hostname,
path: this.basePath,
headers: form.getHeaders(),
timeout: this.info.timeout || undefined,
}
await httpExecutor.doApiRequest(configureRequestOptions(upload, this.auth, "POST"), this.context.cancellationToken, it => form.pipe(it))
return fileName
Expand All @@ -59,6 +60,7 @@ export class BitbucketPublisher extends HttpPublisher {
const req: RequestOptions = {
hostname: this.hostname,
path: `${this.basePath}/${filename}`,
timeout: this.info.timeout || undefined,
}
await httpExecutor.request(configureRequestOptions(req, this.auth, "DELETE"), this.context.cancellationToken)
}
Expand Down
5 changes: 5 additions & 0 deletions packages/app-builder-lib/src/publish/KeygenPublisher.ts
Expand Up @@ -139,6 +139,7 @@ export class KeygenPublisher extends HttpPublisher {
headers: {
"Content-Length": dataLength,
},
timeout: this.info.timeout || undefined,
}

await httpExecutor.doApiRequest(configureRequestOptions(upload, null, "PUT"), this.context.cancellationToken, requestProcessor)
Expand All @@ -154,6 +155,7 @@ export class KeygenPublisher extends HttpPublisher {
"Keygen-Version": "1.1",
Prefer: "no-redirect",
},
timeout: this.info.timeout || undefined,
}

const data: RecursivePartial<KeygenArtifact> = {
Expand Down Expand Up @@ -211,6 +213,7 @@ export class KeygenPublisher extends HttpPublisher {
Accept: "application/vnd.api+json",
"Keygen-Version": "1.1",
},
timeout: this.info.timeout || undefined,
}

return parseJson(httpExecutor.request(configureRequestOptions(req, this.auth, "GET"), this.context.cancellationToken, null))
Expand All @@ -225,6 +228,7 @@ export class KeygenPublisher extends HttpPublisher {
Accept: "application/vnd.api+json",
"Keygen-Version": "1.1",
},
timeout: this.info.timeout || undefined,
}

const data: RecursivePartial<KeygenRelease> = {
Expand Down Expand Up @@ -257,6 +261,7 @@ export class KeygenPublisher extends HttpPublisher {
Accept: "application/vnd.api+json",
"Keygen-Version": "1.1",
},
timeout: this.info.timeout || undefined,
}
await httpExecutor.request(configureRequestOptions(req, this.auth, "DELETE"), this.context.cancellationToken)
}
Expand Down
4 changes: 4 additions & 0 deletions packages/app-builder-lib/src/publish/PublishManager.ts
Expand Up @@ -167,6 +167,10 @@ export class PublishManager implements PublishContext {
return
}

if (publishConfig.timeout) {
event.timeout = publishConfig.timeout
}

this.taskManager.addTask(publisher.upload(event))
}

Expand Down
16 changes: 8 additions & 8 deletions packages/builder-util-runtime/src/httpExecutor.ts
Expand Up @@ -111,11 +111,11 @@ export abstract class HttpExecutor<T extends Request> {
const request = this.createRequest(options, (response: any) => {
try {
this.handleResponse(response, options, cancellationToken, resolve, reject, redirectCount, requestProcessor)
} catch (e) {
} catch (e: any) {
reject(e)
}
})
this.addErrorAndTimeoutHandlers(request, reject)
this.addErrorAndTimeoutHandlers(request, reject, options.timeout)
this.addRedirectHandlers(request, options, reject, redirectCount, options => {
this.doApiRequest(options, cancellationToken, requestProcessor, redirectCount).then(resolve).catch(reject)
})
Expand All @@ -130,8 +130,8 @@ export abstract class HttpExecutor<T extends Request> {
// not required for NodeJS
}

addErrorAndTimeoutHandlers(request: any, reject: (error: Error) => void) {
this.addTimeOutHandler(request, reject)
addErrorAndTimeoutHandlers(request: any, reject: (error: Error) => void, timeout = 60 * 1000) {
this.addTimeOutHandler(request, reject, timeout)
request.on("error", reject)
request.on("aborted", () => {
reject(new Error("Request has been aborted by the server"))
Expand Down Expand Up @@ -213,7 +213,7 @@ Please double check that your authentication token is correct. Due to security r
}

// noinspection JSUnusedLocalSymbols
abstract createRequest(options: any, callback: (response: any) => void): T
abstract createRequest(options: RequestOptions, callback: (response: any) => void): T

async downloadToBuffer(url: URL, options: DownloadOptions): Promise<Buffer> {
return await options.cancellationToken.createPromise<Buffer>((resolve, reject, onCancel) => {
Expand Down Expand Up @@ -313,7 +313,7 @@ Please double check that your authentication token is correct. Due to security r
options.responseHandler(response, options.callback)
}
})
this.addErrorAndTimeoutHandlers(request, options.callback)
this.addErrorAndTimeoutHandlers(request, options.callback, requestOptions.timeout)
this.addRedirectHandlers(request, requestOptions, options.callback, redirectCount, requestOptions => {
this.doDownload(requestOptions, options, redirectCount++)
})
Expand All @@ -324,9 +324,9 @@ Please double check that your authentication token is correct. Due to security r
return new Error(`Too many redirects (> ${this.maxRedirects})`)
}

private addTimeOutHandler(request: any, callback: (error: Error) => void) {
private addTimeOutHandler(request: any, callback: (error: Error) => void, timeout: number) {
request.on("socket", (socket: Socket) => {
socket.setTimeout(60 * 1000, () => {
socket.setTimeout(timeout, () => {
request.abort()
callback(new Error("Request timed out"))
})
Expand Down
7 changes: 7 additions & 0 deletions packages/builder-util-runtime/src/publishOptions.ts
Expand Up @@ -46,6 +46,13 @@ export interface PublishConfiguration {
* Any custom request headers
*/
readonly requestHeaders?: OutgoingHttpHeaders

/**
* Request timeout in milliseconds. (Default is 2 minutes; O is ignored)
*
* @default 60000
*/
readonly timeout?: number | null
}

// https://github.com/electron-userland/electron-builder/issues/3261
Expand Down
2 changes: 2 additions & 0 deletions packages/electron-publish/src/gitHubPublisher.ts
Expand Up @@ -191,6 +191,7 @@ export class GitHubPublisher extends HttpPublisher {
"Content-Type": mime.getType(fileName) || "application/octet-stream",
"Content-Length": dataLength,
},
timeout: this.info.timeout || undefined,
},
this.token
),
Expand Down Expand Up @@ -277,6 +278,7 @@ export class GitHubPublisher extends HttpPublisher {
port: baseUrl.port as any,
path: this.info.host != null && this.info.host !== "github.com" ? `/api/v3${path.startsWith("/") ? path : `/${path}`}` : path,
headers: { accept: "application/vnd.github.v3+json" },
timeout: this.info.timeout || undefined,
},
token,
method
Expand Down

0 comments on commit e7179b5

Please sign in to comment.