aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Einwag <[email protected]>2020-09-23 07:12:51 +0100
committerMatthias Einwag <[email protected]>2020-09-23 07:12:51 +0100
commitb93ced6f633fab2733b40aef2541582b00e053fb (patch)
treed4f100e94e0f8cbece5fddc76385f42cdd181e45
parentbcdedbb3d5a45ea974cc5f8e9068e9604c43a757 (diff)
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
-rw-r--r--editors/code/src/main.ts55
-rw-r--r--editors/code/src/net.ts10
-rw-r--r--editors/code/src/persistent_state.ts11
3 files changed, 72 insertions, 4 deletions
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
173 if (!shouldCheckForNewNightly) return; 173 if (!shouldCheckForNewNightly) return;
174 } 174 }
175 175
176 const release = await fetchRelease("nightly").catch((e) => { 176 const release = await performDownloadWithRetryDialog(async () => {
177 return await fetchRelease("nightly", state.githubToken);
178 }, state).catch((e) => {
177 log.error(e); 179 log.error(e);
178 if (state.releaseId === undefined) { // Show error only for the initial download 180 if (state.releaseId === undefined) { // Show error only for the initial download
179 vscode.window.showErrorMessage(`Failed to download rust-analyzer nightly ${e}`); 181 vscode.window.showErrorMessage(`Failed to download rust-analyzer nightly ${e}`);
@@ -308,7 +310,10 @@ async function getServer(config: Config, state: PersistentState): Promise<string
308 if (userResponse !== "Download now") return dest; 310 if (userResponse !== "Download now") return dest;
309 } 311 }
310 312
311 const release = await fetchRelease(config.package.releaseTag); 313 const releaseTag = config.package.releaseTag;
314 const release = await performDownloadWithRetryDialog(async () => {
315 return await fetchRelease(releaseTag, state.githubToken);
316 }, state);
312 const artifact = release.assets.find(artifact => artifact.name === `rust-analyzer-${platform}.gz`); 317 const artifact = release.assets.find(artifact => artifact.name === `rust-analyzer-${platform}.gz`);
313 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); 318 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`);
314 319
@@ -333,3 +338,49 @@ async function getServer(config: Config, state: PersistentState): Promise<string
333 await state.updateServerVersion(config.package.version); 338 await state.updateServerVersion(config.package.version);
334 return dest; 339 return dest;
335} 340}
341
342async function performDownloadWithRetryDialog<T>(downloadFunc: () => Promise<T>, state: PersistentState): Promise<T> {
343 while (true) {
344 try {
345 return await downloadFunc();
346 } catch (e) {
347 let selected = await vscode.window.showErrorMessage("Failed perform download: " + e.message, {}, {
348 title: "Update Github Auth Token",
349 updateToken: true,
350 }, {
351 title: "Retry download",
352 retry: true,
353 }, {
354 title: "Dismiss",
355 });
356
357 if (selected?.updateToken) {
358 await queryForGithubToken(state);
359 continue;
360 } else if (selected?.retry) {
361 continue;
362 }
363 throw e;
364 };
365 }
366
367}
368
369async function queryForGithubToken(state: PersistentState): Promise<void> {
370 const githubTokenOptions: vscode.InputBoxOptions = {
371 value: state.githubToken,
372 password: true,
373 prompt: `
374 This dialog allows to store a Github authorization token.
375 The usage of an authorization token allows will increase the rate
376 limit on the use of Github APIs and can thereby prevent getting
377 throttled.
378 Auth tokens can be obtained at https://github.com/settings/tokens`,
379 };
380
381 const newToken = await vscode.window.showInputBox(githubTokenOptions);
382 if (newToken) {
383 log.info("Storing new github token");
384 await state.updateGithubToken(newToken);
385 }
386} \ 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";
18const REPO = "rust-analyzer"; 18const REPO = "rust-analyzer";
19 19
20export async function fetchRelease( 20export async function fetchRelease(
21 releaseTag: string 21 releaseTag: string,
22 githubToken: string | null | undefined,
22): Promise<GithubRelease> { 23): Promise<GithubRelease> {
23 24
24 const apiEndpointPath = `/repos/${OWNER}/${REPO}/releases/tags/${releaseTag}`; 25 const apiEndpointPath = `/repos/${OWNER}/${REPO}/releases/tags/${releaseTag}`;
@@ -27,7 +28,12 @@ export async function fetchRelease(
27 28
28 log.debug("Issuing request for released artifacts metadata to", requestUrl); 29 log.debug("Issuing request for released artifacts metadata to", requestUrl);
29 30
30 const response = await fetch(requestUrl, { headers: { Accept: "application/vnd.github.v3+json" } }); 31 var headers: any = { Accept: "application/vnd.github.v3+json" };
32 if (githubToken != null) {
33 headers.Authorization = "token " + githubToken;
34 }
35
36 const response = await fetch(requestUrl, { headers: headers });
31 37
32 if (!response.ok) { 38 if (!response.ok) {
33 log.error("Error fetching artifact release info", { 39 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 {
38 async updateServerVersion(value: string | undefined) { 38 async updateServerVersion(value: string | undefined) {
39 await this.globalState.update("serverVersion", value); 39 await this.globalState.update("serverVersion", value);
40 } 40 }
41
42 /**
43 * Github authorization token.
44 * This is used for API requests against the Github API.
45 */
46 get githubToken(): string | undefined {
47 return this.globalState.get("githubToken");
48 }
49 async updateGithubToken(value: string | undefined) {
50 await this.globalState.update("githubToken", value);
51 }
41} 52}