1 |
|
2 |
|
3 |
|
4 | import { CompletionMessage, HubMessage, IHubProtocol, InvocationMessage, MessageType, StreamItemMessage } from "./IHubProtocol";
|
5 | import { ILogger, LogLevel } from "./ILogger";
|
6 | import { TransferFormat } from "./ITransport";
|
7 | import { NullLogger } from "./Loggers";
|
8 | import { TextMessageFormat } from "./TextMessageFormat";
|
9 |
|
10 | const JSON_HUB_PROTOCOL_NAME: string = "json";
|
11 |
|
12 |
|
13 | export class JsonHubProtocol implements IHubProtocol {
|
14 |
|
15 |
|
16 | public readonly name: string = JSON_HUB_PROTOCOL_NAME;
|
17 |
|
18 | public readonly version: number = 1;
|
19 |
|
20 |
|
21 | public readonly transferFormat: TransferFormat = TransferFormat.Text;
|
22 |
|
23 | |
24 |
|
25 |
|
26 |
|
27 |
|
28 | public parseMessages(input: string, logger: ILogger): HubMessage[] {
|
29 |
|
30 | if (typeof input !== "string") {
|
31 | throw new Error("Invalid input for JSON hub protocol. Expected a string.");
|
32 | }
|
33 |
|
34 | if (!input) {
|
35 | return [];
|
36 | }
|
37 |
|
38 | if (logger === null) {
|
39 | logger = NullLogger.instance;
|
40 | }
|
41 |
|
42 |
|
43 | const messages = TextMessageFormat.parse(input);
|
44 |
|
45 | const hubMessages = [];
|
46 | for (const message of messages) {
|
47 | const parsedMessage = JSON.parse(message) as HubMessage;
|
48 | if (typeof parsedMessage.type !== "number") {
|
49 | throw new Error("Invalid payload.");
|
50 | }
|
51 | switch (parsedMessage.type) {
|
52 | case MessageType.Invocation:
|
53 | this.isInvocationMessage(parsedMessage);
|
54 | break;
|
55 | case MessageType.StreamItem:
|
56 | this.isStreamItemMessage(parsedMessage);
|
57 | break;
|
58 | case MessageType.Completion:
|
59 | this.isCompletionMessage(parsedMessage);
|
60 | break;
|
61 | case MessageType.Ping:
|
62 |
|
63 | break;
|
64 | case MessageType.Close:
|
65 |
|
66 | break;
|
67 | default:
|
68 |
|
69 | logger.log(LogLevel.Information, "Unknown message type '" + parsedMessage.type + "' ignored.");
|
70 | continue;
|
71 | }
|
72 | hubMessages.push(parsedMessage);
|
73 | }
|
74 |
|
75 | return hubMessages;
|
76 | }
|
77 |
|
78 | |
79 |
|
80 |
|
81 |
|
82 |
|
83 | public writeMessage(message: HubMessage): string {
|
84 | return TextMessageFormat.write(JSON.stringify(message));
|
85 | }
|
86 |
|
87 | private isInvocationMessage(message: InvocationMessage): void {
|
88 | this.assertNotEmptyString(message.target, "Invalid payload for Invocation message.");
|
89 |
|
90 | if (message.invocationId !== undefined) {
|
91 | this.assertNotEmptyString(message.invocationId, "Invalid payload for Invocation message.");
|
92 | }
|
93 | }
|
94 |
|
95 | private isStreamItemMessage(message: StreamItemMessage): void {
|
96 | this.assertNotEmptyString(message.invocationId, "Invalid payload for StreamItem message.");
|
97 |
|
98 | if (message.item === undefined) {
|
99 | throw new Error("Invalid payload for StreamItem message.");
|
100 | }
|
101 | }
|
102 |
|
103 | private isCompletionMessage(message: CompletionMessage): void {
|
104 | if (message.result && message.error) {
|
105 | throw new Error("Invalid payload for Completion message.");
|
106 | }
|
107 |
|
108 | if (!message.result && message.error) {
|
109 | this.assertNotEmptyString(message.error, "Invalid payload for Completion message.");
|
110 | }
|
111 |
|
112 | this.assertNotEmptyString(message.invocationId, "Invalid payload for Completion message.");
|
113 | }
|
114 |
|
115 | private assertNotEmptyString(value: any, errorMessage: string): void {
|
116 | if (typeof value !== "string" || value === "") {
|
117 | throw new Error(errorMessage);
|
118 | }
|
119 | }
|
120 | }
|