import { HttpError } from "./errors";
import { CAMO_PREFIX, verifyCamoPath } from "./camo-sign";
import { enforceRateLimit, requestIp } from "./limits";
import { assertResolvesPublic } from "./net/resolve-guard";
import { isOwnHost } from "./camo-sign";
import { limitStream } from "./git/ref-sync-protocol";
const MAX_IMAGE_BYTES = 5 * 1024 * 1024;
const MAX_REDIRECTS = 3;
const UPSTREAM_TIMEOUT_MS = 10_000;
const CACHE_TTL_SECONDS = 24 * 60 * 60;
const NEGATIVE_TTL_SECONDS = 5 * 60;
const USER_AGENT = "gitfor-camo/1.0 (+image proxy)";
const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
const HTTP_OK = 200;
const HTTP_NOT_FOUND = 404;
const HTTP_UNSUPPORTED = 415;
const HTTP_TOO_LARGE = 413;
const HTTP_BAD_GATEWAY = 502;
const HTTP_METHOD_NOT_ALLOWED = 405;
/** Raster formats browsers render in `<img>`; SVG is allowed only because the sandbox CSP makes its scripts inert. */
const IMAGE_TYPE = /^image\/(?:png|jpeg|gif|webp|avif|bmp|x-icon|vnd\.microsoft\.icon|apng|svg\+xml)$/i;
const RESPONSE_HEADERS: Record<string, string> = {
"x-content-type-options": "nosniff",
"content-disposition": "inline",
"content-security-policy": "default-src 'none'; sandbox",
"referrer-policy": "no-referrer",
"cross-origin-resource-policy": "same-origin",
"x-frame-options": "DENY",
};
function reply(status: number, message: string): Response {
return new Response(`${message}\n`, {
status,
headers: {
...RESPONSE_HEADERS,
"content-type": "text/plain; charset=utf-8",
"cache-control": `public, max-age=${NEGATIVE_TTL_SECONDS}`,
},