Skip to content

Commit

Permalink
feat(cloudflare-durable): allow redirect fetch to durable object (#3048)
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 authored Jan 30, 2025
1 parent 12fab02 commit 74a1ad7
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 35 deletions.
2 changes: 1 addition & 1 deletion playground/wrangler.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ class_name = "$DurableObject"

[[migrations]]
tag = "v1"
new_classes = ["$DurableObject"]
new_sqlite_classes = ["$DurableObject"]
65 changes: 40 additions & 25 deletions src/presets/cloudflare/runtime/_module-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,47 +11,26 @@ export function createHandler<Env>(hooks: {
...params: [
...Parameters<NonNullable<ExportedHandler<Env>["fetch"]>>,
url: URL,
cfContextExtras: any,
]
) => MaybePromise<Response | CF.Response | undefined>;
}) {
const nitroApp = useNitroApp();

return <ExportedHandler<Env>>{
async fetch(request, env, context) {
const ctxExt = {};
const url = new URL(request.url);

// Preset-specific logic
if (hooks.fetch) {
const res = await hooks.fetch(request, env, context, url);
const res = await hooks.fetch(request, env, context, url, ctxExt);
if (res) {
return res;
}
}

let body;
if (requestHasBody(request as unknown as Request)) {
body = Buffer.from(await request.arrayBuffer());
}

// Expose latest env to the global context
(globalThis as any).__env__ = env;

return nitroApp.localFetch(url.pathname + url.search, {
context: {
cf: (request as any).cf,
waitUntil: (promise: Promise<any>) => context.waitUntil(promise),
cloudflare: {
request,
env,
context,
},
},
host: url.hostname,
protocol: url.protocol,
method: request.method,
headers: request.headers as unknown as Headers,
body,
}) as unknown as Promise<Response>;
return fetchHandler(request, env, context, url, nitroApp, ctxExt);
},

scheduled(controller, env, context) {
Expand Down Expand Up @@ -125,3 +104,39 @@ export function createHandler<Env>(hooks: {
},
};
}

export async function fetchHandler(
request: Request | CF.Request,
env: unknown,
context: CF.ExecutionContext | DurableObjectState,
url: URL = new URL(request.url),
nitroApp = useNitroApp(),
ctxExt: any
) {
let body;
if (requestHasBody(request as unknown as Request)) {
body = Buffer.from(await request.arrayBuffer());
}

// Expose latest env to the global context
(globalThis as any).__env__ = env;

return nitroApp.localFetch(url.pathname + url.search, {
context: {
cf: (request as any).cf,
waitUntil: (promise: Promise<any>) => context.waitUntil(promise),
cloudflare: {
request,
env,
context,
url,
...ctxExt,
},
},
host: url.hostname,
protocol: url.protocol,
method: request.method,
headers: request.headers as unknown as Headers,
body,
}) as unknown as Promise<Response>;
}
47 changes: 38 additions & 9 deletions src/presets/cloudflare/runtime/cloudflare-durable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,47 @@ import { DurableObject } from "cloudflare:workers";
import wsAdapter from "crossws/adapters/cloudflare-durable";
import { useNitroApp } from "nitropack/runtime";
import { isPublicAssetURL } from "#nitro-internal-virtual/public-assets";
import { createHandler } from "./_module-handler";
import { createHandler, fetchHandler } from "./_module-handler";

const nitroApp = useNitroApp();

const ws = import.meta._websocket
? wsAdapter(nitroApp.h3App.websocket)
: undefined;
const DURABLE_BINDING = "$DurableObject";
const DURABLE_INSTANCE = "server";

interface Env {
ASSETS?: { fetch: typeof CF.fetch };
[DURABLE_BINDING]?: CF.DurableObjectNamespace;
}

const nitroApp = useNitroApp();

const getDurableStub = (env: Env) => {
const binding = env[DURABLE_BINDING];
if (!binding) {
throw new Error(
`Durable Object binding "${DURABLE_BINDING}" not available.`
);
}
const id = binding.idFromName(DURABLE_INSTANCE);
return binding.get(id);
};

const ws = import.meta._websocket
? wsAdapter({
...nitroApp.h3App.websocket,
instanceName: DURABLE_INSTANCE,
bindingName: DURABLE_BINDING,
})
: undefined;

export default createHandler<Env>({
fetch(request, env, context, url) {
fetch(request, env, context, url, ctxExt) {
// Static assets fallback (optional binding)
if (env.ASSETS && isPublicAssetURL(url.pathname)) {
return env.ASSETS.fetch(request);
}

// Expose stub fetch to the context
ctxExt.durableFetch = (req = request) => getDurableStub(env).fetch(req);

// Websocket upgrade
// https://crossws.unjs.io/adapters/cloudflare#durable-objects
if (
Expand All @@ -49,10 +71,17 @@ export class $DurableObject extends DurableObject {
}

override fetch(request: Request) {
if (import.meta._websocket) {
if (
import.meta._websocket &&
request.headers.get("upgrade") === "websocket"
) {
return ws!.handleDurableUpgrade(this, request);
}
return new Response("404", { status: 404 });
// Main handler
const url = new URL(request.url);
return fetchHandler(request, this.env, this.ctx, url, nitroApp, {
durable: this,
});
}

override alarm(): void | Promise<void> {
Expand Down

0 comments on commit 74a1ad7

Please sign in to comment.