Skip to content

Commit 209cee9

Browse files
committed
framework: disable prototype for kHandler
1 parent a33826c commit 209cee9

2 files changed

Lines changed: 36 additions & 34 deletions

File tree

build/prepare.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const withoutTypes = (data) => ({
1515

1616
/** @type {import('typescript/lib/typescript').CompilerOptions} */
1717
const compilerOptionsBase = {
18-
target: 'es2020',
18+
target: 'es2022',
1919
module: 'commonjs',
2020
esModuleInterop: true,
2121
moduleResolution: 'node',

framework/framework/server.ts

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
CsrfTokenError, HydroError, InvalidOperationError,
2020
MethodNotAllowedError, NotFoundError, UserFacingError,
2121
} from './error';
22+
import type { KnownHandlers } from './interface';
2223
import { Router } from './router';
2324
import serializer from './serializer';
2425

@@ -127,7 +128,7 @@ export interface UserModel {
127128

128129
export interface HandlerCommon<C> { } // eslint-disable-line @typescript-eslint/no-unused-vars
129130
export class HandlerCommon<C> {
130-
static [kHandler]: string | boolean = true;
131+
static [kHandler]: string | boolean = 'HandlerCommon';
131132
session: Record<string, any>;
132133
args: Record<string, any>;
133134
request: HydroRequest;
@@ -198,6 +199,8 @@ export class HandlerCommon<C> {
198199
}
199200

200201
export class Handler<C = CordisContext> extends HandlerCommon<C> {
202+
static [kHandler] = 'Handler';
203+
201204
loginMethods: any;
202205
noCheckPermView = false;
203206
notUsage = false;
@@ -244,6 +247,8 @@ export class Handler<C = CordisContext> extends HandlerCommon<C> {
244247
}
245248

246249
export class ConnectionHandler<C> extends HandlerCommon<C> {
250+
static [kHandler] = 'ConnectionHandler';
251+
247252
conn: WebSocket;
248253
compression: Shorty;
249254
counter = 0;
@@ -472,7 +477,8 @@ ${c.response.status} ${endTime - startTime}ms ${c.response.length}`);
472477
const h = new HandlerClass(ctx, this.ctx);
473478
ctx.handler = h;
474479
const method = ctx.method.toLowerCase();
475-
const name = (typeof HandlerClass[kHandler] === 'string' ? HandlerClass[kHandler] : HandlerClass.name).replace(/Handler$/, '');
480+
const name = ((Object.hasOwn(HandlerClass, kHandler) && typeof HandlerClass[kHandler] === 'string')
481+
? HandlerClass[kHandler] : HandlerClass.name).replace(/Handler$/, '');
476482
try {
477483
const operation = (method === 'post' && ctx.request.body?.operation)
478484
? `_${ctx.request.body.operation}`.replace(/_([a-z])/gm, (s) => s[1].toUpperCase())
@@ -645,7 +651,8 @@ ${c.response.status} ${endTime - startTime}ms ${c.response.length}`);
645651

646652
private register(type: 'route' | 'conn', routeName: string, path: string, HandlerClass: any, ...permPrivChecker) {
647653
if (!HandlerClass?.[kHandler] || !isClass(HandlerClass)) throw new Error('Invalid registration.');
648-
const name = typeof HandlerClass[kHandler] === 'string' ? HandlerClass[kHandler] : HandlerClass.name;
654+
const name = ((Object.hasOwn(HandlerClass, kHandler) && typeof HandlerClass[kHandler] === 'string')
655+
? HandlerClass[kHandler] : HandlerClass.name).replace(/Handler$/, '');
649656
if (this.registrationCount[name] && this.registry[name] !== HandlerClass) {
650657
logger.warn('Route with name %s already exists.', name);
651658
}
@@ -746,51 +753,46 @@ ${c.response.status} ${endTime - startTime}ms ${c.response.length}`);
746753
this.captureAllRoutes[prefix] = cb;
747754
}
748755

749-
public handlerMixin(MixinClass: Partial<HandlerCommon<C>>) {
756+
private _applyMixin(Target: any, MixinClass: Partial<any>) {
757+
if (!('prototype' in Target)) throw new Error('Target must be a class.');
750758
this.ctx.effect(() => {
759+
let oldValue = null;
751760
for (const val of Object.getOwnPropertyNames(MixinClass)) {
752-
if (HandlerCommon.prototype[val]) {
753-
logger.warn('HandlerCommon.prototype[%s] already exists.', val);
761+
if (Target.prototype[val]) {
762+
logger.warn(`${Target.name}.prototype[${val}] already exists.`);
763+
oldValue = Target.prototype[val];
754764
}
755-
HandlerCommon.prototype[val] = MixinClass[val];
765+
Target.prototype[val] = MixinClass[val];
756766
}
757767
return () => {
758768
for (const val of Object.getOwnPropertyNames(MixinClass)) {
759-
delete HandlerCommon.prototype[val];
769+
if (Target.prototype[val] !== MixinClass[val]) {
770+
logger.warn(`Failed to unload mixin ${Target.name}.prototype[${val}]: not the same as the original value.`);
771+
} else {
772+
delete Target.prototype[val];
773+
if (oldValue) Target.prototype[val] = oldValue;
774+
}
760775
}
761776
};
762777
});
763778
}
764779

765-
public httpHandlerMixin(MixinClass: Partial<Handler<C>>) {
766-
this.ctx.effect(() => {
767-
for (const val of Object.getOwnPropertyNames(MixinClass)) {
768-
if (Handler.prototype[val]) {
769-
logger.warn('Handler.prototype[%s] already exists.', val);
770-
}
771-
Handler.prototype[val] = MixinClass[val];
772-
}
773-
return () => {
774-
for (const val of Object.getOwnPropertyNames(MixinClass)) {
775-
delete Handler.prototype[val];
776-
}
777-
};
780+
public applyMixin<T extends keyof KnownHandlers>(name: T, MixinClass: any) {
781+
this.withHandlerClass(name, (HandlerClass) => {
782+
this._applyMixin(HandlerClass, MixinClass);
778783
});
779784
}
780785

786+
public handlerMixin(MixinClass: Partial<HandlerCommon<C>>) {
787+
return this._applyMixin(HandlerCommon, MixinClass);
788+
}
789+
790+
public httpHandlerMixin(MixinClass: Partial<Handler<C>>) {
791+
return this._applyMixin(Handler, MixinClass);
792+
}
793+
781794
public wsHandlerMixin(MixinClass: Partial<ConnectionHandler<C>>) {
782-
this.ctx.effect(() => {
783-
for (const val of Object.getOwnPropertyNames(MixinClass)) {
784-
if (ConnectionHandler.prototype[val]) {
785-
logger.warn('ConnectionHandler.prototype[%s] already exists.', val);
786-
}
787-
}
788-
return () => {
789-
for (const val of Object.getOwnPropertyNames(MixinClass)) {
790-
delete ConnectionHandler.prototype[val];
791-
}
792-
};
793-
});
795+
return this._applyMixin(ConnectionHandler, MixinClass);
794796
}
795797

796798
public registerRenderer(name: string, func: Renderer) {

0 commit comments

Comments
 (0)