UNPKG

tgrid

Version:

Grid Computing Framework for TypeScript

206 lines (186 loc) 6.21 kB
import { Invoke } from "../../components/Invoke"; import { AcceptorBase } from "../internal/AcceptorBase"; import { IReject } from "./internal/IReject"; import { IWorkerSystem } from "./internal/IWorkerSystem"; /** * SharedWorker acceptor for client. * * - available only in the Web Browser. * * The `SharedWorkerAcceptor` is a communicator class interacting with the * {@link SharedWorkerConnector} through RFC (Remote Function Call), created by * the {@link SharedWorkerServer} class whenever a client connects to the * `SharedWorker` instance. * * When a remote client connects to the {@link SharedWorkerServer}, * so that a new `SharedworkerAcceptor` instance being created, you can determine * whether to {@link accept} the client's connection or {@link reject not}, * reading the {@link header} property. If you've decided to accept the connection, * call the {@link accept} method with `Provider` instance. Otherwise, reject it * thorugh the {@link reject} method. * * After {@link accept accepting} the connection, don't forget to * {@link close closing} the connection after your business has been completed * to clean up the resources. Otherwise the closing must be performed by the remote * client, you can wait the remote client's closing signal by the {@link join} method. * * Also, when declaring this {@link SharedworkerAcceptor} type, you have to define three * generic arguments; `Header`, `Provider` and `Remote`. Those generic arguments must * be same with the ones defined in the {@link SharedWorkerServer} class. * * For reference, the first `Header` type represents an initial data from the * remote client after the connection. I recommend utilize it as an activation tool * for security enhancement. The second generic argument `Provider` represents a * provider from server to client, and the other `Remote` means a provider from the * remote client to server. * * @template Header Type of the header containing initial data. * @template Provider Type of features provided for the remote client. * @template Remote Type of features provided by remote client. * @author Jeongho Nam - https://github.com/samchon */ export class SharedWorkerAcceptor< Header, Provider extends object | null, Remote extends object | null, > extends AcceptorBase<Header, Provider, Remote> implements IWorkerSystem { /** * @hidden */ private port_: MessagePort; /** * @hidden */ private eraser_: () => void; /* ---------------------------------------------------------------- CONSTRUCTOR ---------------------------------------------------------------- */ /** * @internal */ public static create< Header, Provider extends object | null, Remote extends object | null, >( port: MessagePort, header: Header, eraser: () => void, ): SharedWorkerAcceptor<Header, Provider, Remote> { return new SharedWorkerAcceptor(port, header, eraser); } /** * @hidden */ private constructor(port: MessagePort, header: Header, eraser: () => void) { super(header); // ASSIGN MEMBER this.port_ = port; this.eraser_ = eraser; } /** * @inheritDoc */ public async close(): Promise<void> { // TEST CONDITION const error: Error | null = this.inspectReady("close"); if (error) throw error; // CLOSE CONNECTION this.state_ = SharedWorkerAcceptor.State.CLOSING; await this._Close(); } /** * @hidden */ private async _Close(reason?: IReject): Promise<void> { // DESTRUCTOR this.eraser_(); await this.destructor(); // DO CLOSE setTimeout(() => { this.port_.postMessage( reason === undefined ? SharedWorkerAcceptor.State.CLOSING : JSON.stringify(reason), ); this.port_.close(); }); // WELL, IT MAY HARD TO SEE SUCH PROPERTIES this.state_ = SharedWorkerAcceptor.State.CLOSED; } /* ---------------------------------------------------------------- HANDSHAKES ---------------------------------------------------------------- */ /** * @inheritDoc */ public async accept(provider: Provider): Promise<void> { // TEST CONDITION if (this.state_ !== SharedWorkerAcceptor.State.NONE) throw new Error( "Error on SharedWorkerAcceptor.accept(): you've already accepted (or rejected) the connection.", ); //---- // ACCEPT CONNECTION //---- this.state_ = SharedWorkerAcceptor.State.ACCEPTING; { // SET PROVIDER this.provider_ = provider; // PREPARE PORT this.port_.onmessage = this._Handle_message.bind(this); this.port_.start(); // INFORM ACCEPTANCE this.port_.postMessage(SharedWorkerAcceptor.State.OPEN); } this.state_ = SharedWorkerAcceptor.State.OPEN; } /** * Reject connection. * * Reject without acceptance, any interaction. The connection would be closed immediately. * * @param reason Detailed reason of the rejection. Default is "Rejected by server". */ public async reject(reason: string = "Rejected by server"): Promise<void> { // TEST CONDITION if (this.state_ !== SharedWorkerAcceptor.State.NONE) throw new Error( "Error on SharedWorkerAcceptor.reject(): you've already accepted (or rejected) the connection.", ); //---- // REJECT CONNECTION (CLOSE) //---- this.state_ = SharedWorkerAcceptor.State.REJECTING; await this._Close({ name: "reject", message: reason }); } /* ---------------------------------------------------------------- COMMUNICATOR ---------------------------------------------------------------- */ /** * @hidden */ protected async sendData(invoke: Invoke): Promise<void> { this.port_.postMessage(JSON.stringify(invoke)); } /** * @hidden */ private _Handle_message(evt: MessageEvent): void { if (evt.data === SharedWorkerAcceptor.State.CLOSING) this.close().catch(() => {}); else this.replyData(JSON.parse(evt.data)); } } /** * */ export namespace SharedWorkerAcceptor { /** * Current state of the {@link SharedWorkerAcceptor}. */ export import State = AcceptorBase.State; }