Skip to content

Commit 371693d

Browse files
committed
fix: end stream for files <= 150 bytes in resolveLfsPointer
The LFS-pointer probe buffered up to 150 bytes before deciding whether to forward the blob or swap to the raw URL. For blobs that fit entirely in the probe, decide() ran from the source's end event and attached data/end listeners to an already-ended stream, so out.end() was never called. The response hung until upstream timed out and storage.write left an incomplete cached copy, which then forced a re-fetch on every subsequent read. Pass a sourceEnded flag through decide() and end the output directly when the source has already finished. Also skip the GitHub blob fetch when the tree size is already over MAX_FILE_SIZE, surfacing file_too_big instead of a translated 422.
1 parent 7dd6d87 commit 371693d

1 file changed

Lines changed: 25 additions & 8 deletions

File tree

src/core/source/GitHubStream.ts

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import { octokit } from "../GitHubUtils";
1515
import FileModel from "../model/files/files.model";
1616
import { IFile } from "../model/files/files.types";
1717
import { createLogger, serializeError } from "../logger";
18+
import config from "../../config";
19+
1820

1921
const logger = createLogger("gh-stream");
2022

@@ -85,7 +87,7 @@ export default class GitHubStream extends GitHubBase {
8587
const PROBE_BYTES = 150;
8688
const LFS_PREFIX = "version https://git-lfs.github.com/spec/";
8789

88-
const decide = (extra?: Buffer) => {
90+
const decide = (extra?: Buffer, sourceEnded = false) => {
8991
if (decided) return;
9092
decided = true;
9193
const head = probe.toString(
@@ -98,13 +100,17 @@ export default class GitHubStream extends GitHubBase {
98100
const lfsStream = this.downloadFileViaRaw(token, filePath);
99101
lfsStream.on("error", (err) => out.destroy(err));
100102
lfsStream.pipe(out);
101-
} else {
102-
out.write(probe);
103-
if (extra && extra.length) out.write(extra);
104-
blobStream.on("data", (c) => out.write(c));
105-
blobStream.on("end", () => out.end());
106-
blobStream.on("error", (err) => out.destroy(err));
103+
return;
107104
}
105+
out.write(probe);
106+
if (extra && extra.length) out.write(extra);
107+
if (sourceEnded) {
108+
out.end();
109+
return;
110+
}
111+
blobStream.on("data", (c) => out.write(c));
112+
blobStream.on("end", () => out.end());
113+
blobStream.on("error", (err) => out.destroy(err));
108114
};
109115

110116
blobStream.on("data", (chunk: Buffer) => {
@@ -118,7 +124,7 @@ export default class GitHubStream extends GitHubBase {
118124
decide(chunk.slice(remaining));
119125
}
120126
});
121-
blobStream.on("end", () => decide());
127+
blobStream.on("end", () => decide(undefined, true));
122128
blobStream.on("error", (err) => {
123129
// Always propagate — pre-decision this is the only listener; once a
124130
// non-LFS decision is made, the inner branch attaches its own
@@ -172,6 +178,17 @@ export default class GitHubStream extends GitHubBase {
172178
object: filePath,
173179
});
174180
}
181+
182+
// GitHub's blob API rejects blobs larger than 100 MB with HTTP 422.
183+
// Skip the download entirely when the tree already tells us the file is
184+
// over the cap, so we surface a clean `file_too_big` instead of paying
185+
// the round-trip just to translate a 422.
186+
if (expected.size != null && expected.size > config.MAX_FILE_SIZE) {
187+
throw new AnonymousError("file_too_big", {
188+
httpStatus: 413,
189+
object: filePath,
190+
});
191+
}
175192
const token = await this.data.getToken();
176193
const blobStream = this.downloadFile(token, expected.sha);
177194
// If the blob is a Git LFS pointer, swap to a raw-URL fetch so the

0 commit comments

Comments
 (0)