diff options
Diffstat (limited to 'devtools/client/netmonitor/src/components/messages/parsers/signalr')
7 files changed, 318 insertions, 0 deletions
diff --git a/devtools/client/netmonitor/src/components/messages/parsers/signalr/HandshakeProtocol.js b/devtools/client/netmonitor/src/components/messages/parsers/signalr/HandshakeProtocol.js new file mode 100644 index 0000000000..c1c64d6717 --- /dev/null +++ b/devtools/client/netmonitor/src/components/messages/parsers/signalr/HandshakeProtocol.js @@ -0,0 +1,82 @@ +/* + * A helper class for working with SignalR handshakes. + * + * Copyright (c) .NET Foundation. All rights reserved. + * + * This source code is licensed under the Apache License, Version 2.0, + * found in the LICENSE.txt file in the root directory of the library + * source tree. + * + * https://github.com/aspnet/AspNetCore + */ + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const TextMessageFormat = require("resource://devtools/client/netmonitor/src/components/messages/parsers/signalr/TextMessageFormat.js"); +const Utils = require("resource://devtools/client/netmonitor/src/components/messages/parsers/signalr/Utils.js"); +/** @private */ +class HandshakeProtocol { + // Handshake request is always JSON + writeHandshakeRequest(handshakeRequest) { + return TextMessageFormat.TextMessageFormat.write( + JSON.stringify(handshakeRequest) + ); + } + parseHandshakeResponse(data) { + let messageData; + let remainingData; + if ( + Utils.isArrayBuffer(data) || + // eslint-disable-next-line no-undef + (typeof Buffer !== "undefined" && data instanceof Buffer) + ) { + // Format is binary but still need to read JSON text from handshake response + const binaryData = new Uint8Array(data); + const separatorIndex = binaryData.indexOf( + TextMessageFormat.TextMessageFormat.RecordSeparatorCode + ); + if (separatorIndex === -1) { + throw new Error("Message is incomplete."); + } + // content before separator is handshake response + // optional content after is additional messages + const responseLength = separatorIndex + 1; + messageData = String.fromCharCode.apply( + null, + binaryData.slice(0, responseLength) + ); + remainingData = + binaryData.byteLength > responseLength + ? binaryData.slice(responseLength).buffer + : null; + } else { + const textData = data; + const separatorIndex = textData.indexOf( + TextMessageFormat.TextMessageFormat.RecordSeparator + ); + if (separatorIndex === -1) { + throw new Error("Message is incomplete."); + } + // content before separator is handshake response + // optional content after is additional messages + const responseLength = separatorIndex + 1; + messageData = textData.substring(0, responseLength); + remainingData = + textData.length > responseLength + ? textData.substring(responseLength) + : null; + } + // At this point we should have just the single handshake message + const messages = TextMessageFormat.TextMessageFormat.parse(messageData); + const response = JSON.parse(messages[0]); + if (response.type) { + throw new Error("Expected a handshake response from the server."); + } + const responseMessage = response; + // multiple messages could have arrived with handshake + // return additional data to be parsed as usual, or null if all parsed + return [remainingData, responseMessage]; + } +} +exports.HandshakeProtocol = HandshakeProtocol; diff --git a/devtools/client/netmonitor/src/components/messages/parsers/signalr/IHubProtocol.js b/devtools/client/netmonitor/src/components/messages/parsers/signalr/IHubProtocol.js new file mode 100644 index 0000000000..fec2cdbff4 --- /dev/null +++ b/devtools/client/netmonitor/src/components/messages/parsers/signalr/IHubProtocol.js @@ -0,0 +1,33 @@ +/* + * A protocol abstraction for communicating with SignalR hubs. + * + * Copyright (c) .NET Foundation. All rights reserved. + * + * This source code is licensed under the Apache License, Version 2.0, + * found in the LICENSE.txt file in the root directory of the library + * source tree. + * + * https://github.com/aspnet/AspNetCore + */ + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +/** Defines the type of a Hub Message. */ +var MessageType; +(function (_MessageType) { + /** Indicates the message is an Invocation message and implements the {@link @microsoft/signalr.InvocationMessage} interface. */ + MessageType[(MessageType.Invocation = 1)] = "Invocation"; + /** Indicates the message is a StreamItem message and implements the {@link @microsoft/signalr.StreamItemMessage} interface. */ + MessageType[(MessageType.StreamItem = 2)] = "StreamItem"; + /** Indicates the message is a Completion message and implements the {@link @microsoft/signalr.CompletionMessage} interface. */ + MessageType[(MessageType.Completion = 3)] = "Completion"; + /** Indicates the message is a Stream Invocation message and implements the {@link @microsoft/signalr.StreamInvocationMessage} interface. */ + MessageType[(MessageType.StreamInvocation = 4)] = "StreamInvocation"; + /** Indicates the message is a Cancel Invocation message and implements the {@link @microsoft/signalr.CancelInvocationMessage} interface. */ + MessageType[(MessageType.CancelInvocation = 5)] = "CancelInvocation"; + /** Indicates the message is a Ping message and implements the {@link @microsoft/signalr.PingMessage} interface. */ + MessageType[(MessageType.Ping = 6)] = "Ping"; + /** Indicates the message is a Close message and implements the {@link @microsoft/signalr.CloseMessage} interface. */ + MessageType[(MessageType.Close = 7)] = "Close"; +})((MessageType = exports.MessageType || (exports.MessageType = {}))); diff --git a/devtools/client/netmonitor/src/components/messages/parsers/signalr/JSONHubProtocol.js b/devtools/client/netmonitor/src/components/messages/parsers/signalr/JSONHubProtocol.js new file mode 100644 index 0000000000..c3648e940f --- /dev/null +++ b/devtools/client/netmonitor/src/components/messages/parsers/signalr/JSONHubProtocol.js @@ -0,0 +1,120 @@ +/* + * Implements the SignalR Hub Protocol. + * + * Copyright (c) .NET Foundation. All rights reserved. + * + * This source code is licensed under the Apache License, Version 2.0, + * found in the LICENSE.txt file in the root directory of the library + * source tree. + * + * https://github.com/aspnet/AspNetCore + */ + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const IHubProtocol = require("resource://devtools/client/netmonitor/src/components/messages/parsers/signalr/IHubProtocol.js"); +const TextMessageFormat = require("resource://devtools/client/netmonitor/src/components/messages/parsers/signalr/TextMessageFormat.js"); +/** Implements the JSON Hub Protocol. */ +class JsonHubProtocol { + /** Creates an array of {@link @microsoft/signalr.HubMessage} objects from the specified serialized representation. + * + * @param {string} input A string containing the serialized representation. + */ + parseMessages(input) { + // The interface does allow "ArrayBuffer" to be passed in, but this implementation does not. So let's throw a useful error. + if (typeof input !== "string") { + throw new Error( + "Invalid input for JSON hub protocol. Expected a string." + ); + } + if (!input) { + return []; + } + // Parse the messages + const messages = TextMessageFormat.TextMessageFormat.parse(input); + const hubMessages = []; + for (const message of messages) { + const parsedMessage = JSON.parse(message); + if (typeof parsedMessage.type !== "number") { + throw new Error("Invalid payload."); + } + switch (parsedMessage.type) { + case IHubProtocol.MessageType.Invocation: + this.isInvocationMessage(parsedMessage); + break; + case IHubProtocol.MessageType.StreamItem: + this.isStreamItemMessage(parsedMessage); + break; + case IHubProtocol.MessageType.Completion: + this.isCompletionMessage(parsedMessage); + break; + case IHubProtocol.MessageType.Ping: + // Single value, no need to validate + break; + case IHubProtocol.MessageType.Close: + // All optional values, no need to validate + break; + default: + // Future protocol changes can add message types, new kinds of messages + // will show up without having to update Firefox. + break; + } + // Map numeric message type to their textual name if it exists + parsedMessage.type = + IHubProtocol.MessageType[parsedMessage.type] || parsedMessage.type; + hubMessages.push(parsedMessage); + } + return hubMessages; + } + /** Writes the specified {@link @microsoft/signalr.HubMessage} to a string and returns it. + * + * @param {HubMessage} message The message to write. + * @returns {string} A string containing the serialized representation of the message. + */ + writeMessage(message) { + return TextMessageFormat.TextMessageFormat.write(JSON.stringify(message)); + } + isInvocationMessage(message) { + this.assertNotEmptyString( + message.target, + "Invalid payload for Invocation message." + ); + if (message.invocationId !== undefined) { + this.assertNotEmptyString( + message.invocationId, + "Invalid payload for Invocation message." + ); + } + } + isStreamItemMessage(message) { + this.assertNotEmptyString( + message.invocationId, + "Invalid payload for StreamItem message." + ); + if (message.item === undefined) { + throw new Error("Invalid payload for StreamItem message."); + } + } + isCompletionMessage(message) { + if (message.result && message.error) { + throw new Error("Invalid payload for Completion message."); + } + if (!message.result && message.error) { + this.assertNotEmptyString( + message.error, + "Invalid payload for Completion message." + ); + } + this.assertNotEmptyString( + message.invocationId, + "Invalid payload for Completion message." + ); + } + assertNotEmptyString(value, errorMessage) { + if (typeof value !== "string" || value === "") { + throw new Error(errorMessage); + } + } +} +exports.JsonHubProtocol = JsonHubProtocol; diff --git a/devtools/client/netmonitor/src/components/messages/parsers/signalr/TextMessageFormat.js b/devtools/client/netmonitor/src/components/messages/parsers/signalr/TextMessageFormat.js new file mode 100644 index 0000000000..a9ec58ee36 --- /dev/null +++ b/devtools/client/netmonitor/src/components/messages/parsers/signalr/TextMessageFormat.js @@ -0,0 +1,33 @@ +/* + * Copyright (c) .NET Foundation. All rights reserved. + * + * This source code is licensed under the Apache License, Version 2.0, + * found in the LICENSE.txt file in the root directory of the library + * source tree. + * + * https://github.com/aspnet/AspNetCore + */ + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +// Not exported from index +/** @private */ +class TextMessageFormat { + static write(output) { + return `${output}${TextMessageFormat.RecordSeparator}`; + } + static parse(input) { + if (input[input.length - 1] !== TextMessageFormat.RecordSeparator) { + throw new Error("Message is incomplete."); + } + const messages = input.split(TextMessageFormat.RecordSeparator); + messages.pop(); + return messages; + } +} +exports.TextMessageFormat = TextMessageFormat; +TextMessageFormat.RecordSeparatorCode = 0x1e; +TextMessageFormat.RecordSeparator = String.fromCharCode( + TextMessageFormat.RecordSeparatorCode +); diff --git a/devtools/client/netmonitor/src/components/messages/parsers/signalr/Utils.js b/devtools/client/netmonitor/src/components/messages/parsers/signalr/Utils.js new file mode 100644 index 0000000000..77b00daf45 --- /dev/null +++ b/devtools/client/netmonitor/src/components/messages/parsers/signalr/Utils.js @@ -0,0 +1,25 @@ +/* + * Copyright (c) .NET Foundation. All rights reserved. + * + * This source code is licensed under the Apache License, Version 2.0, + * found in the LICENSE.txt file in the root directory of the library + * source tree. + * + * https://github.com/aspnet/AspNetCore + */ + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +// Also in signalr-protocol-msgpack/Utils.ts +/** @private */ +function isArrayBuffer(val) { + return ( + val && + typeof ArrayBuffer !== "undefined" && + (val instanceof ArrayBuffer || + // Sometimes we get an ArrayBuffer that doesn't satisfy instanceof + (val.constructor && val.constructor.name === "ArrayBuffer")) + ); +} +exports.isArrayBuffer = isArrayBuffer; diff --git a/devtools/client/netmonitor/src/components/messages/parsers/signalr/index.js b/devtools/client/netmonitor/src/components/messages/parsers/signalr/index.js new file mode 100644 index 0000000000..927b0a0922 --- /dev/null +++ b/devtools/client/netmonitor/src/components/messages/parsers/signalr/index.js @@ -0,0 +1,13 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const JsonHubProtocol = require("resource://devtools/client/netmonitor/src/components/messages/parsers/signalr/JSONHubProtocol.js"); +const HandshakeProtocol = require("resource://devtools/client/netmonitor/src/components/messages/parsers/signalr/HandshakeProtocol.js"); + +module.exports = { + JsonHubProtocol: JsonHubProtocol.JsonHubProtocol, + HandshakeProtocol: HandshakeProtocol.HandshakeProtocol, +}; diff --git a/devtools/client/netmonitor/src/components/messages/parsers/signalr/moz.build b/devtools/client/netmonitor/src/components/messages/parsers/signalr/moz.build new file mode 100644 index 0000000000..48329513f2 --- /dev/null +++ b/devtools/client/netmonitor/src/components/messages/parsers/signalr/moz.build @@ -0,0 +1,12 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DevToolsModules( + "HandshakeProtocol.js", + "IHubProtocol.js", + "index.js", + "JSONHubProtocol.js", + "TextMessageFormat.js", + "Utils.js", +) |