mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-18 01:36:08 +08:00
61 lines
1.8 KiB
TypeScript
61 lines
1.8 KiB
TypeScript
import { IncomingMessage } from 'http';
|
|
import KoaRouter from 'koa-router';
|
|
import { pathToRegexp } from 'path-to-regexp';
|
|
import type WebSocket from 'ws';
|
|
import type { KoaContext } from './server';
|
|
|
|
type WebSocketCallback = (socket: WebSocket, request: IncomingMessage, ctx: KoaContext) => void;
|
|
function remove<T>(list: T[], item: T) {
|
|
const index = list.indexOf(item);
|
|
if (index >= 0) list.splice(index, 1);
|
|
}
|
|
|
|
export class WebSocketLayer {
|
|
clients = new Set<WebSocket>();
|
|
regexp: RegExp;
|
|
|
|
constructor(path: Parameters<typeof pathToRegexp>[0], public callback?: WebSocketCallback) {
|
|
this.regexp = pathToRegexp(path);
|
|
}
|
|
|
|
accept(socket: WebSocket, request: IncomingMessage, ctx: KoaContext) {
|
|
if (!this.regexp.test(new URL(request.url, `http://${request.headers.host}`).pathname)) return false;
|
|
this.clients.add(socket);
|
|
socket.on('close', () => {
|
|
this.clients.delete(socket);
|
|
});
|
|
this.callback?.(socket, request, ctx);
|
|
return true;
|
|
}
|
|
|
|
close() {
|
|
for (const socket of this.clients) {
|
|
socket.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
export class Router extends KoaRouter {
|
|
wsStack: WebSocketLayer[] = [];
|
|
disposeLastOp = () => null;
|
|
|
|
/**
|
|
* hack into router methods to make sure that koa middlewares are disposable
|
|
*/
|
|
register(...args: Parameters<KoaRouter['register']>) {
|
|
const layer = super.register(...args);
|
|
this.disposeLastOp = () => remove(this.stack, layer);
|
|
return layer;
|
|
}
|
|
|
|
ws(path: Parameters<typeof pathToRegexp>[0], callback?: WebSocketCallback) {
|
|
const layer = new WebSocketLayer(path, callback);
|
|
this.wsStack.push(layer);
|
|
this.disposeLastOp = () => {
|
|
layer.close();
|
|
remove(this.wsStack, layer);
|
|
};
|
|
return layer;
|
|
}
|
|
}
|