mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
fix(cctv): cap buffered snapshot downloads
Adapt the streamed snapshot size cap contributed in public PR #82 to the CCTV media module.
This commit is contained in:
1 parent
c78ea4194c
commit
59a20e2d18
2 files changed
+31
-7
No files matched your search
@@ -38,3 +38,6 @@ export const CCTV_SOURCE_FETCH_TIMEOUT_MS = 15 * 1000;
|
|||||||
* client refresh cadence. A bounded miss can fall through to Street View or
|
* client refresh cadence. A bounded miss can fall through to Street View or
|
||||||
* the synthetic frame instead of leaving the browser preview pending. */
|
* the synthetic frame instead of leaving the browser preview pending. */
|
||||||
export const CCTV_FRAME_FETCH_TIMEOUT_MS = 8 * 1000;
|
export const CCTV_FRAME_FETCH_TIMEOUT_MS = 8 * 1000;
|
||||||
|
|
||||||
|
/** Maximum buffered snapshot size. */
|
||||||
|
export const CCTV_FRAME_MAX_BODY_BYTES = 16 * 1024 * 1024;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Readable } from 'node:stream';
|
import { Readable } from 'node:stream';
|
||||||
import { hashSeed, escapeXml } from './normalize.js';
|
import { hashSeed, escapeXml } from './normalize.js';
|
||||||
import { CCTV_FRAME_FETCH_TIMEOUT_MS } from './constants.js';
|
import { CCTV_FRAME_FETCH_TIMEOUT_MS, CCTV_FRAME_MAX_BODY_BYTES } from './constants.js';
|
||||||
/**
|
/**
|
||||||
* Generate a synthetic SVG billboard image for a CCTV camera placeholder.
|
* Generate a synthetic SVG billboard image for a CCTV camera placeholder.
|
||||||
*
|
*
|
||||||
@@ -166,9 +166,32 @@ export async function proxyMediaResponse(
|
|||||||
* @param {number} [options.timeoutMs=CCTV_FRAME_FETCH_TIMEOUT_MS] - Abort timeout.
|
* @param {number} [options.timeoutMs=CCTV_FRAME_FETCH_TIMEOUT_MS] - Abort timeout.
|
||||||
* @returns {Promise<{ok:true,body:Buffer,contentType:string}|null>}
|
* @returns {Promise<{ok:true,body:Buffer,contentType:string}|null>}
|
||||||
*/
|
*/
|
||||||
|
async function readCappedResponseBytes(upstream, maxBytes) {
|
||||||
|
const declared = Number(upstream.headers.get('content-length'));
|
||||||
|
if (Number.isFinite(declared) && declared > maxBytes) {
|
||||||
|
try { await upstream.body?.cancel(); } catch { /* no-op */ }
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!upstream.body || typeof upstream.body[Symbol.asyncIterator] !== 'function') {
|
||||||
|
const buffered = Buffer.from(await upstream.arrayBuffer());
|
||||||
|
return buffered.length > maxBytes ? null : buffered;
|
||||||
|
}
|
||||||
|
const chunks = [];
|
||||||
|
let total = 0;
|
||||||
|
for await (const chunk of upstream.body) {
|
||||||
|
total += chunk.length;
|
||||||
|
if (total > maxBytes) {
|
||||||
|
try { await upstream.body.cancel(); } catch { /* no-op */ }
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
chunks.push(Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength));
|
||||||
|
}
|
||||||
|
return Buffer.concat(chunks, total);
|
||||||
|
}
|
||||||
|
|
||||||
export async function fetchCctvImageFromUpstream(
|
export async function fetchCctvImageFromUpstream(
|
||||||
url,
|
url,
|
||||||
{ fetchImpl = fetch, timeoutMs = CCTV_FRAME_FETCH_TIMEOUT_MS } = {},
|
{ fetchImpl = fetch, timeoutMs = CCTV_FRAME_FETCH_TIMEOUT_MS, maxBytes = CCTV_FRAME_MAX_BODY_BYTES } = {},
|
||||||
) {
|
) {
|
||||||
if (!url || !/^https?:\/\//i.test(url)) return null;
|
if (!url || !/^https?:\/\//i.test(url)) return null;
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
@@ -184,11 +207,9 @@ export async function fetchCctvImageFromUpstream(
|
|||||||
});
|
});
|
||||||
const contentType = upstream.headers.get('content-type') || '';
|
const contentType = upstream.headers.get('content-type') || '';
|
||||||
if (!upstream.ok || !contentType.startsWith('image/')) return null;
|
if (!upstream.ok || !contentType.startsWith('image/')) return null;
|
||||||
return {
|
const body = await readCappedResponseBytes(upstream, maxBytes);
|
||||||
ok: true,
|
if (!body) return null;
|
||||||
body: Buffer.from(await upstream.arrayBuffer()),
|
return { ok: true, body, contentType };
|
||||||
contentType,
|
|
||||||
};
|
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
Reference in new issue
Block a user