From cf7ffc1cd99e6545c40cd2bcf0f8cb89c46b178f Mon Sep 17 00:00:00 2001 From: Jonathan Raphaelson Date: Thu, 5 Jun 2025 21:51:31 -0600 Subject: [PATCH] cleaning up and trying for better error handling --- deno.json | 6 +- deno.lock | 109 +++++++++-------------------------- src/common/aborts.ts | 26 ++++++++- src/common/blocking-atom.ts | 1 + src/common/blocking-queue.ts | 5 +- src/common/gate.ts | 2 + src/common/socket.ts | 35 +++++++---- src/schema/crypto.ts | 37 +++++++----- src/schema/proto.ts | 2 +- src/server/socket/handler.ts | 45 ++++++++++----- 10 files changed, 141 insertions(+), 127 deletions(-) diff --git a/deno.json b/deno.json index a1507c0..cfc1f35 100644 --- a/deno.json +++ b/deno.json @@ -25,7 +25,8 @@ "@deno/vite-plugin": "npm:@deno/vite-plugin@^1.0.4", "@oak/oak": "jsr:@oak/oak@^17.1.4", "@sitnik/nanoid": "jsr:@sitnik/nanoid@^5.1.5", - "@types/react": "npm:@types/react@^19.1.2", + "@types/react": "npm:@types/react@^19.1.6", + "@types/react-dom": "npm:@types/react-dom@^19.1.6", "@vitejs/plugin-react": "npm:@vitejs/plugin-react@^4.4.1", "react": "npm:react@^19.1.0", "react-dom": "npm:react-dom@^19.1.0", @@ -37,8 +38,7 @@ "compilerOptions": { "types": [ - "react", - "react-dom", + "@types/react-dom", "@types/react" ], "lib": [ diff --git a/deno.lock b/deno.lock index e6fd8c7..490d10c 100644 --- a/deno.lock +++ b/deno.lock @@ -2,41 +2,35 @@ "version": "5", "specifiers": { "jsr:@oak/commons@1": "1.0.1", - "jsr:@oak/oak@*": "17.1.4", "jsr:@oak/oak@^17.1.4": "17.1.4", "jsr:@panva/jose@^6.0.11": "6.0.11", "jsr:@sitnik/nanoid@^5.1.5": "5.1.5", "jsr:@std/assert@1": "1.0.13", - "jsr:@std/assert@^1.0.12": "1.0.13", "jsr:@std/bytes@1": "1.0.6", "jsr:@std/cli@*": "1.0.19", "jsr:@std/crypto@1": "1.0.5", "jsr:@std/encoding@1": "1.0.10", "jsr:@std/encoding@^1.0.10": "1.0.10", "jsr:@std/http@1": "1.0.17", - "jsr:@std/internal@^1.0.6": "1.0.8", "jsr:@std/media-types@1": "1.1.0", "jsr:@std/path@1": "1.1.0", "jsr:@std/regexp@*": "1.0.1", - "jsr:@std/ulid@*": "1.0.0", - "npm:@deno/vite-plugin@^1.0.4": "1.0.4_vite@6.3.5__picomatch@4.0.2_@types+node@22.15.15", - "npm:@types/node@*": "22.15.15", - "npm:@types/react@^19.1.2": "19.1.6", - "npm:@vitejs/plugin-react@^4.4.1": "4.5.1_vite@6.3.5__picomatch@4.0.2_@babel+core@7.27.4_@types+node@22.15.15", - "npm:lodash-es@*": "4.17.21", + "npm:@deno/vite-plugin@^1.0.4": "1.0.4_vite@6.3.5__picomatch@4.0.2", + "npm:@types/react-dom@^19.1.6": "19.1.6_@types+react@19.1.6", + "npm:@types/react@^19.1.6": "19.1.6", + "npm:@vitejs/plugin-react@^4.4.1": "4.5.1_vite@6.3.5__picomatch@4.0.2_@babel+core@7.27.4", "npm:path-to-regexp@^6.3.0": "6.3.0", "npm:react-dom@^19.1.0": "19.1.0_react@19.1.0", "npm:react-router-dom@^7.5.1": "7.6.2_react@19.1.0_react-dom@19.1.0__react@19.1.0", "npm:react@^19.1.0": "19.1.0", - "npm:vite@*": "6.3.5_picomatch@4.0.2_@types+node@22.15.15", - "npm:vite@^6.3.2": "6.3.5_picomatch@4.0.2_@types+node@22.15.15", - "npm:zod@3": "3.25.51" + "npm:vite@^6.3.2": "6.3.5_picomatch@4.0.2", + "npm:zod@3": "3.25.53" }, "jsr": { "@oak/commons@1.0.1": { "integrity": "889ff210f0b4292591721be07244ecb1b5c118742f5273c70cf30d7cd4184d0c", "dependencies": [ - "jsr:@std/assert@1", + "jsr:@std/assert", "jsr:@std/bytes", "jsr:@std/crypto", "jsr:@std/encoding@1", @@ -48,7 +42,7 @@ "integrity": "60530b582bf276ff741e39cc664026781aa08dd5f2bc5134d756cc427bf2c13e", "dependencies": [ "jsr:@oak/commons", - "jsr:@std/assert@1", + "jsr:@std/assert", "jsr:@std/bytes", "jsr:@std/http", "jsr:@std/media-types", @@ -63,10 +57,7 @@ "integrity": "55bd5f57087d67b1dcb7c1f4a07efdfe77a3ac57ca0af90f162c1f676ebf8f4b" }, "@std/assert@1.0.13": { - "integrity": "ae0d31e41919b12c656c742b22522c32fb26ed0cba32975cb0de2a273cb68b29", - "dependencies": [ - "jsr:@std/internal" - ] + "integrity": "ae0d31e41919b12c656c742b22522c32fb26ed0cba32975cb0de2a273cb68b29" }, "@std/bytes@1.0.6": { "integrity": "f6ac6adbd8ccd99314045f5703e23af0a68d7f7e58364b47d2c7f408aeb5820a" @@ -86,9 +77,6 @@ "jsr:@std/encoding@^1.0.10" ] }, - "@std/internal@1.0.8": { - "integrity": "fc66e846d8d38a47cffd274d80d2ca3f0de71040f855783724bb6b87f60891f5" - }, "@std/media-types@1.1.0": { "integrity": "c9d093f0c05c3512932b330e3cc1fe1d627b301db33a4c2c2185c02471d6eaa4" }, @@ -97,9 +85,6 @@ }, "@std/regexp@1.0.1": { "integrity": "5179d823465085c5480dafb44438466e83c424fadc61ba31f744050ecc0f596d" - }, - "@std/ulid@1.0.0": { - "integrity": "d41c3d27a907714413649fee864b7cde8d42ee68437d22b79d5de4f81d808780" } }, "npm": { @@ -189,8 +174,8 @@ "@babel/helper-validator-option@7.27.1": { "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==" }, - "@babel/helpers@7.27.4": { - "integrity": "sha512-Y+bO6U+I7ZKaM5G5rDUZiYfUvQPUibYmAFe7EnKdnKBbVXDZxvp+MWOH5gYciY0EPk4EScsuFMQBbEfpdRKSCQ==", + "@babel/helpers@7.27.6": { + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", "dependencies": [ "@babel/template", "@babel/types" @@ -237,8 +222,8 @@ "globals" ] }, - "@babel/types@7.27.3": { - "integrity": "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==", + "@babel/types@7.27.6": { + "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", "dependencies": [ "@babel/helper-string-parser", "@babel/helper-validator-identifier" @@ -247,13 +232,7 @@ "@deno/vite-plugin@1.0.4_vite@6.3.5__picomatch@4.0.2": { "integrity": "sha512-xg8YT8Wn2sGXSnJgiGTpBGX1Dov0c6fd1rAp8VsfrCUtyBRRWzwVMAnd3fQ4yq8h7LSVvJUxEFN4U421k/DQLA==", "dependencies": [ - "vite@6.3.5_picomatch@4.0.2" - ] - }, - "@deno/vite-plugin@1.0.4_vite@6.3.5__picomatch@4.0.2_@types+node@22.15.15": { - "integrity": "sha512-xg8YT8Wn2sGXSnJgiGTpBGX1Dov0c6fd1rAp8VsfrCUtyBRRWzwVMAnd3fQ4yq8h7LSVvJUxEFN4U421k/DQLA==", - "dependencies": [ - "vite@6.3.5_picomatch@4.0.2_@types+node@22.15.15" + "vite" ] }, "@esbuild/aix-ppc64@0.25.5": { @@ -540,10 +519,10 @@ "@types/estree@1.0.7": { "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==" }, - "@types/node@22.15.15": { - "integrity": "sha512-R5muMcZob3/Jjchn5LcO8jdKwSCbzqmPB6ruBxMcf9kbxtniZHP327s6C37iOfuw8mbKK3cAQa7sEl7afLrQ8A==", + "@types/react-dom@19.1.6_@types+react@19.1.6": { + "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", "dependencies": [ - "undici-types" + "@types/react" ] }, "@types/react@19.1.6": { @@ -561,19 +540,7 @@ "@rolldown/pluginutils", "@types/babel__core", "react-refresh", - "vite@6.3.5_picomatch@4.0.2" - ] - }, - "@vitejs/plugin-react@4.5.1_vite@6.3.5__picomatch@4.0.2_@babel+core@7.27.4_@types+node@22.15.15": { - "integrity": "sha512-uPZBqSI0YD4lpkIru6M35sIfylLGTyhGHvDZbNLuMA73lMlwJKz5xweH7FajfcCAc2HnINciejA9qTz0dr0M7A==", - "dependencies": [ - "@babel/core", - "@babel/plugin-transform-react-jsx-self", - "@babel/plugin-transform-react-jsx-source", - "@rolldown/pluginutils", - "@types/babel__core", - "react-refresh", - "vite@6.3.5_picomatch@4.0.2_@types+node@22.15.15" + "vite" ] }, "browserslist@4.25.0": { @@ -586,8 +553,8 @@ ], "bin": true }, - "caniuse-lite@1.0.30001720": { - "integrity": "sha512-Ec/2yV2nNPwb4DnTANEV99ZWwm3ZWfdlfkQbWSDDt+PsXEVYwlhPH8tdMaPunYTKKmz7AnHi2oNEi1GcmKCD8g==" + "caniuse-lite@1.0.30001721": { + "integrity": "sha512-cOuvmUVtKrtEaoKiO0rSc29jcjwMwX5tOHDy4MgVFEWiUXj4uBMJkwI8MDySkgXidpMiHUcviogAvFi4pA2hDQ==" }, "convert-source-map@2.0.0": { "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" @@ -604,8 +571,8 @@ "ms" ] }, - "electron-to-chromium@1.5.162": { - "integrity": "sha512-hQA+Zb5QQwoSaXJWEAGEw1zhk//O7qDzib05Z4qTqZfNju/FAkrm5ZInp0JbTp4Z18A6bilopdZWEYrFSsfllA==" + "electron-to-chromium@1.5.165": { + "integrity": "sha512-naiMx1Z6Nb2TxPU6fiFrUrDTjyPMLdTtaOd2oLmG8zVSg2hCWGkhPyxwk+qRmZ1ytwVqUv0u7ZcDA5+ALhaUtw==" }, "esbuild@0.25.5": { "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", @@ -673,9 +640,6 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "bin": true }, - "lodash-es@4.17.21": { - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" - }, "lru-cache@5.1.1": { "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dependencies": [ @@ -792,9 +756,6 @@ "picomatch" ] }, - "undici-types@6.21.0": { - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" - }, "update-browserslist-db@1.1.3_browserslist@4.25.0": { "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "dependencies": [ @@ -819,30 +780,11 @@ ], "bin": true }, - "vite@6.3.5_picomatch@4.0.2_@types+node@22.15.15": { - "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", - "dependencies": [ - "@types/node", - "esbuild", - "fdir", - "picomatch", - "postcss", - "rollup", - "tinyglobby" - ], - "optionalDependencies": [ - "fsevents" - ], - "optionalPeers": [ - "@types/node" - ], - "bin": true - }, "yallist@3.1.1": { "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, - "zod@3.25.51": { - "integrity": "sha512-TQSnBldh+XSGL+opiSIq0575wvDPqu09AqWe1F7JhUMKY+M91/aGlK4MhpVNO7MgYfHcVCB1ffwAUTJzllKJqg==" + "zod@3.25.53": { + "integrity": "sha512-BKOKoY3XcGUVkqaalCtFK15LhwR0G0i65AClFpWSXLN2gJNBGlTktukHgwexCTa/dAacPPp9ReryXPWyeZF4LQ==" } }, "workspace": { @@ -851,7 +793,8 @@ "jsr:@panva/jose@^6.0.11", "jsr:@sitnik/nanoid@^5.1.5", "npm:@deno/vite-plugin@^1.0.4", - "npm:@types/react@^19.1.2", + "npm:@types/react-dom@^19.1.6", + "npm:@types/react@^19.1.6", "npm:@vitejs/plugin-react@^4.4.1", "npm:react-dom@^19.1.0", "npm:react-router-dom@^7.5.1", diff --git a/src/common/aborts.ts b/src/common/aborts.ts index 4bb01eb..1beef94 100644 --- a/src/common/aborts.ts +++ b/src/common/aborts.ts @@ -1,3 +1,17 @@ +export class TimeoutError extends Error { + constructor(ms: number, operation?: string) { + super(`${operation ? operation+" " : ""}timed out after ${ms}ms`); + this.name = "TimeoutError"; + } +} + +export class CancellationError extends Error { + constructor(reason?: string) { + super(`operation was cancelled${reason ? `: ${reason}` : ""}`); + this.name = "CancellationError"; + } +} + export type CancellableAbortController = AbortController & { cancel: () => void; }; @@ -17,7 +31,7 @@ export function makeAbort(): CancellableAbortController { }; } -export function makeTimeoutAbort(ms: number): CancellableAbortController { +export function makeTimeoutAbort(ms: number, operation?: string): CancellableAbortController { const controller = new AbortController(); const { promise, triggered, cancel } = makeTimeoutPromise(ms); @@ -29,13 +43,19 @@ export function makeTimeoutAbort(ms: number): CancellableAbortController { promise.then(() => { if (!controller.signal.aborted) { - controller.abort(); + controller.abort(new TimeoutError(ms, operation)); } }); return { cancel, - abort: controller.abort.bind(controller), + abort: (reason?: any) => { + if (reason instanceof TimeoutError) { + controller.abort(reason); + } else { + controller.abort(new CancellationError(reason?.toString())); + } + }, signal: controller.signal, }; } diff --git a/src/common/blocking-atom.ts b/src/common/blocking-atom.ts index 02b140d..af96076 100644 --- a/src/common/blocking-atom.ts +++ b/src/common/blocking-atom.ts @@ -17,6 +17,7 @@ export class BlockingAtom { return this.#item; } + signal?.throwIfAborted(); return undefined; } } diff --git a/src/common/blocking-queue.ts b/src/common/blocking-queue.ts index 499af6f..13a226b 100644 --- a/src/common/blocking-queue.ts +++ b/src/common/blocking-queue.ts @@ -28,8 +28,11 @@ export class BlockingQueue { } async dequeue(signal?: AbortSignal) { - if (await this.#sema.take(signal)) return this.poll(); + if (await this.#sema.take(signal)) { + return this.poll(); + } + signal?.throwIfAborted(); throw Error("canceled dequeue"); } diff --git a/src/common/gate.ts b/src/common/gate.ts index 9a33227..8260523 100644 --- a/src/common/gate.ts +++ b/src/common/gate.ts @@ -10,6 +10,7 @@ export function makeGate void>( if (!done) { done = true; + // TODO: if these throw, what to do? outercb?.(); innercb(...args); } @@ -17,6 +18,7 @@ export function makeGate void>( many: (innercb: F) => (...args: Parameters): void => { if (!done) { + // TODO: if these throw, what to do? innercb(...args); } }, diff --git a/src/common/socket.ts b/src/common/socket.ts index 671cd0d..bae8561 100644 --- a/src/common/socket.ts +++ b/src/common/socket.ts @@ -1,4 +1,4 @@ -import { makeAbort, makeTimeoutAbort } from "./aborts.ts"; +import { makeAbort, makeTimeoutAbort, TimeoutError, CancellationError } from "./aborts.ts"; import { BlockingAtom } from "./blocking-atom.ts"; import { BlockingQueue } from "./blocking-queue.ts"; import { makeGate } from "./gate.ts"; @@ -29,8 +29,8 @@ export async function* streamSocket( const onMessage = gate.many((m: MessageEvent) => { queue.enqueue(["yield", m.data]); - if (queue.size > config.threshPause) { - config.onPause?.(); + if (queue.size > config.threshPause && config.onPause) { + queueMicrotask(config.onPause); } }); @@ -51,10 +51,11 @@ export async function* streamSocket( ws.addEventListener("close", onClose); while (true) { + config.signal?.throwIfAborted(); + const [event, value] = await queue.dequeue(config.signal); - if (queue.size < config.threshResume) { - config.onResume?.(); - } + if (queue.size < config.threshResume && config.onResume) + queueMicrotask(config.onResume); switch (event) { case "yield": @@ -75,22 +76,27 @@ export async function* streamSocket( } } +export async function takeSocket(ws: WebSocket): Promise; +export async function takeSocket( + ws: WebSocket, + ms: number, +): Promise; export async function takeSocket( ws: WebSocket, ms?: number, -): Promise { +): Promise { const atom = new BlockingAtom(); - const abort = ms ? makeTimeoutAbort(ms) : makeAbort(); + const abort = ms ? makeTimeoutAbort(ms, "socket read") : makeAbort(); const gate = makeGate(); // callback functions - consts so we can `off` them on clean up const onMessage = gate.once((m: MessageEvent) => atom.set(m.data)); - const onClose = gate.once(() => abort.abort("closed")); + const onClose = gate.once(() => abort.abort("socket closed")); const onError = gate.once((e: Error) => { if (e.message == "Unexpected EOF") { // todo: why are we getting this on client shutdown? - abort.abort("closed"); + abort.abort("socket closed"); } else { abort.abort(e); } @@ -102,6 +108,15 @@ export async function takeSocket( ws.addEventListener("error", onError); return await atom.get(abort.signal); + } catch (e) { + // only throw if we're not awaiting a timeout + // otherwise we know to expect to get undefined + if (ms && e instanceof DOMException && e.name === "AbortError" && abort.signal.reason instanceof TimeoutError) { + return undefined; + } + + // other errors bubble up (cancellations, socket errors, ...) + throw e; } finally { if (!abort.signal.aborted) { abort.cancel(); diff --git a/src/schema/crypto.ts b/src/schema/crypto.ts index 02de700..a4b5768 100644 --- a/src/schema/crypto.ts +++ b/src/schema/crypto.ts @@ -11,26 +11,37 @@ export const jwtDecodedSchema = z.jwt({ abort: true }).transform((val) => { }); export const jwtVerifier = (pubkey: CryptoKey) => - z.transform>(async (val, ctx) => { - try { - return await jwtVerify(val, pubkey, { algorithms: [SIGNING_ALGO] }); - } catch (e) { - ctx.issues.push({ - code: "custom", - message: `could not verify JWT: ${e}`, - input: val, - }); + z.transform>>( + async (val, ctx) => { + try { + return await jwtVerify(val, pubkey, { algorithms: [SIGNING_ALGO] }); + } catch (e) { + ctx.issues.push({ + code: "custom", + message: `could not verify JWT: ${e}`, + input: val, + }); - return z.NEVER; - } - }); + return z.NEVER; + } + }, + ); // keys export const jwkSchema = z.unknown().transform(async (val, ctx) => { try { if (typeof val === "object" && val !== null) { - return await importJWK(val, SIGNING_ALGO, { extractable: true }); + const key = await importJWK(val, SIGNING_ALGO, { extractable: true }); + if (key instanceof CryptoKey) { + return key; + } + + ctx.issues.push({ + code: "custom", + message: "symmetric keys unsupported", + input: val, + }); } else { ctx.issues.push({ code: "custom", diff --git a/src/schema/proto.ts b/src/schema/proto.ts index 189dc54..2801529 100644 --- a/src/schema/proto.ts +++ b/src/schema/proto.ts @@ -1,5 +1,5 @@ import { z } from "zod/v4"; -import { jwkSchema, jwtSchema } from "./crypto.ts"; +import { jwkSchema } from "./crypto.ts"; import { identIdSchema, realmIdSchema } from "./state.ts"; export const preauthAuthnMessageSchema = z.object({ diff --git a/src/server/socket/handler.ts b/src/server/socket/handler.ts index 691382f..9cb024d 100644 --- a/src/server/socket/handler.ts +++ b/src/server/socket/handler.ts @@ -1,4 +1,5 @@ import { takeSocket } from "@repo/common/socket.ts"; +import { TimeoutError, CancellationError } from "@repo/common/aborts.ts"; import { z } from "zod/v4"; import { StrictMap } from "@repo/common/strict-map.ts"; @@ -12,6 +13,8 @@ type Realm = { identities: StrictMap; }; +export class AuthError extends Error {} + /* const realmMap = new StrictMap(); @@ -26,29 +29,45 @@ function detachSocket(realm: Realm, ident: IdentId, socket: WebSocket) { export async function socketHandler(this: WebSocket) { try { - await preauthHandler(this); + const jwt = await preauthHandler(this); + console.log(jwt) } catch (e) { - if (e instanceof z.ZodError) { - this.send(`400 bad params: ${z.prettifyError(e)}\n`); - } else { - this.send(`500 server error: ${e}\n`); - console.error(e); - } - - this.send(`kthkbye\n`); + errorHandler(this, e) } finally { if (this.readyState !== this.CLOSED) { + this.send(`kthkbye\n`); this.close(); } - console.log("kthxbye"); } } async function preauthHandler(ws: WebSocket) { const data = await takeSocket(ws, 3000); - const jwt = await jwtDecodedSchema.pipe(preauthMessageSchema).parseAsync( - data, - ); + if (!data) { + throw new AuthError("authentication timeout"); + } + const jwt = await jwtDecodedSchema.pipe(preauthMessageSchema).parseAsync(data); console.log(jwt); + + return jwt; } + +function errorHandler(ws: WebSocket, e: any) { + if (e instanceof z.ZodError) { + ws.send(`400 bad params: ${z.prettifyError(e)}\n`); + console.warn('400 bad params') + } else if (e instanceof AuthError) { + ws.send(`403 forbidden: ${e.message}\n`); + console.warn('404 forbidden') + } else if (e instanceof TimeoutError) { + ws.send(`408 request timeout: ${e.message}\n`); + console.warn('408 request timeout') + } else if (e instanceof CancellationError) { + ws.send(`499 client closed connection: ${e.message}\n`); + console.warn('499 client closed connection') + } else { + ws.send(`500 server error: ${e}\n`); + console.error(e); + } +} \ No newline at end of file -- 2.51.2