Skip to content

Commit 72d13a4

Browse files
dormouse-botclaude
andcommitted
iframe-proxy: serve error page on HTML body truncation
When an upstream HTML response exceeds HTML_BODY_LIMIT, collectBody previously destroyed the stream and handed back the partial bytes, which were then instrumented and served as a truncated document. Serve a clear "exceeded HTML_BODY_LIMIT of N MB" error page instead, so we never ship half a page (where the shim may have landed past the cutoff). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent a00f596 commit 72d13a4

1 file changed

Lines changed: 20 additions & 3 deletions

File tree

vscode-ext/src/iframe-proxy-host.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,13 +198,19 @@ function handleRequest(grant: Grant, req: http.IncomingMessage, res: http.Server
198198
passThrough(grant, upstreamRes, res);
199199
return;
200200
}
201-
collectBody(upstreamRes, (body) => {
201+
collectBody(upstreamRes, (body, truncated) => {
202202
// A remote that forbids embedding is never force-framed — serve an
203203
// actionable page that points at `dor ab` instead of a blank pane.
204204
if (!grant.isLoopback && refusesFraming(upstreamRes.headers)) {
205205
serveErrorPage(res, frameRefusedPage(grant.upstream));
206206
return;
207207
}
208+
// An over-limit body can't be safely instrumented (the shim may land past
209+
// the cutoff, and serving half a document is worse than an honest error).
210+
if (truncated) {
211+
serveErrorPage(res, bodyTooLargePage(grant.upstream));
212+
return;
213+
}
208214
const html = instrumentHtml(body);
209215
const outHeaders = sanitizeResponseHeaders(grant, upstreamRes.headers);
210216
outHeaders['content-type'] = 'text/html; charset=utf-8';
@@ -225,18 +231,20 @@ function passThrough(grant: Grant, upstreamRes: http.IncomingMessage, res: http.
225231
upstreamRes.pipe(res);
226232
}
227233

228-
function collectBody(stream: http.IncomingMessage, done: (body: string) => void): void {
234+
function collectBody(stream: http.IncomingMessage, done: (body: string, truncated: boolean) => void): void {
229235
const chunks: Buffer[] = [];
230236
let size = 0;
237+
let truncated = false;
231238
stream.on('data', (chunk: Buffer) => {
232239
size += chunk.length;
233240
if (size > HTML_BODY_LIMIT) {
241+
truncated = true;
234242
stream.destroy();
235243
return;
236244
}
237245
chunks.push(chunk);
238246
});
239-
const complete = () => done(Buffer.concat(chunks).toString('utf8'));
247+
const complete = () => done(Buffer.concat(chunks).toString('utf8'), truncated);
240248
stream.on('end', complete);
241249
stream.on('error', complete);
242250
}
@@ -367,6 +375,15 @@ function frameRefusedPage(upstream: URL): ErrorPage {
367375
};
368376
}
369377

378+
function bodyTooLargePage(upstream: URL): ErrorPage {
379+
const limit = `${Math.round(HTML_BODY_LIMIT / (1024 * 1024))} MB`;
380+
return {
381+
title: `${upstream.host} sent too much HTML`,
382+
message: `The HTML response from ${upstream.href} exceeded HTML_BODY_LIMIT of ${limit}, so it couldn’t be instrumented and shown in an iframe surface.`,
383+
hint: `dor ab open ${upstream.href}`,
384+
};
385+
}
386+
370387
function unreachablePage(upstream: URL, detail: string): ErrorPage {
371388
return {
372389
title: `Nothing responding at ${upstream.host}`,

0 commit comments

Comments
 (0)