Skip to content

Instantly share code, notes, and snippets.

@lucacasonato
Last active April 3, 2021 22:43
Show Gist options
  • Save lucacasonato/1a30a4fa6ef6c053a93f271675ef93fc to your computer and use it in GitHub Desktop.
Save lucacasonato/1a30a4fa6ef6c053a93f271675ef93fc to your computer and use it in GitHub Desktop.
FetchEvent polyfill in Deno, with demo

This example demonstrates how to polyfill the "fetch" event in Deno, and gives an example for how this can be used to run Cloudflare Workers in Deno.

To try it locally run the script below, and visit http://0.0.0.0:8080:

$ deno run --allow-net https://gist.githubusercontent.com/lucacasonato/1a30a4fa6ef6c053a93f271675ef93fc/raw/efcdc8e798604e194831830fcb962b50261384b3/example-worker.js
// Import the polyfill.
import { serve } from "./polyfill.js";
// The code below is copied from the Cloudflare Worker examples
// exactly, and was not modified. It runs as is with the polyfill.
// See https://developers.cloudflare.com/workers/examples/return-html
const html = `<!DOCTYPE html>
<body>
<h1>Hello World</h1>
<p>This markup was generated by a Cloudflare Worker.</p>
</body>`
async function handleRequest(request) {
return new Response(html, {
headers: {
"content-type": "text/html;charset=UTF-8",
},
})
}
addEventListener("fetch", event => {
return event.respondWith(handleRequest(event.request))
})
// This starts the polyfill. It will serve requests on port 8080 on all interfaces.
await serve(":8080")
// Be careful if you want to use this in production. This polyfill has no proper
// error handling.
import { Server } from "https://deno.land/std@0.87.0/http/server.ts";
import { readerFromStreamReader } from "https://deno.land/std@0.87.0/io/streams.ts";
import { green } from "https://deno.land/std@0.87.0/fmt/colors.ts";
class FetchEventImpl extends Event {
#stdReq;
#request;
get request() {
return this.#request;
}
set request(_) {
// noop
}
constructor(stdReq, addr) {
super("fetch");
const host = stdReq.headers.get("host") ?? addr;
this.#stdReq = stdReq;
this.#request = new Request(
new URL(stdReq.url, `http://${host}`).toString(),
{
body: new ReadableStream({
start: async (controller) => {
for await (const chunk of Deno.iter(stdReq.body)) {
controller.enqueue(chunk);
}
controller.close();
},
}),
headers: stdReq.headers,
method: stdReq.method,
},
);
}
async respondWith(response) {
const resp = await response;
await this.#stdReq.respond({
headers: resp.headers,
status: resp.status,
body: resp.body != null
? readerFromStreamReader(resp.body.getReader())
: undefined,
});
return resp;
}
[Symbol.toStringTag]() {
return "FetchEvent";
}
}
window.FetchEvent = FetchEventImpl;
export async function serve(addr) {
if (typeof addr === "string") {
const [hostname, port] = addr.split(":");
addr = { hostname, port: Number(port) };
}
const listener = Deno.listen(addr);
const host = `${listener.addr.hostname}:${listener.addr.port}`;
console.log(green(`Listening on http://${host}`));
const server = new Server(listener);
for await (const req of server) {
window.dispatchEvent(
new FetchEventImpl(
req,
host,
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment