Skip to content

Commit

Permalink
Wranger dev support with experimental_serve_directly (#7445)
Browse files Browse the repository at this point in the history
  • Loading branch information
WillTaylorDev authored Dec 5, 2024
1 parent 8c873ed commit f4ae6ee
Show file tree
Hide file tree
Showing 13 changed files with 236 additions and 5 deletions.
6 changes: 6 additions & 0 deletions .changeset/strong-kings-visit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@cloudflare/workers-shared": minor
"wrangler": minor
---

Support for `assets.experimental_serve_directly` with `wrangler dev`
17 changes: 17 additions & 0 deletions fixtures/workers-with-assets-serve-directly/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# workers-assets-with-user-worker"

`workers-assets-with-user-worker-serve-directly"` is a test fixture that showcases Workers with Assets using the serve_directly option. This particular fixture forces a user Worker to be invoked, even if it otherwise would have matched an assets request.

## dev

To start a dev session you can run

```
wrangler dev
```

## Run tests

```
npm run test
```
20 changes: 20 additions & 0 deletions fixtures/workers-with-assets-serve-directly/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "workers-assets-with-user-worker-serve-directly",
"private": true,
"scripts": {
"check:type": "tsc",
"dev": "npx wrangler dev",
"test:ci": "vitest run",
"test:watch": "vitest",
"type:tests": "tsc -p ./tests/tsconfig.json"
},
"devDependencies": {
"@cloudflare/workers-tsconfig": "workspace:*",
"@cloudflare/workers-types": "^4.20241106.0",
"undici": "catalog:default",
"wrangler": "workspace:*"
},
"volta": {
"extends": "../../package.json"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<h1>Hello Workers + Assets World 🚀!</h1>
22 changes: 22 additions & 0 deletions fixtures/workers-with-assets-serve-directly/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export interface Env {
// Example binding to a Service. Learn more at https://developers.cloudflare.com/workers/runtime-apis/service-bindings/
ASSETS: Fetcher;
}

export default {
async fetch(request: Request, env: Env): Promise<Response> {
const auth = request.headers.get("Authorization");

if (!auth) {
return new Response("Forbidden", {
status: 403,
statusText: "Forbidden",
headers: {
"Content-Type": "text/plain",
},
});
}

return await env.ASSETS.fetch(request);
},
};
37 changes: 37 additions & 0 deletions fixtures/workers-with-assets-serve-directly/tests/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { resolve } from "node:path";
import { fetch } from "undici";
import { afterAll, beforeAll, describe, it } from "vitest";
import { runWranglerDev } from "../../shared/src/run-wrangler-long-lived";

describe("[Workers + Assets] serve_directly false", () => {
let ip: string, port: number, stop: (() => Promise<unknown>) | undefined;

beforeAll(async () => {
({ ip, port, stop } = await runWranglerDev(resolve(__dirname, ".."), [
"--port=0",
"--inspector-port=0",
]));
});

afterAll(async () => {
await stop?.();
});

it("should return a 403 without an Authorization header", async ({
expect,
}) => {
let response = await fetch(`http://${ip}:${port}/index.html`);
let text = await response.text();
expect(response.status).toBe(403);
});

it("should return a 200 with an Authorization header", async ({ expect }) => {
let response = await fetch(`http://${ip}:${port}/index.html`, {
headers: { Authorization: "some auth header" },
});
let text = await response.text();

expect(response.status).toBe(200);
expect(text).toContain(`<h1>Hello Workers + Assets World 🚀!</h1>`);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "@cloudflare/workers-tsconfig/tsconfig.json",
"compilerOptions": {
"types": ["node"]
},
"include": ["**/*.ts", "../../../node-types.d.ts"]
}
13 changes: 13 additions & 0 deletions fixtures/workers-with-assets-serve-directly/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"lib": ["ES2020"],
"types": ["@cloudflare/workers-types"],
"moduleResolution": "node",
"noEmit": true,
"skipLibCheck": true
},
"include": ["**/*.ts"],
"exclude": ["tests"]
}
10 changes: 10 additions & 0 deletions fixtures/workers-with-assets-serve-directly/wrangler.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#:schema node_modules/wrangler/config-schema.json

name = "worker-with-assets-serve-directly"
main = "src/index.ts"
compatibility_date = "2024-01-01"

[assets]
directory = "./public"
binding="ASSETS"
experimental_serve_directly = false
22 changes: 22 additions & 0 deletions packages/workers-shared/router-worker/tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,26 @@ describe("unit tests", async () => {
const response = await worker.fetch(request, env, ctx);
expect(await response.text()).toEqual("hello from asset worker");
});

it("it returns fetch from asset worker when matching existing asset path and invoke_user_worker_ahead_of_assets is not provided", async () => {
const request = new Request("https://example.com");
const ctx = createExecutionContext();

const env = {
CONFIG: {
has_user_worker: false,
},
ASSET_WORKER: {
async fetch(_: Request): Promise<Response> {
return new Response("hello from asset worker");
},
async unstable_canFetch(_: Request): Promise<boolean> {
return true;
},
},
} as typeof env;

const response = await worker.fetch(request, env, ctx);
expect(await response.text()).toEqual("hello from asset worker");
});
});
58 changes: 58 additions & 0 deletions packages/wrangler/e2e/deployments.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,4 +400,62 @@ describe("Workers + Assets deployment", { timeout: TIMEOUT }, () => {
);
expect(text).toContain("<h1>404.html</h1>");
});

it("runs user worker ahead of matching assets when serve_directly = false", async () => {
await helper.seed({
"wrangler.toml": dedent`
name = "${workerName}"
main = "src/index.ts"
compatibility_date = "2023-01-01"
[assets]
directory = "public"
binding = "ASSETS"
html_handling = "none"
not_found_handling = "404-page"
experimental_serve_directly = false
`,
"src/index.ts": dedent`
export default {
async fetch(request, env) {
return new Response("Hello World from User Worker!")
}
}`,
...initialAssets,
});

const output = await helper.run(`wrangler deploy`);
// expect only no asset files to be uploaded as no new asset files have been added
expect(normalize(output.stdout)).toMatchInlineSnapshot(`
"🌀 Building list of assets...
🌀 Starting asset upload...
No files to upload. Proceeding with deployment...
Total Upload: xx KiB / gzip: xx KiB
Uploaded tmp-e2e-worker-00000000-0000-0000-0000-000000000000 (TIMINGS)
Deployed tmp-e2e-worker-00000000-0000-0000-0000-000000000000 triggers (TIMINGS)
https://tmp-e2e-worker-00000000-0000-0000-0000-000000000000.SUBDOMAIN.workers.dev
Current Version ID: 00000000-0000-0000-0000-000000000000"
`);

const match = output.stdout.match(
/(?<url>https:\/\/tmp-e2e-.+?\..+?\.workers\.dev)/
);
assert(match?.groups);
deployedUrl = match.groups.url;

const testCases: AssetTestCase[] = [
{
path: "/index.html",
content: "Hello World from User Worker!",
},
{
path: "/",
content: "Hello World from User Worker!",
},
{
path: "/worker",
content: "Hello World from User Worker!",
},
];
await checkAssets(testCases, deployedUrl);
});
});
3 changes: 3 additions & 0 deletions packages/wrangler/src/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,9 @@ export function getAssetsOptions(

const routingConfig = {
has_user_worker: Boolean(args.script || config.main),
invoke_user_worker_ahead_of_assets: !(
config.assets?.experimental_serve_directly ?? true
),
};
// defaults are set in asset worker
const assetConfig = {
Expand Down
25 changes: 20 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit f4ae6ee

Please sign in to comment.