Created
April 6, 2024 19:08
-
-
Save eduardvercaemer/16670bfc6760c4c92f7948ded04c3dbd to your computer and use it in GitHub Desktop.
Basic websocket message passer for Cloudflare Durable Objects
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
declare global { | |
interface Env { | |
readonly CASTER: DurableObjectNamespace; | |
readonly RATE_LIMITER: { | |
limit(opt: { key: string }): Promise<{ success?: boolean }>; | |
}; | |
} | |
} | |
// noinspection JSUnusedGlobalSymbols | |
export class Caster { | |
constructor(private readonly state: DurableObjectState) { | |
this.state.setWebSocketAutoResponse( | |
new WebSocketRequestResponsePair("ping", "pong"), | |
); | |
} | |
async fetch(request: Request) { | |
const url = new URL(request.url); | |
switch (request.method) { | |
case "GET": { | |
if (request.headers.get("upgrade") !== "websocket") { | |
return new Response(null, { status: 422 }); | |
} | |
const tags = url.searchParams.getAll("t"); | |
const pair = new WebSocketPair(); | |
this.state.acceptWebSocket( | |
pair[1], | |
tags.length >= 0 ? tags : undefined, | |
); | |
return new Response(null, { status: 101, webSocket: pair[0] }); | |
} | |
case "POST": { | |
if ( | |
!request.headers.get("content-type")?.startsWith("application/json") | |
) { | |
return new Response(null, { status: 422 }); | |
} | |
const tag = url.searchParams.get("t"); | |
let payload: any; | |
try { | |
payload = await request.json(); | |
} catch { | |
return new Response(null, { status: 400 }); | |
} | |
const clients = this.state.getWebSockets(tag ?? undefined); | |
const message = JSON.stringify(payload); | |
clients.forEach((client) => client.send(message)); | |
return new Response(null, { status: 201 }); | |
} | |
default: | |
return new Response(null, { status: 422 }); | |
} | |
} | |
} | |
// noinspection JSUnusedGlobalSymbols | |
export default { | |
async fetch(request: Request, env: Env) { | |
const url = new URL(request.url); | |
const uuid = url.searchParams.get("id"); | |
if (!uuid) { | |
return new Response(null, { status: 403 }); | |
} | |
const { success } = await env.RATE_LIMITER.limit({ key: uuid }); | |
if (!success) { | |
return new Response(null, { status: 429 }); | |
} | |
const id = env.CASTER.idFromName(uuid); | |
const caster = env.CASTER.get(id); | |
return caster.fetch(request); | |
}, | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name = "caster" | |
main = "index.ts" | |
compatibility_date = "2024-04-06" | |
[[unsafe.bindings]] | |
name = "RATE_LIMITER" | |
type = "ratelimit" | |
namespace_id = "1001" | |
simple = { limit = 100, period = 60 } | |
[[durable_objects.bindings]] | |
name = "CASTER" | |
class_name = "Caster" | |
[[migrations]] | |
tag = "v1" | |
new_classes = ["Caster"] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Basic websocket message passer.
NOT MEANT FOR PUBLIC ROUTE. USE PROPER SERVICE BINDING OR ADD CUSTOM AUTH