perf(cache): add strategy-based cache policy
This commit is contained in:
1 parent
79e6069c37
commit
931463ed63
14 files changed
+407
-49
No files matched your search
@@ -103,7 +103,7 @@ describe('Authentication Header Forwarding', () => {
|
||||
expect(new Headers(fetchSpy.mock.calls[0][1]?.headers).get('Authorization')).toBe(authToken);
|
||||
expect(cacheDefault.match).not.toHaveBeenCalled();
|
||||
expect(cacheDefault.put).not.toHaveBeenCalled();
|
||||
expect(response.headers.get('Cache-Control')).toBe('no-store');
|
||||
expect(response.headers.get('Cache-Control')).toBe('private, no-store');
|
||||
});
|
||||
|
||||
it('forwards Authorization for gated Hugging Face model downloads', async () => {
|
||||
|
||||
@@ -47,6 +47,27 @@ describe('Cache Privacy', () => {
|
||||
expect(response.status).toBe(200);
|
||||
expect(cacheDefault.match).not.toHaveBeenCalled();
|
||||
expect(cacheDefault.put).not.toHaveBeenCalled();
|
||||
expect(fetchStub).toHaveBeenCalled();
|
||||
expect(fetchStub.mock.calls[0][1]?.cf).toBeUndefined();
|
||||
expect(response.headers.get('Cache-Control')).toBe('private, no-store');
|
||||
});
|
||||
|
||||
it('should not enable Cloudflare fetch caching for requests with Cookie', async () => {
|
||||
const request = new Request('https://example.com/gh/test/repo/file.txt', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Cookie: 'session=secret'
|
||||
}
|
||||
});
|
||||
|
||||
const ctx = { waitUntil: () => {}, passThroughOnException: () => {} };
|
||||
const response = await worker.fetch(request, {}, ctx);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(cacheDefault.match).not.toHaveBeenCalled();
|
||||
expect(cacheDefault.put).not.toHaveBeenCalled();
|
||||
expect(fetchStub).toHaveBeenCalled();
|
||||
expect(fetchStub.mock.calls[0][1]?.cf).toBeUndefined();
|
||||
expect(response.headers.get('Cache-Control')).toBe('private, no-store');
|
||||
});
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { createRequestContext } from '../../src/app/request-context.js';
|
||||
import { CONFIG } from '../../src/config/index.js';
|
||||
import { finalizeResponse } from '../../src/response/finalize-response.js';
|
||||
import { resolveTarget } from '../../src/routing/resolve-target.js';
|
||||
import { tryReadCachedResponse } from '../../src/upstream/cache.js';
|
||||
import { fetchUpstreamResponse } from '../../src/upstream/fetch-upstream.js';
|
||||
import { PerformanceMonitor } from '../../src/utils/performance.js';
|
||||
@@ -112,5 +113,89 @@ describe('Pipeline modules', () => {
|
||||
expect(response.headers.get('Content-Length')).toBe(
|
||||
String(new TextEncoder().encode(body).byteLength)
|
||||
);
|
||||
expect(response.headers.get('Cache-Control')).toBe(
|
||||
'public, max-age=0, s-maxage=60, must-revalidate'
|
||||
);
|
||||
});
|
||||
|
||||
it('uses long-lived caching for immutable package artifacts', async () => {
|
||||
const artifactCases = [
|
||||
{
|
||||
cacheTargetUrl: 'https://registry.npmjs.org/pkg/-/pkg-1.0.0.tgz',
|
||||
effectivePath: '/npm/pkg/-/pkg-1.0.0.tgz',
|
||||
platform: 'npm',
|
||||
requestUrl: 'https://example.com/npm/pkg/-/pkg-1.0.0.tgz'
|
||||
},
|
||||
{
|
||||
cacheTargetUrl:
|
||||
'https://files.pythonhosted.org/packages/py3/r/requests/requests-2.31.0-py3-none-any.whl',
|
||||
effectivePath: '/pypi/files/packages/py3/r/requests/requests-2.31.0-py3-none-any.whl',
|
||||
platform: 'pypi-files',
|
||||
requestUrl:
|
||||
'https://example.com/pypi/files/packages/py3/r/requests/requests-2.31.0-py3-none-any.whl'
|
||||
},
|
||||
{
|
||||
cacheTargetUrl: 'https://repo1.maven.org/maven2/org/example/demo/1.0.0/demo-1.0.0.jar',
|
||||
effectivePath: '/maven/maven2/org/example/demo/1.0.0/demo-1.0.0.jar',
|
||||
platform: 'maven',
|
||||
requestUrl: 'https://example.com/maven/maven2/org/example/demo/1.0.0/demo-1.0.0.jar'
|
||||
}
|
||||
];
|
||||
|
||||
for (const artifactCase of artifactCases) {
|
||||
const request = new Request(artifactCase.requestUrl);
|
||||
const requestContext = createRequestContext(request, {});
|
||||
|
||||
const response = await finalizeResponse({
|
||||
cache: null,
|
||||
cacheTargetUrl: artifactCase.cacheTargetUrl,
|
||||
canUseCache: true,
|
||||
config: CONFIG,
|
||||
ctx: /** @type {ExecutionContext} */ ({ waitUntil() {}, passThroughOnException() {} }),
|
||||
effectivePath: artifactCase.effectivePath,
|
||||
hasSensitiveHeaders: false,
|
||||
monitor: new PerformanceMonitor(),
|
||||
platform: artifactCase.platform,
|
||||
request,
|
||||
requestContext,
|
||||
response: new Response('artifact-data', {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'Content-Length': '13'
|
||||
}
|
||||
}),
|
||||
responseGeneratedLocally: false,
|
||||
url: new URL(request.url)
|
||||
});
|
||||
|
||||
expect(response.headers.get('Cache-Control')).toBe(
|
||||
'public, max-age=3600, s-maxage=86400, immutable'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('varies npm metadata cache keys by request origin after rewriting', () => {
|
||||
const targetA = resolveTarget(
|
||||
new URL('https://mirror-a.example/npm/pkg'),
|
||||
'/npm/pkg',
|
||||
CONFIG.PLATFORMS
|
||||
);
|
||||
const targetB = resolveTarget(
|
||||
new URL('https://mirror-b.example/npm/pkg'),
|
||||
'/npm/pkg',
|
||||
CONFIG.PLATFORMS
|
||||
);
|
||||
|
||||
expect('cacheTargetUrl' in targetA && targetA.cacheTargetUrl).toContain(
|
||||
'__xget_origin=https%3A%2F%2Fmirror-a.example'
|
||||
);
|
||||
expect('cacheTargetUrl' in targetB && targetB.cacheTargetUrl).toContain(
|
||||
'__xget_origin=https%3A%2F%2Fmirror-b.example'
|
||||
);
|
||||
expect('cacheTargetUrl' in targetA && 'cacheTargetUrl' in targetB).toBe(true);
|
||||
if ('cacheTargetUrl' in targetA && 'cacheTargetUrl' in targetB) {
|
||||
expect(targetA.cacheTargetUrl).not.toBe(targetB.cacheTargetUrl);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user