Skip to content

Commit b1734d2

Browse files
committed
Make APK download more resilient to GH issues & rate limit
1 parent 9c4ac4f commit b1734d2

File tree

1 file changed

+40
-7
lines changed

1 file changed

+40
-7
lines changed

src/interceptors/android/fetch-apk.ts

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,25 @@ import * as fs from '../../util/fs';
77
import { HtkConfig } from '../../config';
88
import { logError } from '../../error-tracking';
99

10-
async function getLatestRelease(): Promise<{ version: string, url: string } | undefined> {
10+
const APK_REPO_RELEASE = 'httptoolkit/httptoolkit-android/releases/latest';
11+
12+
async function getLatestRelease(): Promise<
13+
| { version: string, url: string }
14+
| 'unknown-latest'
15+
| undefined
16+
> {
1117
try {
12-
const response = await fetch(
13-
"https://api.github.com/repos/httptoolkit/httptoolkit-android/releases/latest"
14-
);
18+
const response = await fetch(`https://api.github.com/repos/${APK_REPO_RELEASE}`);
19+
20+
if (response.status === 403 && response.headers.get('x-ratelimit-remaining') === '0') {
21+
// Likely connectable but we can't check the version (API rate limit). Used only if
22+
// we don't have any other APK available.
23+
console.log("Can't check latest APK version due to GitHub rate limit");
24+
return 'unknown-latest';
25+
} else if (!response.ok) {
26+
throw new Error(`Checking latest Android app release failed with ${response.status}`);
27+
}
28+
1529
const release = await response.json();
1630
const apkAsset = release.assets.filter((a: any) => a.name === "httptoolkit.apk")[0];
1731
const releaseName = release.name || release.tag_name;
@@ -119,8 +133,18 @@ export async function streamLatestApk(config: HtkConfig): Promise<stream.Readabl
119133
if (!latestApkRelease) {
120134
throw new Error("Couldn't find an Android APK locally or remotely");
121135
} else {
136+
// No APK locally, but we can get one remotely:
137+
122138
console.log('Streaming remote APK directly');
123-
const apkStream = (await fetch(latestApkRelease.url)).body;
139+
const apkUrl = latestApkRelease === 'unknown-latest'
140+
? `https://github.com/${APK_REPO_RELEASE}/download/httptoolkit.apk`
141+
: latestApkRelease.url;
142+
143+
const apkResponse = await fetch(apkUrl);
144+
if (!apkResponse.ok) {
145+
throw new Error(`APK download failed with ${apkResponse.status}`);
146+
}
147+
const apkStream = apkResponse.body;
124148

125149
// We buffer output into two passthrough streams, so both file & install
126150
// stream usage can be set up async independently. Buffers are 10MB, to
@@ -130,12 +154,21 @@ export async function streamLatestApk(config: HtkConfig): Promise<stream.Readabl
130154
const apkOutputStream = new stream.PassThrough({ highWaterMark: 10485760 });
131155
apkStream.pipe(apkOutputStream);
132156

133-
updateLocalApk(latestApkRelease.version, apkFileStream, config).catch(logError);
157+
if (latestApkRelease !== 'unknown-latest') {
158+
updateLocalApk(latestApkRelease.version, apkFileStream, config).catch(logError);
159+
}
160+
134161
return apkOutputStream;
135162
}
136163
}
137164

138-
if (!latestApkRelease || semver.gte(localApk.version, latestApkRelease.version, true)) {
165+
// So, we now have a local APK. Do we want to pull the remote one anyway?
166+
167+
if (
168+
!latestApkRelease || // Can't get releases at all
169+
latestApkRelease === 'unknown-latest' || // Can get, but don't know the version
170+
semver.gte(localApk.version, latestApkRelease.version, true) // Already up to date
171+
) {
139172
console.log('Streaming local APK');
140173
// If we have an APK locally and it's up to date, or we can't tell, just use it
141174
return fs.createReadStream(localApk.path);

0 commit comments

Comments
 (0)