UNPKG

nerdbank-streams

Version:
307 lines 13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MultiplexingStreamV3Formatter = exports.MultiplexingStreamV2Formatter = exports.MultiplexingStreamV1Formatter = exports.MultiplexingStreamFormatter = void 0; const MultiplexingStream_1 = require("./MultiplexingStream"); const Utilities_1 = require("./Utilities"); const msgpack = require("msgpack-lite"); const Deferred_1 = require("./Deferred"); const FrameHeader_1 = require("./FrameHeader"); const QualifiedChannelId_1 = require("./QualifiedChannelId"); class MultiplexingStreamFormatter { static getIsOddRandomData() { const size = 16; const buffer = Buffer.alloc(size); for (let i = 0; i < size; i++) { buffer[i] = Math.floor(Math.random() * 256); } return buffer; } static isOdd(localRandom, remoteRandom) { let isOdd; for (let i = 0; i < localRandom.length; i++) { const sent = localRandom[i]; const recv = remoteRandom[i]; if (sent > recv) { isOdd = true; break; } else if (sent < recv) { isOdd = false; break; } } if (isOdd === undefined) { throw new Error('Unable to determine even/odd party.'); } return isOdd; } createFrameHeader(code, id) { if (!id) { return new FrameHeader_1.FrameHeader(code); } const channelIsOdd = id % 2 === 1; // Remember that this is from the remote sender's point of view. const source = channelIsOdd === this.isOdd ? QualifiedChannelId_1.ChannelSource.Remote : QualifiedChannelId_1.ChannelSource.Local; return new FrameHeader_1.FrameHeader(code, { id, source }); } } exports.MultiplexingStreamFormatter = MultiplexingStreamFormatter; // tslint:disable-next-line: max-classes-per-file class MultiplexingStreamV1Formatter extends MultiplexingStreamFormatter { constructor(stream) { super(); this.stream = stream; } end() { this.stream.end(); } async writeHandshakeAsync() { const randomSendBuffer = MultiplexingStreamFormatter.getIsOddRandomData(); const sendBuffer = Buffer.concat([MultiplexingStreamV1Formatter.protocolMagicNumber, randomSendBuffer]); await (0, Utilities_1.writeAsync)(this.stream, sendBuffer); return randomSendBuffer; } async readHandshakeAsync(writeHandshakeResult, cancellationToken) { const localRandomBuffer = writeHandshakeResult; const recvBuffer = await (0, Utilities_1.getBufferFrom)(this.stream, MultiplexingStreamV1Formatter.protocolMagicNumber.length + 16, false, cancellationToken); for (let i = 0; i < MultiplexingStreamV1Formatter.protocolMagicNumber.length; i++) { const expected = MultiplexingStreamV1Formatter.protocolMagicNumber[i]; const actual = recvBuffer.readUInt8(i); if (expected !== actual) { throw new Error(`Protocol magic number mismatch. Expected ${expected} but was ${actual}.`); } } const isOdd = MultiplexingStreamFormatter.isOdd(localRandomBuffer, recvBuffer.slice(MultiplexingStreamV1Formatter.protocolMagicNumber.length)); return { isOdd, protocolVersion: { major: 1, minor: 0 } }; } async writeFrameAsync(header, payload) { var _a; const headerBuffer = Buffer.alloc(7); headerBuffer.writeInt8(header.code, 0); headerBuffer.writeUInt32BE(((_a = header.channel) === null || _a === void 0 ? void 0 : _a.id) || 0, 1); headerBuffer.writeUInt16BE((payload === null || payload === void 0 ? void 0 : payload.length) || 0, 5); await (0, Utilities_1.writeAsync)(this.stream, headerBuffer); if (payload && payload.length > 0) { await (0, Utilities_1.writeAsync)(this.stream, payload); } } async readFrameAsync(cancellationToken) { if (this.isOdd === undefined) { throw new Error('isOdd must be set first.'); } const headerBuffer = await (0, Utilities_1.getBufferFrom)(this.stream, 7, true, cancellationToken); if (headerBuffer === null) { return null; } const header = this.createFrameHeader(headerBuffer.readInt8(0), headerBuffer.readUInt32BE(1)); const payloadLength = headerBuffer.readUInt16BE(5); const payload = await (0, Utilities_1.getBufferFrom)(this.stream, payloadLength); return { header, payload }; } serializeOfferParameters(offer) { const payload = Buffer.from(offer.name, MultiplexingStream_1.MultiplexingStream.ControlFrameEncoding); if (payload.length > MultiplexingStream_1.MultiplexingStream.framePayloadMaxLength) { throw new Error('Name is too long.'); } return payload; } deserializeOfferParameters(payload) { return { name: payload.toString(MultiplexingStream_1.MultiplexingStream.ControlFrameEncoding), }; } serializeAcceptanceParameters(_) { return Buffer.from([]); } deserializeAcceptanceParameters(_) { return {}; } serializeContentProcessed(bytesProcessed) { throw new Error('Not supported in the V1 protocol.'); } deserializeContentProcessed(payload) { throw new Error('Not supported in the V1 protocol.'); } } exports.MultiplexingStreamV1Formatter = MultiplexingStreamV1Formatter; /** * The magic number to send at the start of communication when using v1 of the protocol. */ MultiplexingStreamV1Formatter.protocolMagicNumber = Buffer.from([0x2f, 0xdf, 0x1d, 0x50]); // tslint:disable-next-line: max-classes-per-file class MultiplexingStreamV2Formatter extends MultiplexingStreamFormatter { constructor(stream) { super(); this.reader = msgpack.createDecodeStream(); stream.pipe(this.reader); this.writer = stream; } end() { this.writer.end(); } async writeHandshakeAsync() { const randomData = MultiplexingStreamFormatter.getIsOddRandomData(); const msgpackObject = [[MultiplexingStreamV2Formatter.protocolVersion.major, MultiplexingStreamV2Formatter.protocolVersion.minor], randomData]; await (0, Utilities_1.writeAsync)(this.writer, msgpack.encode(msgpackObject)); return randomData; } async readHandshakeAsync(writeHandshakeResult, cancellationToken) { if (!writeHandshakeResult) { throw new Error('Provide the result of writeHandshakeAsync as a first argument.'); } const handshake = await this.readMessagePackAsync(cancellationToken); if (handshake === null) { throw new Error('No data received during handshake.'); } return { isOdd: MultiplexingStreamFormatter.isOdd(writeHandshakeResult, handshake[1]), protocolVersion: { major: handshake[0][0], minor: handshake[0][1] }, }; } async writeFrameAsync(header, payload) { var _a; const msgpackObject = [header.code]; if ((_a = header.channel) === null || _a === void 0 ? void 0 : _a.id) { msgpackObject.push(header.channel.id); if (payload && payload.length > 0) { msgpackObject.push(payload); } } else if (payload && payload.length > 0) { throw new Error('A frame may not contain payload without a channel ID.'); } await (0, Utilities_1.writeAsync)(this.writer, msgpack.encode(msgpackObject)); } async readFrameAsync(cancellationToken) { if (this.isOdd === undefined) { throw new Error('isOdd must be set first.'); } const msgpackObject = (await this.readMessagePackAsync(cancellationToken)); if (msgpackObject === null) { return null; } const header = this.createFrameHeader(msgpackObject[0], msgpackObject[1]); return { header, payload: msgpackObject[2] || Buffer.from([]), }; } serializeOfferParameters(offer) { const payload = [offer.name]; if (offer.remoteWindowSize) { payload.push(offer.remoteWindowSize); } return msgpack.encode(payload); } deserializeOfferParameters(payload) { const msgpackObject = msgpack.decode(payload); return { name: msgpackObject[0], remoteWindowSize: msgpackObject[1], }; } serializeAcceptanceParameters(acceptance) { const payload = []; if (acceptance.remoteWindowSize) { payload.push(acceptance.remoteWindowSize); } return msgpack.encode(payload); } deserializeAcceptanceParameters(payload) { const msgpackObject = msgpack.decode(payload); return { remoteWindowSize: msgpackObject[0], }; } serializeContentProcessed(bytesProcessed) { return msgpack.encode([bytesProcessed]); } deserializeContentProcessed(payload) { return msgpack.decode(payload)[0]; } serializeException(error) { // If the error doesn't exist then return an empty buffer. if (!error) { return Buffer.alloc(0); } const errorMsg = `${error.name}: ${error.message}`; const payload = [errorMsg]; return msgpack.encode(payload); } deserializeException(payload) { // If the payload is empty then return null. if (payload.length === 0) { return null; } // Make sure that the message pack object contains a message const msgpackObject = msgpack.decode(payload); if (!msgpackObject || msgpackObject.length === 0) { return null; } // Get error message and return the error to the remote side let errorMsg = msgpack.decode(payload)[0]; errorMsg = `Received error from remote side: ${errorMsg}`; return new Error(errorMsg); } async readMessagePackAsync(cancellationToken) { const streamEnded = new Deferred_1.Deferred(); while (true) { const readObject = this.reader.read(); if (readObject === null) { const bytesAvailable = new Deferred_1.Deferred(); const bytesAvailableCallback = bytesAvailable.resolve.bind(bytesAvailable); const streamEndedCallback = streamEnded.resolve.bind(streamEnded); this.reader.once('readable', bytesAvailableCallback); this.reader.once('end', streamEndedCallback); const endPromise = Promise.race([bytesAvailable.promise, streamEnded.promise]); await (cancellationToken ? cancellationToken.racePromise(endPromise) : endPromise); this.reader.removeListener('readable', bytesAvailableCallback); this.reader.removeListener('end', streamEndedCallback); if (bytesAvailable.isCompleted) { continue; } return null; } return readObject; } } } exports.MultiplexingStreamV2Formatter = MultiplexingStreamV2Formatter; MultiplexingStreamV2Formatter.protocolVersion = { major: 2, minor: 0 }; // tslint:disable-next-line: max-classes-per-file class MultiplexingStreamV3Formatter extends MultiplexingStreamV2Formatter { writeHandshakeAsync() { return Promise.resolve(null); } readHandshakeAsync() { return Promise.resolve({ protocolVersion: MultiplexingStreamV3Formatter.protocolV3Version }); } async writeFrameAsync(header, payload) { const msgpackObject = [header.code]; if (header.channel) { msgpackObject.push(header.channel.id); msgpackObject.push(header.channel.source); if (payload && payload.length > 0) { msgpackObject.push(payload); } } else if (payload && payload.length > 0) { throw new Error('A frame may not contain payload without a channel ID.'); } await (0, Utilities_1.writeAsync)(this.writer, msgpack.encode(msgpackObject)); } async readFrameAsync(cancellationToken) { const msgpackObject = (await this.readMessagePackAsync(cancellationToken)); if (msgpackObject === null) { return null; } const header = new FrameHeader_1.FrameHeader(msgpackObject[0], msgpackObject.length > 1 ? { id: msgpackObject[1], source: msgpackObject[2] } : undefined); return { header, payload: msgpackObject[3] || Buffer.from([]), }; } } exports.MultiplexingStreamV3Formatter = MultiplexingStreamV3Formatter; MultiplexingStreamV3Formatter.protocolV3Version = { major: 2, minor: 0 }; //# sourceMappingURL=MultiplexingStreamFormatters.js.map