UNPKG

4.9 kBPlain TextView Raw
1// Copyright (c) .NET Foundation. All rights reserved.
2// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3
4import { CompletionMessage, HubMessage, IHubProtocol, InvocationMessage, MessageType, StreamItemMessage } from "./IHubProtocol";
5import { ILogger, LogLevel } from "./ILogger";
6import { TransferFormat } from "./ITransport";
7import { NullLogger } from "./Loggers";
8import { TextMessageFormat } from "./TextMessageFormat";
9
10const JSON_HUB_PROTOCOL_NAME: string = "json";
11
12/** Implements the JSON Hub Protocol. */
13export class JsonHubProtocol implements IHubProtocol {
14
15 /** @inheritDoc */
16 public readonly name: string = JSON_HUB_PROTOCOL_NAME;
17 /** @inheritDoc */
18 public readonly version: number = 1;
19
20 /** @inheritDoc */
21 public readonly transferFormat: TransferFormat = TransferFormat.Text;
22
23 /** Creates an array of {@link @microsoft/signalr.HubMessage} objects from the specified serialized representation.
24 *
25 * @param {string} input A string containing the serialized representation.
26 * @param {ILogger} logger A logger that will be used to log messages that occur during parsing.
27 */
28 public parseMessages(input: string, logger: ILogger): HubMessage[] {
29 // The interface does allow "ArrayBuffer" to be passed in, but this implementation does not. So let's throw a useful error.
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 // Parse the messages
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 // Single value, no need to validate
63 break;
64 case MessageType.Close:
65 // All optional values, no need to validate
66 break;
67 default:
68 // Future protocol changes can add message types, old clients can ignore them
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 /** Writes the specified {@link @microsoft/signalr.HubMessage} to a string and returns it.
79 *
80 * @param {HubMessage} message The message to write.
81 * @returns {string} A string containing the serialized representation of the message.
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}