UNPKG

@fakehost/signalr

Version:

A Fake Signalr Service for faking/mocking signalr hub services for testing, prototyping, and demoing

1 lines 33.6 kB
{"version":3,"sources":["../src/index.ts","../src/FakeSignalrHub.ts","../src/ClientState.ts","../src/messageTypes.ts","../src/messagePack/BinaryMessageFormat.ts","../src/messagePack/parse.ts"],"sourcesContent":["export { FakeSignalrHub } from './FakeSignalrHub'\nexport type { ConnectionId } from '@fakehost/exchange'\n","import { HubMessage, IStreamResult, Subject } from '@microsoft/signalr'\nimport { MessagePackHubProtocol } from '@microsoft/signalr-protocol-msgpack'\nimport {\n CloseConnectionOptions,\n Connection,\n ConnectionId,\n Host,\n ExchangeEvent,\n} from '@fakehost/exchange'\nimport { Observable } from 'rxjs'\nimport { ClientState } from './ClientState'\nimport { MessageType, InboundMessage, isHandshakeMessage } from './messageTypes'\nimport { parse } from './messagePack'\n\nconst protocol = new MessagePackHubProtocol()\n\ntype AllKeys<T = object> = {\n [K in keyof T]: boolean\n}\n\ntype HubClients<T = object> = {\n All: { [K in keyof T]: T[K] }\n Others: { [K in keyof T]: T[K] }\n Caller: { [K in keyof T]: T[K] }\n Client: (clientId: ConnectionId) => { [K in keyof T]: T[K] }\n}\n\ntype ConnectionState<State = object> = {\n id: ConnectionId\n setState: <Key extends keyof State>(key: Key, value: State[Key]) => void\n getState: <Key extends keyof State>(key: Key) => State[Key] | undefined\n addEventHandler(event: 'disconnect', handler: () => void): void\n removeEventHandler(event: 'disconnect', handler: () => void): void\n}\n\ntype SignalrInstanceThis<Receiver = object, State = object> = {\n Clients: HubClients<Receiver>\n Connection: ConnectionState<State>\n}\n\nconst TERMINATING_CHAR = String.fromCharCode(30)\n\ntype ConnectionEvents = 'disconnect'\n\ntype FormatTarget<Hub extends object, Receiver = object> =\n | 'capitalize'\n | undefined\n | ((s: keyof Hub | keyof Receiver) => string)\n\nexport class FakeSignalrHub<\n Hub extends object,\n Receiver extends object = object,\n State extends object = object,\n> {\n // active client connections to this hub\n private clients = new Map<ConnectionId, ClientState<State>>()\n // methods from Hub. Not typed due to casing of methods (camelCase in ts vs PascalCase in C#)\n private handlers = new Map<string, Handler>()\n private host?: Host\n private messageProtocol = new Map<ConnectionId, 'json' | 'messagepack' | string>()\n private connectionEvents = new Map<`${ConnectionId}.${ConnectionEvents}`, Set<() => void>>()\n\n constructor(\n public readonly path: string,\n private receivers: Partial<AllKeys<Receiver>> = {},\n private format?: FormatTarget<Hub, Receiver>,\n ) {}\n\n disconnect(options?: CloseConnectionOptions) {\n this.host?.disconnect({ path: this.path, ...options })\n }\n\n setHost(host: Host) {\n this.host = host\n this.host.on('connection', this.onConnection.bind(this))\n this.host.on('disconnection', this.onDisconnection.bind(this))\n this.host.on('message', e => {\n // Message not for this hub\n if (e.connection.url.pathname !== this.path) return\n\n // Handle initial handshake\n if (!this.messageProtocol.has(e.connection.id)) {\n this.handleHandshake(e.connection, e.message)\n return\n }\n\n const messages = this.deserialize(e.connection, e.message)\n messages.forEach(message => this.onMessage.bind(this)(e.connection, message))\n })\n\n // TODO: off\n }\n\n /**\n * There can be differences in casing between the client typescript and the server handler methods in C#.\n * This method formats the target to match the casing of the server.\n * @param s\n * @returns\n */\n private formatTarget(s: keyof Hub | keyof Receiver): string {\n if (this.format === 'capitalize' && typeof s === 'string') {\n return capitalize(s)\n } else if (typeof this.format === 'function') {\n return this.format(s as keyof (Hub | Receiver))\n } else {\n return s as string\n }\n }\n\n private onConnection({ connection }: ExchangeEvent<'connection'>) {\n if (connection.url.pathname !== this.path) return\n this.clients.set(connection.id, new ClientState(connection))\n }\n\n private onDisconnection({ connection }: ExchangeEvent<'disconnection'>) {\n if (connection.url.pathname !== this.path) return\n this.clients.get(connection.id)?.dispose()\n this.clients.delete(connection.id)\n\n const handlers = this.connectionEvents.get(`${connection.id}.disconnect`)\n handlers?.forEach(handler => handler())\n this.connectionEvents.delete(`${connection.id}.disconnect`)\n }\n\n private handleHandshake(connection: Connection, message: string | Buffer) {\n const [parsed] = message\n .toString()\n .split(TERMINATING_CHAR)\n .filter(Boolean)\n .map(m => JSON.parse(m))\n\n if (!isHandshakeMessage(parsed)) {\n console.error('Expected initial handshake message, but none was received.')\n connection.close({ code: 1002, reason: 'No handshake supplied' })\n return\n }\n\n this.messageProtocol.set(connection.id, parsed.protocol)\n connection.write(JSON.stringify({ type: 0 }) + TERMINATING_CHAR)\n }\n\n private serialize(connection: Connection, message: unknown) {\n switch (this.messageProtocol.get(connection.id)) {\n case 'json':\n return JSON.stringify(message) + TERMINATING_CHAR\n case 'messagepack':\n return protocol.writeMessage(message as HubMessage) as Buffer\n default:\n throw new Error('Unknown connection mode')\n }\n }\n\n private deserialize(\n connection: Connection,\n message: string | Buffer,\n ): Array<InboundMessage<Hub>> {\n switch (this.messageProtocol.get(connection.id)) {\n case 'json': {\n return message\n .toString()\n .split(TERMINATING_CHAR)\n .filter(Boolean)\n .map(m => JSON.parse(m))\n }\n case 'messagepack': {\n return parse(message as Buffer)\n }\n default:\n throw new Error('Unknown connection mode')\n }\n }\n\n private async onMessage(connection: Connection, message: InboundMessage<Hub>) {\n const connectionId = connection.id as ConnectionId\n const client = this.clients.get(connectionId)\n if (!client) return\n\n switch (message.type) {\n case MessageType.Invocation: {\n const handler = this.handlers.get(message.target as string)\n if (message.streamIds) {\n // starting a stream from the client -> service\n message.streamIds.forEach(async streamId => {\n const subject = new Subject<unknown>()\n client.subjects.set(streamId, subject)\n await handler?.apply(this.getSignalrInstance(connectionId), [subject])\n })\n return\n }\n try {\n const result = await handler?.apply(\n this.getSignalrInstance(connectionId),\n message.arguments ?? [],\n )\n return connection.write(\n this.serialize(connection, {\n type: MessageType.Completion,\n invocationId: message.invocationId,\n result,\n }),\n )\n } catch (error: unknown) {\n return connection.write(\n this.serialize(connection, {\n type: MessageType.Completion,\n invocationId: message.invocationId,\n error: stringifyError(error),\n }),\n )\n }\n }\n case MessageType.StreamInvocation: {\n const handler = this.handlers.get(message.target as string)\n const result: IStreamResult<unknown> | Observable<unknown> = await handler?.apply(\n this.getSignalrInstance(connectionId),\n message.arguments ?? [],\n )\n const subscription = result.subscribe({\n next: value => {\n connection.write(\n this.serialize(connection, {\n type: MessageType.StreamItem,\n invocationId: message.invocationId,\n item: value,\n }),\n )\n },\n error: error => {\n connection.write(\n this.serialize(connection, {\n type: MessageType.Completion,\n invocationId: message.invocationId,\n error: stringifyError(error),\n }),\n )\n },\n complete: () => {\n connection.write(\n this.serialize(connection, {\n type: MessageType.Completion,\n invocationId: message.invocationId,\n }),\n )\n },\n })\n client.subscriptions.set(message.invocationId, subscription)\n return\n }\n case MessageType.StreamItem: {\n const subject = client.subjects.get(message.invocationId)\n return subject?.next(message.item)\n }\n case MessageType.CancelInvocation: {\n client.cleanup(message.invocationId)\n connection.write(\n this.serialize(connection, {\n type: MessageType.Completion,\n invocationId: message.invocationId,\n }),\n )\n return\n }\n case MessageType.Completion: {\n client.cleanup(message.invocationId)\n return\n }\n case MessageType.Ping:\n return connection.write(this.serialize(connection, { type: MessageType.Ping }))\n default:\n console.warn('Not handled', message)\n }\n }\n\n private getSignalrInstance(\n currentConnectionId: ConnectionId,\n ): SignalrInstanceThis<Receiver, State> {\n const client = this.clients.get(currentConnectionId)\n if (!client) {\n throw new Error('Excepted a client but there was none')\n }\n\n const createClientSender = (\n predicate: (connectionId: ConnectionId) => boolean,\n ): Receiver => {\n const result = Object.keys(this.receivers).reduce((acc, target) => {\n acc[target] = (...args: unknown[]) => {\n Array.from(this.clients.entries())\n .filter(([connId]) => predicate(connId as ConnectionId))\n .forEach(([, client]) => {\n client.connection.write(\n this.serialize(client.connection, {\n type: MessageType.Invocation,\n target: this.formatTarget(target as keyof Receiver),\n arguments: args,\n }),\n )\n })\n }\n return acc\n }, {} as Record<string, (...args: unknown[]) => void>)\n return result as Receiver\n }\n\n const signalrThis: SignalrInstanceThis<Receiver, State> = {\n Connection: {\n get id() {\n return client.connection.id\n },\n setState: (key, value) => {\n client.setState(key, value)\n },\n getState: key => {\n return client.state[key]\n },\n addEventHandler: (eventName, handler) => {\n const handlers =\n this.connectionEvents.get(`${currentConnectionId}.${eventName}`) ||\n new Set()\n handlers.add(handler)\n this.connectionEvents.set(`${currentConnectionId}.${eventName}`, handlers)\n },\n removeEventHandler: (eventName, handler) => {\n const handlers =\n this.connectionEvents.get(`${currentConnectionId}.${eventName}`) ||\n new Set()\n handlers.delete(handler)\n this.connectionEvents.set(`${currentConnectionId}.${eventName}`, handlers)\n },\n },\n Clients: {\n get All() {\n return createClientSender(() => true)\n },\n get Others() {\n return createClientSender(id => id !== client.connection.id)\n },\n get Caller() {\n return createClientSender(id => id === client.connection.id)\n },\n Client: (clientId: ConnectionId) => {\n return createClientSender(id => id === clientId)\n },\n },\n }\n return signalrThis\n }\n\n get thisInstance(): SignalrInstanceThis<Receiver, State> {\n throw new Error('Not callable. Used only for type inference.')\n }\n\n register<Target extends keyof Hub>(target: Target, handler: Handler) {\n this.handlers.set(this.formatTarget(target), handler)\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Handler = (...args: any[]) => any\n\nconst capitalize = (key: string) => key.slice(0, 1).toUpperCase() + key.slice(1)\n\nconst stringifyError = (e: unknown) => {\n if (typeof e === 'string') return e\n if (e instanceof Error) return e.message\n return JSON.stringify(e)\n}\n","import { Connection } from '@fakehost/exchange'\nimport { ISubscription, Subject } from '@microsoft/signalr'\nimport { Subscription } from 'rxjs'\nimport { InvocationId } from './messageTypes'\n\nexport class ClientState<T = object> {\n public readonly state = {} as T\n // streams service -> client\n public readonly subscriptions = new Map<InvocationId, ISubscription<unknown> | Subscription>()\n // streams client -> service\n public readonly subjects = new Map<InvocationId, Subject<unknown>>()\n\n constructor(public readonly connection: Connection) {}\n\n private unsubscribe(id: InvocationId) {\n const subscription = this.subscriptions.get(id)\n if (!subscription) return\n if ('unsubscribe' in subscription) {\n subscription.unsubscribe()\n } else {\n subscription.dispose()\n }\n }\n\n cleanup(id: InvocationId) {\n const subject = this.subjects.get(id)\n if (subject) {\n subject.complete()\n this.subjects.delete(id)\n }\n const subscription = this.subscriptions.get(id)\n if (subscription) {\n this.unsubscribe(id)\n this.subscriptions.delete(id)\n }\n }\n\n dispose() {\n Array.from(this.subscriptions.keys()).forEach(id => this.unsubscribe(id))\n this.subjects.forEach(subject => subject.complete())\n }\n\n setState(key: keyof T, value: T[keyof T]) {\n this.state[key] = value\n }\n}\n","export enum MessageType {\n Invocation = 1,\n StreamItem = 2,\n Completion = 3,\n StreamInvocation = 4,\n CancelInvocation = 5,\n Ping = 6,\n Close = 7,\n}\n\nexport type InvocationId = string & { __invocationId: never }\n\nexport type HandshakeMessage = {\n protocol: string\n version: number\n}\n\nexport type InvocationMessage<Hub> = {\n type: MessageType.Invocation\n target: keyof Hub\n invocationId: InvocationId\n arguments?: unknown[]\n streamIds?: InvocationId[]\n}\n\nexport type StreamItemMessage = {\n type: MessageType.StreamItem\n item?: unknown\n invocationId: InvocationId\n}\n\nexport type Ping = {\n type: MessageType.Ping\n}\n\nexport type CloseMessage = {\n type: MessageType.Close\n error?: string\n allowReconnect?: boolean\n}\n\nexport type StreamInvocation<Hub> = {\n type: MessageType.StreamInvocation\n target: keyof Hub\n invocationId: InvocationId\n arguments?: unknown[]\n}\n\nexport type CancelInvocation = {\n type: MessageType.CancelInvocation\n invocationId: InvocationId\n}\n\nexport type CompleteInvocation = {\n type: MessageType.Completion\n error?: string\n invocationId: InvocationId\n result?: unknown\n}\n\nexport type InboundMessage<Hub> =\n | CompleteInvocation\n | InvocationMessage<Hub>\n | StreamInvocation<Hub>\n | StreamItemMessage\n | CancelInvocation\n | Ping\n | CloseMessage\n\nexport const isHandshakeMessage = (\n message: unknown | HandshakeMessage,\n): message is HandshakeMessage => {\n return (message as HandshakeMessage).protocol !== undefined\n}\n","export class BinaryMessageFormat {\n // The length prefix of binary messages is encoded as VarInt. Read the comment in\n // the BinaryMessageParser.TryParseMessage for details.\n\n public static write(output: Uint8Array): ArrayBuffer {\n let size = output.byteLength || output.length\n const lenBuffer = []\n do {\n let sizePart = size & 0x7f\n size = size >> 7\n if (size > 0) {\n sizePart |= 0x80\n }\n lenBuffer.push(sizePart)\n } while (size > 0)\n\n size = output.byteLength || output.length\n\n const buffer = new Uint8Array(lenBuffer.length + size)\n buffer.set(lenBuffer, 0)\n buffer.set(output, lenBuffer.length)\n return buffer.buffer\n }\n\n public static parse(input: ArrayBuffer): Uint8Array[] {\n const result: Uint8Array[] = []\n const uint8Array = new Uint8Array(input)\n const maxLengthPrefixSize = 5\n const numBitsToShift = [0, 7, 14, 21, 28]\n\n for (let offset = 0; offset < input.byteLength; ) {\n let numBytes = 0\n let size = 0\n let byteRead\n do {\n byteRead = uint8Array[offset + numBytes]\n size = size | ((byteRead & 0x7f) << numBitsToShift[numBytes])\n numBytes++\n } while (\n numBytes < Math.min(maxLengthPrefixSize, input.byteLength - offset) &&\n (byteRead & 0x80) !== 0\n )\n\n if ((byteRead & 0x80) !== 0 && numBytes < maxLengthPrefixSize) {\n throw new Error('Cannot read message size.')\n }\n\n if (numBytes === maxLengthPrefixSize && byteRead > 7) {\n throw new Error('Messages bigger than 2GB are not supported.')\n }\n\n if (uint8Array.byteLength >= offset + numBytes + size) {\n // IE does not support .slice() so use subarray\n result.push(\n uint8Array.slice\n ? uint8Array.slice(offset + numBytes, offset + numBytes + size)\n : uint8Array.subarray(offset + numBytes, offset + numBytes + size),\n )\n } else {\n throw new Error('Incomplete message.')\n }\n\n offset = offset + numBytes + size\n }\n\n return result\n }\n}\n","import { InboundMessage, MessageType } from '../messageTypes'\nimport { BinaryMessageFormat } from './BinaryMessageFormat'\nimport { Decoder } from '@msgpack/msgpack'\n\nexport const parse = <Hub extends object>(input: Buffer): Array<InboundMessage<Hub>> => {\n const buffer = Buffer.from(input as Uint8Array)\n const arrayBuffer = buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n )\n const messages = BinaryMessageFormat.parse(arrayBuffer)\n\n const hubMessages = new Array<InboundMessage<Hub>>()\n\n for (const message of messages) {\n const parsedMessage = parseMessage(message)\n // Can be null for an unknown message. Unknown message is logged in parseMessage\n if (parsedMessage) {\n hubMessages.push(parsedMessage)\n }\n }\n\n return hubMessages\n}\n\nconst parseMessage = <Hub extends object>(input: Uint8Array): InboundMessage<Hub> | null => {\n if (input.length === 0) {\n throw new Error('Invalid payload.')\n }\n\n const decoder = new Decoder()\n\n const properties = decoder.decode(input) as any\n if (properties.length === 0 || !(properties instanceof Array)) {\n throw new Error('Invalid payload.')\n }\n\n const messageType = properties[0] as MessageType\n switch (messageType) {\n case MessageType.Invocation:\n return {\n type: MessageType.Invocation,\n arguments: properties[4],\n invocationId: properties[2],\n streamIds: properties[5],\n target: properties[3],\n }\n case MessageType.StreamItem:\n return {\n type: MessageType.StreamItem,\n invocationId: properties[2],\n item: properties[3],\n }\n case MessageType.Completion: {\n const resultKind = properties[3]\n return {\n type: MessageType.Completion,\n error: resultKind === 1 ? properties[4] : undefined,\n invocationId: properties[2],\n result: resultKind === 1 ? undefined : properties[4],\n }\n }\n case MessageType.Ping:\n return {\n type: MessageType.Ping,\n }\n case MessageType.Close:\n return {\n type: MessageType.Close,\n allowReconnect: properties.length >= 3 ? properties[2] : undefined,\n error: properties[1],\n }\n case MessageType.CancelInvocation:\n return {\n type: MessageType.CancelInvocation,\n invocationId: properties[2],\n }\n case MessageType.StreamInvocation:\n return {\n type: MessageType.StreamInvocation,\n invocationId: properties[2],\n target: properties[3],\n arguments: properties[4],\n }\n default:\n // Future protocol changes can add message types, old clients can ignore them\n console.log(\"Unknown message type '\" + messageType + \"' ignored.\")\n return null\n }\n}\n\ntype MessageHeaders = Record<string, string>\n\n// const readHeaders = (properties: any): MessageHeaders => {\n// const headers: MessageHeaders = properties[1] as MessageHeaders\n// if (typeof headers !== 'object') {\n// throw new Error('Invalid headers.')\n// }\n// return headers\n// }\n"],"mappings":"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","names":["client"]}