UNPKG

@euirim/microsoft-cognitiveservices-speech-sdk

Version:
272 lines (270 loc) 14.6 kB
"use strict"; // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. Object.defineProperty(exports, "__esModule", { value: true }); var Exports_1 = require("../common/Exports"); var HttpsProxyAgent = require("https-proxy-agent"); var ws = require("ws"); var ocsp = require("../../external/ocsp/ocsp"); var WebsocketMessageAdapter = /** @class */ (function () { function WebsocketMessageAdapter(uri, connectionId, messageFormatter, proxyInfo, headers) { var _this = this; this.open = function () { if (_this.privConnectionState === Exports_1.ConnectionState.Disconnected) { return Exports_1.PromiseHelper.fromError("Cannot open a connection that is in " + _this.privConnectionState + " state"); } if (_this.privConnectionEstablishDeferral) { return _this.privConnectionEstablishDeferral.promise(); } _this.privConnectionEstablishDeferral = new Exports_1.Deferred(); _this.privCertificateValidatedDeferral = new Exports_1.Deferred(); _this.privConnectionState = Exports_1.ConnectionState.Connecting; try { if (typeof WebSocket !== "undefined" && !WebsocketMessageAdapter.forceNpmWebSocket) { // Browser handles cert checks. _this.privCertificateValidatedDeferral.resolve(true); _this.privWebsocketClient = new WebSocket(_this.privUri); } else { if (_this.proxyInfo !== undefined && _this.proxyInfo.HostName !== undefined && _this.proxyInfo.Port > 0) { var httpProxyOptions = { host: _this.proxyInfo.HostName, port: _this.proxyInfo.Port, }; if (undefined !== _this.proxyInfo.UserName) { httpProxyOptions.headers = { "Proxy-Authentication": "Basic " + new Buffer(_this.proxyInfo.UserName + ":" + (_this.proxyInfo.Password === undefined) ? "" : _this.proxyInfo.Password).toString("base64"), "requestOCSP": "true", }; } var httpProxyAgent = new HttpsProxyAgent(httpProxyOptions); var httpsOptions_1 = { agent: httpProxyAgent, headers: _this.privHeaders }; _this.privWebsocketClient = new ws(_this.privUri, httpsOptions_1); // Register to be notified when WebSocket upgrade happens so we can check the validity of the // Certificate. _this.privWebsocketClient.addListener("upgrade", function (e) { var tlsSocket = e.socket; var peer = tlsSocket.getPeerCertificate(true); // Cork the socket until we know if the cert is good. tlsSocket.cork(); ocsp.check({ cert: peer.raw, httpOptions: httpsOptions_1, issuer: peer.issuerCertificate.raw, }, function (error, res) { if (error) { _this.privCertificateValidatedDeferral.reject(error.message); tlsSocket.destroy(error); } else { _this.privCertificateValidatedDeferral.resolve(true); tlsSocket.uncork(); } }); }); } else { // The ocsp library will handle validation for us and fail the connection if needed. _this.privCertificateValidatedDeferral.resolve(true); var ocspAgent = new ocsp.Agent({}); var options = { agent: ocspAgent, headers: _this.privHeaders }; _this.privWebsocketClient = new ws(_this.privUri, options); } } _this.privWebsocketClient.binaryType = "arraybuffer"; _this.privReceivingMessageQueue = new Exports_1.Queue(); _this.privDisconnectDeferral = new Exports_1.Deferred(); _this.privSendMessageQueue = new Exports_1.Queue(); _this.processSendQueue(); } catch (error) { _this.privConnectionEstablishDeferral.resolve(new Exports_1.ConnectionOpenResponse(500, error)); return _this.privConnectionEstablishDeferral.promise(); } _this.onEvent(new Exports_1.ConnectionStartEvent(_this.privConnectionId, _this.privUri)); _this.privWebsocketClient.onopen = function (e) { _this.privCertificateValidatedDeferral.promise().on(function () { _this.privConnectionState = Exports_1.ConnectionState.Connected; _this.onEvent(new Exports_1.ConnectionEstablishedEvent(_this.privConnectionId)); _this.privConnectionEstablishDeferral.resolve(new Exports_1.ConnectionOpenResponse(200, "")); }, function (error) { _this.privConnectionEstablishDeferral.reject(error); }); }; _this.privWebsocketClient.onerror = function (e) { // TODO: Understand what this is error is. Will we still get onClose ? if (_this.privConnectionState !== Exports_1.ConnectionState.Connecting) { // TODO: Is this required ? // this.onEvent(new ConnectionErrorEvent(errorMsg, connectionId)); } }; _this.privWebsocketClient.onclose = function (e) { if (_this.privConnectionState === Exports_1.ConnectionState.Connecting) { _this.privConnectionState = Exports_1.ConnectionState.Disconnected; // this.onEvent(new ConnectionEstablishErrorEvent(this.connectionId, e.code, e.reason)); _this.privConnectionEstablishDeferral.resolve(new Exports_1.ConnectionOpenResponse(e.code, e.reason)); } else { _this.onEvent(new Exports_1.ConnectionClosedEvent(_this.privConnectionId, e.code, e.reason)); } _this.onClose(e.code, e.reason); }; _this.privWebsocketClient.onmessage = function (e) { var networkReceivedTime = new Date().toISOString(); if (_this.privConnectionState === Exports_1.ConnectionState.Connected) { var deferred_1 = new Exports_1.Deferred(); // let id = ++this.idCounter; _this.privReceivingMessageQueue.enqueueFromPromise(deferred_1.promise()); if (e.data instanceof ArrayBuffer) { var rawMessage = new Exports_1.RawWebsocketMessage(Exports_1.MessageType.Binary, e.data); _this.privMessageFormatter .toConnectionMessage(rawMessage) .on(function (connectionMessage) { _this.onEvent(new Exports_1.ConnectionMessageReceivedEvent(_this.privConnectionId, networkReceivedTime, connectionMessage)); deferred_1.resolve(connectionMessage); }, function (error) { // TODO: Events for these ? deferred_1.reject("Invalid binary message format. Error: " + error); }); } else { var rawMessage = new Exports_1.RawWebsocketMessage(Exports_1.MessageType.Text, e.data); _this.privMessageFormatter .toConnectionMessage(rawMessage) .on(function (connectionMessage) { _this.onEvent(new Exports_1.ConnectionMessageReceivedEvent(_this.privConnectionId, networkReceivedTime, connectionMessage)); deferred_1.resolve(connectionMessage); }, function (error) { // TODO: Events for these ? deferred_1.reject("Invalid text message format. Error: " + error); }); } } }; return _this.privConnectionEstablishDeferral.promise(); }; this.send = function (message) { if (_this.privConnectionState !== Exports_1.ConnectionState.Connected) { return Exports_1.PromiseHelper.fromError("Cannot send on connection that is in " + _this.privConnectionState + " state"); } var messageSendStatusDeferral = new Exports_1.Deferred(); var messageSendDeferral = new Exports_1.Deferred(); _this.privSendMessageQueue.enqueueFromPromise(messageSendDeferral.promise()); _this.privMessageFormatter .fromConnectionMessage(message) .on(function (rawMessage) { messageSendDeferral.resolve({ Message: message, RawWebsocketMessage: rawMessage, sendStatusDeferral: messageSendStatusDeferral, }); }, function (error) { messageSendDeferral.reject("Error formatting the message. " + error); }); return messageSendStatusDeferral.promise(); }; this.read = function () { if (_this.privConnectionState !== Exports_1.ConnectionState.Connected) { return Exports_1.PromiseHelper.fromError("Cannot read on connection that is in " + _this.privConnectionState + " state"); } return _this.privReceivingMessageQueue.dequeue(); }; this.close = function (reason) { if (_this.privWebsocketClient) { if (_this.privConnectionState !== Exports_1.ConnectionState.Disconnected) { _this.privWebsocketClient.close(1000, reason ? reason : "Normal closure by client"); } } else { var deferral = new Exports_1.Deferred(); deferral.resolve(true); return deferral.promise(); } return _this.privDisconnectDeferral.promise(); }; this.sendRawMessage = function (sendItem) { try { // indicates we are draining the queue and it came with no message; if (!sendItem) { return Exports_1.PromiseHelper.fromResult(true); } _this.onEvent(new Exports_1.ConnectionMessageSentEvent(_this.privConnectionId, new Date().toISOString(), sendItem.Message)); _this.privWebsocketClient.send(sendItem.RawWebsocketMessage.payload); return Exports_1.PromiseHelper.fromResult(true); } catch (e) { return Exports_1.PromiseHelper.fromError("websocket send error: " + e); } }; this.onClose = function (code, reason) { var closeReason = "Connection closed. " + code + ": " + reason; _this.privConnectionState = Exports_1.ConnectionState.Disconnected; _this.privDisconnectDeferral.resolve(true); _this.privReceivingMessageQueue.dispose(reason); _this.privReceivingMessageQueue.drainAndDispose(function (pendingReceiveItem) { // TODO: Events for these ? // Logger.instance.onEvent(new LoggingEvent(LogType.Warning, null, `Failed to process received message. Reason: ${closeReason}, Message: ${JSON.stringify(pendingReceiveItem)}`)); }, closeReason); _this.privSendMessageQueue.drainAndDispose(function (pendingSendItem) { pendingSendItem.sendStatusDeferral.reject(closeReason); }, closeReason); }; this.processSendQueue = function () { _this.privSendMessageQueue .dequeue() .on(function (sendItem) { // indicates we are draining the queue and it came with no message; if (!sendItem) { return; } _this.sendRawMessage(sendItem) .on(function (result) { sendItem.sendStatusDeferral.resolve(result); _this.processSendQueue(); }, function (sendError) { sendItem.sendStatusDeferral.reject(sendError); _this.processSendQueue(); }); }, function (error) { // do nothing }); }; this.onEvent = function (event) { _this.privConnectionEvents.onEvent(event); Exports_1.Events.instance.onEvent(event); }; if (!uri) { throw new Exports_1.ArgumentNullError("uri"); } if (!messageFormatter) { throw new Exports_1.ArgumentNullError("messageFormatter"); } this.proxyInfo = proxyInfo; this.privConnectionEvents = new Exports_1.EventSource(); this.privConnectionId = connectionId; this.privMessageFormatter = messageFormatter; this.privConnectionState = Exports_1.ConnectionState.None; this.privUri = uri; this.privHeaders = headers; } Object.defineProperty(WebsocketMessageAdapter.prototype, "state", { get: function () { return this.privConnectionState; }, enumerable: true, configurable: true }); Object.defineProperty(WebsocketMessageAdapter.prototype, "events", { get: function () { return this.privConnectionEvents; }, enumerable: true, configurable: true }); WebsocketMessageAdapter.forceNpmWebSocket = false; return WebsocketMessageAdapter; }()); exports.WebsocketMessageAdapter = WebsocketMessageAdapter; //# sourceMappingURL=WebsocketMessageAdapter.js.map