From b93ced6f633fab2733b40aef2541582b00e053fb Mon Sep 17 00:00:00 2001 From: Matthias Einwag Date: Tue, 22 Sep 2020 23:12:51 -0700 Subject: Allow to use a Github Auth token for fetching releases This change allows to use a authorization token provided by Github in order to fetch metadata for a RA release. Using an authorization token prevents to get rate-limited in environments where lots of RA users use a shared client IP (e.g. behind a company NAT). The auth token is stored in `ExtensionContext.globalState`. As far as I could observe through testing with a local WSL2 environment that state is synced between an extension installed locally and a remote version. The change provides no explicit command to query for an auth token. However in case a download fails it will provide a retry option as well as an option to enter the auth token. This should be more discoverable for most users. Closes #3688 --- editors/code/src/main.ts | 55 ++++++++++++++++++++++++++++++++++-- editors/code/src/net.ts | 10 +++++-- editors/code/src/persistent_state.ts | 11 ++++++++ 3 files changed, 72 insertions(+), 4 deletions(-) (limited to 'editors/code') diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index bd99d696a..8c1610570 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 5eba2728d..d6194b63e 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 5705eed81..afb652589 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); + } } -- cgit v1.2.3