Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion web/cloudflare-worker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Every request runs the Worker first (`run_worker_first: true`), which reads the
| HTML (`/`, `/index.html`, any SPA deep link) | Each `<script nonce="nonce-random-value">` gets a per-request nonce that matches the CSP. `Cache-Control: no-cache`, with no `ETag` or `Last-Modified`, since a 304 would pair a cached body with a new nonce |
| Content-hashed files (`main-<16 hex>.bundle.js`, `name.<10 hex>.css`) | `Cache-Control: public, max-age=31536000, immutable` |
| Files with fixed names (`version.json`, `service-worker.js`, `manifest.json`, ...) | Default `public, max-age=0, must-revalidate`, with an `ETag` |
| A file the current build doesn't have (`/main-<old hash>.bundle.js`) | The file from a previous build, if the environment has a `PREVIOUS_BUILDS` bucket that holds it (see [Files from previous builds](#files-from-previous-builds)). Otherwise `404`, so a stale tab gets a `ChunkLoadError` and recovers instead of parsing HTML as JavaScript |
| A file the current build doesn't have (`/main-<old hash>.bundle.js`) | The file from a previous build, if the environment has a `PREVIOUS_BUILDS` bucket that holds it (see [Files from previous builds](#files-from-previous-builds)). Otherwise `404`, also when the bucket can't be read, so a stale tab gets a `ChunkLoadError` and recovers instead of parsing HTML as JavaScript |
| Any other path that does not exist (`/r/123`) | The app shell (`index.html`) with `200` |
| `/.well-known/apple-app-site-association` and `/apple-app-site-association` | The same file with `Content-Type: application/json` |

Expand Down
18 changes: 12 additions & 6 deletions web/cloudflare-worker/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,19 @@ async function getPreviousBuildFile(env: Env, url: URL, method: string): Promise
return null;
}

if (method === 'HEAD') {
const object = await env.PREVIOUS_BUILDS.head(key);
return object ? new Response(null, {headers: getPreviousBuildFileHeaders(object)}) : null;
}
try {
if (method === 'HEAD') {
const object = await env.PREVIOUS_BUILDS.head(key);
return object ? new Response(null, {headers: getPreviousBuildFileHeaders(object)}) : null;
}

const object = await env.PREVIOUS_BUILDS.get(key);
return object ? new Response(object.body, {headers: getPreviousBuildFileHeaders(object)}) : null;
const object = await env.PREVIOUS_BUILDS.get(key);
return object ? new Response(object.body, {headers: getPreviousBuildFileHeaders(object)}) : null;
} catch (error) {
// A 404 lets the tab recover with a reload, as it does for a file no build has.
console.error(`Failed to read ${key} from the previous builds bucket`, error);
return null;
}
}

export default {
Expand Down
12 changes: 12 additions & 0 deletions web/cloudflare-worker/tests/worker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,18 @@ describe('staging worker with files from previous builds', () => {
assert.equal(await response.text(), '');
});

test('returns 404 when the previous builds bucket fails', async () => {
// Given a chunk path whose bucket lookup fails (R2 rejects keys over 1,024 bytes)
for (const method of ['GET', 'HEAD']) {
// When it is requested
const response = await server.fetch(`/${'a'.repeat(1100)}.bundle.js`, {method});

// Then the Worker answers as it does for a chunk no build has, instead of throwing
assert.equal(response.status, 404, method);
assertSecurityHeaders(response);
}
});

test('serves the current build when a previous build has the same file', async () => {
// Given a file that is in both the current build and the bucket
// When it is requested
Expand Down
Loading