UNPKG

tgrid

Version:

Grid Computing Framework for TypeScript

193 lines (178 loc) 6.46 kB
import { HashSet, is_node } from "tstl"; import { IHeaderWrapper } from "../internal/IHeaderWrapper"; import { IServer } from "../internal/IServer"; import { once } from "../internal/once"; import { SharedWorkerAcceptor } from "./SharedWorkerAcceptor"; /** * SharedWorker server. * * - available only in the Web Browser. * * The `SharedWorkerServer` is a class representing a server in `SharedWorker` * environment. Clients connecting to the `SharedWorkerServer` would communicate * with this server through {@link SharedWorkerAcceptor} instances using RPC * (Remote Procedure Call) concept. * * To open the server, call the {@link open} method with your callback function * which would be called whenever a {@link SharedWorkerAcceptor} has been newly * created by a new client's connection. * * Also, when declaring this `SharedWorkerServer` type, you have to define three * generic arguments; `Header`, `Provider` and `Remote`. Those generic arguments * would be propagated to the {@link SharedWorkerAcceptor}, so that * {@link SharedWorkerAcceptor} would have the same generic arguments, too. * * 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 header containing initialization data like activation. * @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 SharedWorkerServer< Header, Provider extends object | null, Remote extends object | null, > implements IServer<SharedWorkerServer.State> { /** * @hidden */ private state_: SharedWorkerServer.State; /** * @hidden */ private acceptors_: HashSet<SharedWorkerAcceptor<Header, Provider, Remote>>; /* ---------------------------------------------------------------- CONSTRUCTOR ---------------------------------------------------------------- */ /** * Default Constructor. */ public constructor() { this.acceptors_ = new HashSet(); this.state_ = SharedWorkerServer.State.NONE; } /** * Open shared worker server. * * Open a server through the shared worker protocol, with *handler* function * determining whether to accept the client's connection or not. After the server * has been opened, clients can connect to that server by using the * {@link SharedWorkerServer} class. * * When implementing the *handler* function with the {@link SharedWorkerServer} * instance, calls the {@link SharedWorkerAcceptor.accept} method if you want to * accept the new client's connection. Otherwise you don't want to accept the * client and reject its connection, just calls the * {@link SharedWorkerAcceptor.reject} instead. * * @param handler Callback function called whenever client connects. */ public async open( handler: ( acceptor: SharedWorkerAcceptor<Header, Provider, Remote>, ) => Promise<void>, ): Promise<void> { // TEST CONDITION if (is_node() === true) throw new Error( "Error on SharedWorkerServer.open(): SharedWorker is not supported in the NodeJS.", ); else if (self.document !== undefined) throw new Error( "Error on SharedWorkerServer.open(): this is not the SharedWorker.", ); else if (this.state_ !== SharedWorkerServer.State.NONE) throw new Error( "Error on SharedWorkerServer.open(): the server has been opened yet.", ); //---- // OPE SHARED-WORKER //---- this.state_ = SharedWorkerServer.State.OPENING; { self.addEventListener("connect", (evt) => { for (const port of (evt as OpenEvent).ports) this._Handle_connect(port, handler); }); } this.state_ = SharedWorkerServer.State.OPEN; } /** * Close server. * * Close all connections between its remote clients ({@link SharedWorkerConnector}s). * * It destroys all RFCs (remote function calls) between this server and remote clients * (through `Driver<Controller>`) that are not returned (completed) yet. The destruction * causes all incomplete RFCs to throw exceptions. */ public async close(): Promise<void> { // TEST VALIDATION if (this.state_ !== SharedWorkerServer.State.OPEN) throw new Error( "Error on SharedWorkerServer.close(): the server is not opened.", ); // CLOSE ALL CONNECTIONS for (const acceptor of this.acceptors_) await acceptor.close(); } /** * @hidden */ private _Handle_connect( port: MessagePort, handler: (acceptor: SharedWorkerAcceptor<Header, Provider, Remote>) => any, ): void { port.onmessage = once((evt) => { // ARGUMENTS const wrapper: IHeaderWrapper<Header> = JSON.parse(evt.data); // CREATE ACCEPTOR /* eslint-disable */ let acceptor: SharedWorkerAcceptor<Header, Provider, Remote>; acceptor = SharedWorkerAcceptor.create(port, wrapper.header, () => { this.acceptors_.erase(acceptor); }); this.acceptors_.insert(acceptor); // SHIFT TO THE CALLBACK handler(acceptor); }); port.postMessage(SharedWorkerServer.State.OPENING); } /* ---------------------------------------------------------------- ACCESSORS ---------------------------------------------------------------- */ /** * Get server state. * * Get current state of the websocket server. * * List of values are such like below: * * - `NONE`: The `{@link SharedWorkerServer} instance is newly created, but did nothing yet. * - `OPENING`: The {@link SharedWorkerServer.open} method is on running. * - `OPEN`: The websocket server is online. * - `CLOSING`: The {@link SharedWorkerServer.close} method is on running. * - `CLOSED`: The websocket server is offline. */ public get state(): SharedWorkerServer.State { return this.state_; } } /** * */ export namespace SharedWorkerServer { /** * Current state of the {@link SharedWorkerServer}. */ export import State = IServer.State; } /** * @hidden */ type OpenEvent = Event & { ports: MessagePort[] };