diff --git a/src/index.js b/src/index.js index 17ca86f..07f149f 100644 --- a/src/index.js +++ b/src/index.js @@ -175,6 +175,11 @@ async function handleRequest(request, env, ctx) { // Check if this is a Hugging Face API request const isHF = isHuggingFaceAPIRequest(request, url); + const shouldVaryCacheByOrigin = + platform === 'flathub' && isFlatpakReferenceFilePath(effectivePath); + const cacheTargetUrl = shouldVaryCacheByOrigin + ? `${targetUrl}${targetUrl.includes('?') ? '&' : '?'}__xget_origin=${encodeURIComponent(url.origin)}` + : targetUrl; const canUseCache = request.method === 'GET' || request.method === 'HEAD'; const shouldPassthroughRequest = isGit || isGitLFS || isDocker || isAI || isHF || !canUseCache; @@ -199,7 +204,7 @@ async function handleRequest(request, env, ctx) { ) { try { // For Range requests, try cache match first - const cacheKey = new Request(targetUrl, { + const cacheKey = new Request(cacheTargetUrl, { method: 'GET', headers: request.headers }); @@ -211,7 +216,7 @@ async function handleRequest(request, env, ctx) { // If Range request missed cache, try with original request to see if we have full content cached const rangeHeader = request.headers.get('Range'); if (rangeHeader) { - const fullContentKey = new Request(targetUrl, { + const fullContentKey = new Request(cacheTargetUrl, { method: 'GET', // Always use GET method for cache key consistency headers: new Headers( [...request.headers.entries()].filter( @@ -631,7 +636,7 @@ async function handleRequest(request, env, ctx) { ) { const rangeHeader = request.headers.get('Range'); const cacheKey = rangeHeader - ? new Request(targetUrl, { + ? new Request(cacheTargetUrl, { method: 'GET', headers: new Headers( [...request.headers.entries()].filter( @@ -639,7 +644,7 @@ async function handleRequest(request, env, ctx) { ) ) }) - : new Request(targetUrl, { method: 'GET' }); + : new Request(cacheTargetUrl, { method: 'GET' }); try { if (ctx && typeof ctx.waitUntil === 'function') { @@ -652,7 +657,7 @@ async function handleRequest(request, env, ctx) { if (rangeHeader && response.status === 200) { const rangedResponse = await cache.match( - new Request(targetUrl, { + new Request(cacheTargetUrl, { method: 'GET', headers: request.headers }) diff --git a/test/unit/flathub-rewrite.test.js b/test/unit/flathub-rewrite.test.js index b6b4070..71e9b19 100644 --- a/test/unit/flathub-rewrite.test.js +++ b/test/unit/flathub-rewrite.test.js @@ -94,6 +94,42 @@ describe('Flathub Response Rewriting', () => { expect(body).toContain('RuntimeRepo=https://example.com/flathub/repo/flathub.flatpakrepo'); }); + it('uses host-scoped cache keys for rewritten Flathub descriptors', async () => { + const cacheEntries = new Map(); + + vi.stubGlobal('caches', { + default: { + match: vi.fn(async request => cacheEntries.get(request.url) || null), + put: vi.fn(async (request, response) => { + cacheEntries.set(request.url, response.clone()); + }) + } + }); + + const fetchSpy = vi.spyOn(globalThis, 'fetch').mockImplementation( + async () => + new Response(`[Flatpak Repo]\nUrl=https://dl.flathub.org/repo/`, { + status: 200, + headers: { 'Content-Type': 'application/octet-stream' } + }) + ); + + const responseA = await worker.fetch( + new Request('https://mirror-a.example/flathub/repo/flathub.flatpakrepo'), + {}, + executionContext + ); + const responseB = await worker.fetch( + new Request('https://mirror-b.example/flathub/repo/flathub.flatpakrepo'), + {}, + executionContext + ); + + expect(fetchSpy).toHaveBeenCalledTimes(2); + expect(await readUtf8Text(responseA)).toContain('Url=https://mirror-a.example/flathub/repo/'); + expect(await readUtf8Text(responseB)).toContain('Url=https://mirror-b.example/flathub/repo/'); + }); + it('does not rewrite binary repository metadata like summary files', async () => { vi.spyOn(globalThis, 'fetch').mockResolvedValue( new Response('summary-binary-payload', {