UNPKG

rpcchannel

Version:

Easy RPC with permission controls

221 lines (220 loc) 8.75 kB
/** * @author Nathan Pennie <kb1rd@kb1rd.net> */ /** */ import EventEmitter from 'eventemitter3'; import { MultistringAddress, WildcardMultistringAddress, AddressMap } from './addrmap'; import { ChainedAccessController, AccessController, OptAccessPolicy, CanCallFunction, RequiresPermissions, PermissionedAccessCanFunction, CanCallOpts } from './accesscontrol'; import { SerializableData, SerializedData } from './serializer'; export declare const RpcFunctionAddress: unique symbol; export declare const RpcRemappedFunction: unique symbol; interface WithValidAddressKey { [RpcFunctionAddress]?: WildcardMultistringAddress; } declare type RpcResult = SerializableData | Promise<SerializableData> | AsyncGenerator<SerializableData, void, void>; /** * A destination function for Remote Procedure Calls (RPCs). * @param src The source `RpcChannel` * @param wildcards Any wildcards that were used in resolution of this function */ export interface RpcFunction extends WithValidAddressKey { (src: RpcChannel, wildcards: string[], ...args: SerializedData[]): RpcResult; [RpcRemappedFunction]?: RpcFunction; [CanCallFunction]?: PermissionedAccessCanFunction; [RequiresPermissions]?: Iterable<string>; } export declare function RpcAddress(address: WildcardMultistringAddress): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void; export declare function RemapArguments(mapping: ('pass' | 'drop' | 'expand')[], key?: string | symbol | number): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor; /** * Sent between threads/workers/tabs/domains/whatever to carry RPCs and * associated data. */ export interface RpcMessage { to: MultistringAddress; args: SerializedData[]; return_addr?: MultistringAddress; return_type?: 'promise' | 'generator'; } export declare namespace RpcMessage { const Schema: { type: string; properties: { to: { type: string; items: { type: string; }; }; args: { type: string; }; return_addr: { type: string; items: { type: string; }; }; return_type: { type: string; enum: string[]; }; }; required: string[]; }; } export declare type BaseRegisteredObject = { [key: string]: WithValidAddressKey; }; /** * Anything where RPC functions can be registered and unregistered. */ interface HandleRegistry { /** * Registers a handler for incoming data * @param address Address for the handle * @param func Function to call when triggered */ register(address: WildcardMultistringAddress, func: RpcFunction): void; /** * Unregisters a handler for incoming data * @param address Address for the handle */ unregister(address: WildcardMultistringAddress): void; /** * Registers member functions of a class (marked with the `RpcAddress` * decorator) */ registerAll(obj: BaseRegisteredObject): void; /** * Unregisters member functions of a class (marked with the `RpcAddress` * decorator). Note that if any functions were removed from the class after * it was registered, those will not be unregistered. */ unregisterAll(obj: BaseRegisteredObject): void; } /** * Where RPC handles are registered to a particular address. This can be * re-used between different `RpcChannel`s. */ export declare class RpcHandlerRegistry extends ChainedAccessController implements HandleRegistry { map: AddressMap<RpcFunction>; return_seq_id: number; constructor(); register(address: WildcardMultistringAddress, func: RpcFunction): void; unregister(address: WildcardMultistringAddress): void; registerAll(base: BaseRegisteredObject): void; unregisterAll(base: BaseRegisteredObject): void; clear(): void; nextSeqAddr(): MultistringAddress; } export interface RpcAccessor { (...args: SerializableData[]): Promise<SerializedData>; [key: string]: RpcAccessor; } /** * Thrown when a return from a function call reaches a different RpcChannel * than it was sent from. This **may** indicate a security issue due to re-used * recieve callbacks. */ export declare class InvalidChannelError extends Error { readonly name = "InvalidChannelError"; } export declare class AccessDeniedError extends Error { readonly name = "AccessDeniedError"; } export declare class ForwardedError extends Error { } interface RpcChannelOpts { /** * If a keepalive has not been received for this much time, assume that this * `RpcChannel` has been closed. */ timeout?: number; /** * The interval with which to send keep-alives. This must be shorter than the * timeout interval on the receiving end. */ keep_alive_interval?: number; /** * Waits to start the channel until a message is received. This should only * be used on the end that is started first: Such as an `iframe` parent, a * worker's host, or a tab's creator. */ await_first_msg?: boolean; } export declare enum RpcState { INACTIVE = 0, ACTIVE = 1, CLOSED = 2 } /** * A wrapper class for functions to perform remote procedure calls. */ export declare class RpcChannel extends EventEmitter implements HandleRegistry, AccessController { protected readonly c_send: (msg: RpcMessage, xfer: Transferable[]) => void; protected readonly default_policy: boolean; reg: RpcHandlerRegistry; protected readonly _opts: RpcChannelOpts; readonly _i_reg: RpcHandlerRegistry; access_controller?: AccessController; active_timeout?: number; active_keepalive?: number; protected _state: RpcState; /** * @param c_send The function to send over whatever transport is used. * @param reg The handle registry. This can be changed later. */ constructor(c_send: (msg: RpcMessage, xfer: Transferable[]) => void, default_policy?: boolean, reg?: RpcHandlerRegistry, _opts?: RpcChannelOpts); get opts(): Readonly<NonNullable<RpcChannelOpts>>; get state(): RpcState; resetTimeout(): void; sendKeepAlive(): void; resetKeepAlive(): void; setTimeout(timeout?: number, keep_alive_interval?: number): void; setAwaitFirstMsg(should: boolean): void; _stateChange(state: RpcState): void; start(): Promise<void>; close(send?: boolean): void; stop: () => void; register(address: WildcardMultistringAddress, func: RpcFunction): void; unregister(address: WildcardMultistringAddress): void; registerAll(obj: BaseRegisteredObject): void; unregisterAll(obj: BaseRegisteredObject): void; can(addr: MultistringAddress, opts: CanCallOpts): OptAccessPolicy; /** * Sends data to a particular handle. Because there is no `await` for the * other side to process this, the `send` function should be used for pushing * data only since multiple messages may be sent before the other side gets * around to processing them. * @param to Address to send data to * @param args Data to send * @param return_addr The address of the return field. This is used for full * transactions, such as function calls */ send(to: MultistringAddress, args?: SerializableData[], return_addr?: MultistringAddress, return_type?: 'promise' | 'generator'): void; /** * Calls a handle and awaits the return value. * @param to Handle to call * @param args Arguments to pass through * @returns A promise that will return when the call is completed. This will * throw an error with the message `Channel closed` if the channel is closed * before a response is received. */ call(to: MultistringAddress, args?: SerializableData[]): Promise<SerializedData>; /** * Returns an async generator. This supports only `yield`ing values: ATM, * returned values and `yield`ed arguments are not supported. **You also must * manually deallocate the generator once you're done!** Yes, manual memory * management. If you don't manually deallocate, the listeners on both ends * will remain allocated leading to memory leaks. To deallocate, call the * `return` or `throw` functions on the generator. */ generate(to: MultistringAddress, args?: SerializableData[]): AsyncGenerator<SerializedData, void, void>; get call_obj(): RpcAccessor; /** * Call this when a new message is recieved to process it. * @param val Incoming message */ receive(val: RpcMessage): void; } export {};