maps: stop reading a community picture at its size cap

An answer without a content-length was buffered whole before its size was
checked. The read now stops at the cap.
This commit is contained in:
Maurice
2026-10-30 23:14:32 +00:00
parent 41edbcf67a
commit 2f689ea9ca
2 changed files with 18 additions and 2 deletions
@@ -1,3 +1,4 @@
import { readCapped } from '../../utils/cappedFetch';
import { trekCommunityByKeys, trekPlacesBaseUrl, trekPlacesById } from './trek-places.client';
import { identifiedFetch } from './trek-places.fetch';
import { Injectable } from '@nestjs/common';
@@ -81,8 +82,9 @@ export class TrekCommunityService {
await res.body?.cancel();
return null;
}
const bytes = Buffer.from(await res.arrayBuffer());
return bytes.length && bytes.length <= IMAGE_MAX_BYTES ? bytes : null;
// A chunked answer declares no length, so the read itself stops at the cap.
const { bytes, truncated } = await readCapped(res, IMAGE_MAX_BYTES);
return bytes.length && !truncated ? bytes : null;
} catch {
return null;
} finally {
@@ -91,4 +91,18 @@ describe('TrekCommunityService.imageBytes', () => {
fetchWithKey.mockRejectedValueOnce(new Error('timeout'));
expect(await svc.imageBytes(A)).toBeNull();
});
it('TCOM-007: an answer that declares no length stops being read at the cap', async () => {
// An endless body in 1 MB chunks, without a content-length, as a chunked answer comes.
let pulled = 0;
const body = new ReadableStream<Uint8Array>({
pull(controller) {
pulled += 1;
controller.enqueue(new Uint8Array(1_000_000));
},
});
fetchWithKey.mockResolvedValueOnce(new Response(body, { status: 200 }));
expect(await new TrekCommunityService().imageBytes(A)).toBeNull();
expect(pulled).toBeLessThanOrEqual(6);
});
});