diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index bd99d696ad8..8c161057063 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -173,7 +173,9 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi if (!shouldCheckForNewNightly) return; } - const release = await fetchRelease("nightly").catch((e) => { + const release = await performDownloadWithRetryDialog(async () => { + return await fetchRelease("nightly", state.githubToken); + }, state).catch((e) => { log.error(e); if (state.releaseId === undefined) { // Show error only for the initial download vscode.window.showErrorMessage(`Failed to download rust-analyzer nightly ${e}`); @@ -308,7 +310,10 @@ async function getServer(config: Config, state: PersistentState): Promise { + return await fetchRelease(releaseTag, state.githubToken); + }, state); const artifact = release.assets.find(artifact => artifact.name === `rust-analyzer-${platform}.gz`); assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); @@ -333,3 +338,49 @@ async function getServer(config: Config, state: PersistentState): Promise(downloadFunc: () => Promise, state: PersistentState): Promise { + while (true) { + try { + return await downloadFunc(); + } catch (e) { + let selected = await vscode.window.showErrorMessage("Failed perform download: " + e.message, {}, { + title: "Update Github Auth Token", + updateToken: true, + }, { + title: "Retry download", + retry: true, + }, { + title: "Dismiss", + }); + + if (selected?.updateToken) { + await queryForGithubToken(state); + continue; + } else if (selected?.retry) { + continue; + } + throw e; + }; + } + +} + +async function queryForGithubToken(state: PersistentState): Promise { + const githubTokenOptions: vscode.InputBoxOptions = { + value: state.githubToken, + password: true, + prompt: ` + This dialog allows to store a Github authorization token. + The usage of an authorization token allows will increase the rate + limit on the use of Github APIs and can thereby prevent getting + throttled. + Auth tokens can be obtained at https://github.com/settings/tokens`, + }; + + const newToken = await vscode.window.showInputBox(githubTokenOptions); + if (newToken) { + log.info("Storing new github token"); + await state.updateGithubToken(newToken); + } +} \ No newline at end of file diff --git a/editors/code/src/net.ts b/editors/code/src/net.ts index 5eba2728d28..d6194b63e4b 100644 --- a/editors/code/src/net.ts +++ b/editors/code/src/net.ts @@ -18,7 +18,8 @@ const OWNER = "rust-analyzer"; const REPO = "rust-analyzer"; export async function fetchRelease( - releaseTag: string + releaseTag: string, + githubToken: string | null | undefined, ): Promise { const apiEndpointPath = `/repos/${OWNER}/${REPO}/releases/tags/${releaseTag}`; @@ -27,7 +28,12 @@ export async function fetchRelease( log.debug("Issuing request for released artifacts metadata to", requestUrl); - const response = await fetch(requestUrl, { headers: { Accept: "application/vnd.github.v3+json" } }); + var headers: any = { Accept: "application/vnd.github.v3+json" }; + if (githubToken != null) { + headers.Authorization = "token " + githubToken; + } + + const response = await fetch(requestUrl, { headers: headers }); if (!response.ok) { log.error("Error fetching artifact release info", { diff --git a/editors/code/src/persistent_state.ts b/editors/code/src/persistent_state.ts index 5705eed812d..afb65258991 100644 --- a/editors/code/src/persistent_state.ts +++ b/editors/code/src/persistent_state.ts @@ -38,4 +38,15 @@ export class PersistentState { async updateServerVersion(value: string | undefined) { await this.globalState.update("serverVersion", value); } + + /** + * Github authorization token. + * This is used for API requests against the Github API. + */ + get githubToken(): string | undefined { + return this.globalState.get("githubToken"); + } + async updateGithubToken(value: string | undefined) { + await this.globalState.update("githubToken", value); + } }