aws-crt
Version:
NodeJS/browser bindings to the aws-c-* libraries
998 lines • 56.6 kB
JavaScript
"use strict";
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RequestResponseClient = exports.StreamingOperationBase = void 0;
/**
*
* @packageDocumentation
* @module mqtt_request_response
* @mergeTarget
*
*/
var protocol_client_adapter = __importStar(require("./mqtt_request_response/protocol_adapter"));
var subscription_manager = __importStar(require("./mqtt_request_response/subscription_manager"));
var mqtt_request_response = __importStar(require("../common/mqtt_request_response"));
var mqtt_request_response_internal = __importStar(require("../common/mqtt_request_response_internal"));
var event_1 = require("../common/event");
var error_1 = require("./error");
var promise_1 = require("../common/promise");
var io = __importStar(require("../common/io"));
var mqtt_shared = __importStar(require("../common/mqtt_shared"));
__exportStar(require("../common/mqtt_request_response"), exports);
var OperationState;
(function (OperationState) {
/* creation -> in event loop enqueue */
OperationState[OperationState["None"] = 0] = "None";
/* in event loop queue -> non blocked response from subscription manager */
OperationState[OperationState["Queued"] = 1] = "Queued";
/* subscribing response from sub manager -> subscription success/failure event */
OperationState[OperationState["PendingSubscription"] = 2] = "PendingSubscription";
/* (request only) subscription success -> (publish failure OR correlated response received) */
OperationState[OperationState["PendingResponse"] = 3] = "PendingResponse";
/* (streaming only) subscription success -> (operation finished OR subscription ended event) */
OperationState[OperationState["Subscribed"] = 4] = "Subscribed";
/* (streaming only) (subscription failure OR subscription ended) -> operation close/terminate */
OperationState[OperationState["Terminal"] = 5] = "Terminal";
})(OperationState || (OperationState = {}));
function operationStateToString(state) {
switch (state) {
case OperationState.None:
return "None";
case OperationState.Queued:
return "Queued";
case OperationState.PendingSubscription:
return "PendingSubscription";
case OperationState.PendingResponse:
return "PendingResponse";
case OperationState.Subscribed:
return "Subscribed";
case OperationState.Terminal:
return "Terminal";
default:
return "Unknown";
}
}
var OperationType;
(function (OperationType) {
OperationType[OperationType["RequestResponse"] = 0] = "RequestResponse";
OperationType[OperationType["Streaming"] = 1] = "Streaming";
})(OperationType || (OperationType = {}));
function areClientOptionsValid(options) {
if (!options) {
return false;
}
if (!options.maxRequestResponseSubscriptions) {
return false;
}
if (!Number.isInteger(options.maxRequestResponseSubscriptions)) {
return false;
}
if (options.maxRequestResponseSubscriptions < 2) {
return false;
}
if (!options.maxStreamingSubscriptions) {
return false;
}
if (!Number.isInteger(options.maxStreamingSubscriptions)) {
return false;
}
if (options.operationTimeoutInSeconds) {
if (!Number.isInteger(options.operationTimeoutInSeconds)) {
return false;
}
if (options.operationTimeoutInSeconds <= 0) {
return false;
}
}
return true;
}
/**
* An AWS MQTT service streaming operation. A streaming operation listens to messages on
* a particular topic, deserializes them using a service model, and emits the modeled data as Javascript events.
*/
var StreamingOperationBase = /** @class */ (function (_super) {
__extends(StreamingOperationBase, _super);
function StreamingOperationBase(options) {
var _this = _super.call(this) || this;
_this.state = mqtt_request_response_internal.StreamingOperationState.None;
_this.internalOptions = options;
return _this;
}
/**
* Triggers the streaming operation to start listening to the configured stream of events. Has no effect on an
* already-open operation. It is an error to attempt to re-open a closed streaming operation.
*/
StreamingOperationBase.prototype.open = function () {
if (this.state == mqtt_request_response_internal.StreamingOperationState.None) {
this.internalOptions.open();
this.state = mqtt_request_response_internal.StreamingOperationState.Open;
}
else if (this.state == mqtt_request_response_internal.StreamingOperationState.Closed) {
throw new error_1.CrtError("MQTT streaming operation already closed");
}
};
/**
* Stops a streaming operation from listening to the configured stream of events
*/
StreamingOperationBase.prototype.close = function () {
if (this.state != mqtt_request_response_internal.StreamingOperationState.Closed) {
this.state = mqtt_request_response_internal.StreamingOperationState.Closed;
this.internalOptions.close();
}
};
StreamingOperationBase.prototype.on = function (event, listener) {
_super.prototype.on.call(this, event, listener);
return this;
};
/**
* Event emitted when the stream's subscription status changes.
*
* Listener type: {@link SubscriptionStatusListener}
*
* @event
*/
StreamingOperationBase.SUBSCRIPTION_STATUS = 'subscriptionStatus';
/**
* Event emitted when a stream message is received
*
* Listener type: {@link IncomingPublishListener}
*
* @event
*/
StreamingOperationBase.INCOMING_PUBLISH = 'incomingPublish';
return StreamingOperationBase;
}(event_1.BufferedEventEmitter));
exports.StreamingOperationBase = StreamingOperationBase;
var StreamingOperationInternal = /** @class */ (function (_super) {
__extends(StreamingOperationInternal, _super);
function StreamingOperationInternal(options) {
return _super.call(this, options) || this;
}
StreamingOperationInternal.newInternal = function (options) {
var operation = new StreamingOperationInternal(options);
return operation;
};
StreamingOperationInternal.prototype.triggerIncomingPublishEvent = function (publishEvent) {
var _this = this;
process.nextTick(function () {
_this.emit(StreamingOperationBase.INCOMING_PUBLISH, publishEvent);
});
};
StreamingOperationInternal.prototype.triggerSubscriptionStatusUpdateEvent = function (statusEvent) {
var _this = this;
process.nextTick(function () {
_this.emit(StreamingOperationBase.SUBSCRIPTION_STATUS, statusEvent);
});
};
return StreamingOperationInternal;
}(StreamingOperationBase));
/**
* Native implementation of an MQTT-based request-response client tuned for AWS MQTT services.
*
* Supports streaming operations (listen to a stream of modeled events from an MQTT topic) and request-response
* operations (performs the subscribes, publish, and incoming publish correlation and error checking needed to
* perform simple request-response operations over MQTT).
*/
var RequestResponseClient = /** @class */ (function (_super) {
__extends(RequestResponseClient, _super);
function RequestResponseClient(protocolClientAdapter, options) {
var _this = this;
var _a;
if (!areClientOptionsValid(options)) {
throw new error_1.CrtError("Invalid client options passed to RequestResponseClient constructor");
}
_this = _super.call(this) || this;
_this.nextOperationId = 1;
_this.state = mqtt_request_response_internal.RequestResponseClientState.Ready;
_this.operations = new Map();
_this.streamingOperationsByTopicFilter = new Map(); // topic filter -> set of operation ids
_this.correlationTokenPathsByResponsePaths = new Map(); // response topic -> response path entry
_this.operationsByCorrelationToken = new Map(); // correlation token -> operation id
_this.operationQueue = new Array;
_this.operationTimeoutInSeconds = (_a = options.operationTimeoutInSeconds) !== null && _a !== void 0 ? _a : 60;
_this.protocolClientAdapter = protocolClientAdapter;
_this.protocolClientAdapter.addListener(protocol_client_adapter.ProtocolClientAdapter.PUBLISH_COMPLETION, _this.handlePublishCompletionEvent.bind(_this));
_this.protocolClientAdapter.addListener(protocol_client_adapter.ProtocolClientAdapter.CONNECTION_STATUS, _this.handleConnectionStatusEvent.bind(_this));
_this.protocolClientAdapter.addListener(protocol_client_adapter.ProtocolClientAdapter.INCOMING_PUBLISH, _this.handleIncomingPublishEvent.bind(_this));
var config = {
maxRequestResponseSubscriptions: options.maxRequestResponseSubscriptions,
maxStreamingSubscriptions: options.maxStreamingSubscriptions,
operationTimeoutInSeconds: _this.operationTimeoutInSeconds,
};
_this.subscriptionManager = new subscription_manager.SubscriptionManager(protocolClientAdapter, config);
_this.subscriptionManager.addListener(subscription_manager.SubscriptionManager.SUBSCRIBE_SUCCESS, _this.handleSubscribeSuccessEvent.bind(_this));
_this.subscriptionManager.addListener(subscription_manager.SubscriptionManager.SUBSCRIBE_FAILURE, _this.handleSubscribeFailureEvent.bind(_this));
_this.subscriptionManager.addListener(subscription_manager.SubscriptionManager.SUBSCRIPTION_ENDED, _this.handleSubscriptionEndedEvent.bind(_this));
_this.subscriptionManager.addListener(subscription_manager.SubscriptionManager.STREAMING_SUBSCRIPTION_ESTABLISHED, _this.handleStreamingSubscriptionEstablishedEvent.bind(_this));
_this.subscriptionManager.addListener(subscription_manager.SubscriptionManager.STREAMING_SUBSCRIPTION_LOST, _this.handleStreamingSubscriptionLostEvent.bind(_this));
_this.subscriptionManager.addListener(subscription_manager.SubscriptionManager.STREAMING_SUBSCRIPTION_HALTED, _this.handleStreamingSubscriptionHaltedEvent.bind(_this));
_this.subscriptionManager.addListener(subscription_manager.SubscriptionManager.SUBSCRIPTION_ORPHANED, _this.handleSubscriptionOrphanedEvent.bind(_this));
_this.subscriptionManager.addListener(subscription_manager.SubscriptionManager.UNSUBSCRIBE_COMPLETE, _this.handleUnsubscribeCompleteEvent.bind(_this));
return _this;
}
/**
* Creates a new MQTT service request-response client that uses an MQTT5 client as the protocol implementation.
*
* @param protocolClient protocol client to use for all operations
* @param options configuration options for the desired request-response client
*/
RequestResponseClient.newFromMqtt5 = function (protocolClient, options) {
if (!protocolClient) {
throw new error_1.CrtError("protocol client is null");
}
var adapter = protocol_client_adapter.ProtocolClientAdapter.newFrom5(protocolClient);
var client = new RequestResponseClient(adapter, options);
return client;
};
/**
* Creates a new MQTT service request-response client that uses an MQTT311 client as the protocol implementation.
*
* @param protocolClient protocol client to use for all operations
* @param options configuration options for the desired request-response client
*/
RequestResponseClient.newFromMqtt311 = function (protocolClient, options) {
if (!protocolClient) {
throw new error_1.CrtError("protocol client is null");
}
var adapter = protocol_client_adapter.ProtocolClientAdapter.newFrom311(protocolClient);
var client = new RequestResponseClient(adapter, options);
return client;
};
/**
* Triggers cleanup of native resources associated with the request-response client. Closing a client will fail
* all incomplete requests and close all outstanding streaming operations.
*
* This must be called when finished with a client; otherwise, native resources will leak.
*/
RequestResponseClient.prototype.close = function () {
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Closed) {
io.logInfo(RequestResponseClient.logSubject, "closing MQTT RequestResponseClient");
this.state = mqtt_request_response_internal.RequestResponseClientState.Closed;
this.closeAllOperations();
this.protocolClientAdapter.close();
this.subscriptionManager.close();
}
};
/**
* Submits a request to the request-response client.
*
* @param requestOptions description of the request to perform
*
* Returns a promise that resolves to a response to the request or an error describing how the request attempt
* failed.
*
* A "successful" request-response execution flow is defined as "the service sent a response payload that
* correlates with the request payload." Upon deserialization (which is the responsibility of the service model
* client, one layer up), such a payload may actually indicate a failure.
*/
RequestResponseClient.prototype.submitRequest = function (requestOptions) {
return __awaiter(this, void 0, void 0, function () {
var resultPromise, id, operation;
var _this = this;
return __generator(this, function (_a) {
resultPromise = (0, promise_1.newLiftedPromise)();
if (this.state == mqtt_request_response_internal.RequestResponseClientState.Closed) {
resultPromise.reject(new error_1.CrtError("MQTT request-response client has already been closed"));
return [2 /*return*/, resultPromise.promise];
}
try {
validateRequestOptions(requestOptions);
}
catch (err) {
resultPromise.reject(err);
return [2 /*return*/, resultPromise.promise];
}
id = this.nextOperationId;
this.nextOperationId++;
operation = {
id: id,
type: OperationType.RequestResponse,
state: OperationState.Queued,
pendingSubscriptionCount: requestOptions.subscriptionTopicFilters.length,
inClientTables: false,
options: requestOptions,
resultPromise: resultPromise,
};
this.operations.set(id, operation);
this.operationQueue.push(id);
setTimeout(function () {
try {
_this.completeRequestResponseOperationWithError(id, new error_1.CrtError("Operation timeout"));
}
catch (err) {
;
}
}, this.operationTimeoutInSeconds * 1000);
this.wakeServiceTask();
io.logInfo(RequestResponseClient.logSubject, "request-response operation with id \"".concat(id, "\" submitted to operation queue"));
return [2 /*return*/, resultPromise.promise];
});
});
};
/**
* Creates a new streaming operation from a set of configuration options. A streaming operation provides a
* mechanism for listening to a specific event stream from an AWS MQTT-based service.
*
* @param streamOptions configuration options for the streaming operation
*
* browser/node implementers are covariant by returning an implementation of IStreamingOperation. This split
* is necessary because event listening (which streaming operations need) cannot be modeled on an interface.
*/
RequestResponseClient.prototype.createStream = function (streamOptions) {
var _this = this;
if (this.state == mqtt_request_response_internal.RequestResponseClientState.Closed) {
throw new error_1.CrtError("MQTT request-response client has already been closed");
}
validateStreamingOptions(streamOptions);
var id = this.nextOperationId;
this.nextOperationId++;
var internalOptions = {
open: function () { _this.openStreamingOperation(id); },
close: function () { _this.closeStreamingOperation(id); },
};
var internalOperation = StreamingOperationInternal.newInternal(internalOptions);
var operation = {
id: id,
type: OperationType.Streaming,
state: OperationState.None,
pendingSubscriptionCount: 1,
inClientTables: false,
options: streamOptions,
operation: internalOperation
};
this.operations.set(id, operation);
return internalOperation;
};
RequestResponseClient.prototype.canOperationDequeue = function (operation) {
var _a;
if (operation.type != OperationType.RequestResponse) {
return true;
}
var rrOperation = operation;
var correlationToken = (_a = rrOperation.options.correlationToken) !== null && _a !== void 0 ? _a : "";
return !this.operationsByCorrelationToken.has(correlationToken);
};
RequestResponseClient.buildSuscriptionListFromOperation = function (operation) {
if (operation.type == OperationType.RequestResponse) {
var rrOperation = operation;
return rrOperation.options.subscriptionTopicFilters;
}
else {
var streamingOperation = operation;
return new Array(streamingOperation.options.subscriptionTopicFilter);
}
};
RequestResponseClient.prototype.addOperationToInProgressTables = function (operation) {
var e_1, _a;
var _b;
if (operation.type == OperationType.Streaming) {
var streamingOperation = operation;
var filter = streamingOperation.options.subscriptionTopicFilter;
var existingSet = this.streamingOperationsByTopicFilter.get(filter);
if (!existingSet) {
existingSet = new Set();
this.streamingOperationsByTopicFilter.set(filter, existingSet);
io.logDebug(RequestResponseClient.logSubject, "adding topic filter \"".concat(filter, "\" to streaming subscriptions table"));
}
existingSet.add(operation.id);
io.logDebug(RequestResponseClient.logSubject, "adding operation ".concat(operation.id, " to streaming subscriptions table under topic filter \"").concat(filter, "\""));
}
else {
var rrOperation = operation;
var correlationToken = (_b = rrOperation.options.correlationToken) !== null && _b !== void 0 ? _b : "";
this.operationsByCorrelationToken.set(correlationToken, operation.id);
io.logDebug(RequestResponseClient.logSubject, "operation ".concat(operation.id, " registered with correlation token \"").concat(correlationToken, "\""));
try {
for (var _c = __values(rrOperation.options.responsePaths), _d = _c.next(); !_d.done; _d = _c.next()) {
var path = _d.value;
var existingEntry = this.correlationTokenPathsByResponsePaths.get(path.topic);
if (!existingEntry) {
existingEntry = {
refCount: 0
};
if (path.correlationTokenJsonPath) {
existingEntry.correlationTokenPath = path.correlationTokenJsonPath.split('.');
}
this.correlationTokenPathsByResponsePaths.set(path.topic, existingEntry);
io.logDebug(RequestResponseClient.logSubject, "adding response path \"".concat(path.topic, "\" to response path table"));
}
existingEntry.refCount++;
io.logDebug(RequestResponseClient.logSubject, "operation ".concat(operation.id, " adding reference to response path \"").concat(path.topic, "\""));
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
}
finally { if (e_1) throw e_1.error; }
}
}
operation.inClientTables = true;
};
RequestResponseClient.prototype.handleAcquireSubscriptionResult = function (operation, result) {
if (result == subscription_manager.AcquireSubscriptionResult.Failure || result == subscription_manager.AcquireSubscriptionResult.NoCapacity) {
this.completeOperationWithError(operation.id, new error_1.CrtError("Acquire subscription error: ".concat(subscription_manager.acquireSubscriptionResultToString(result))));
return;
}
this.addOperationToInProgressTables(operation);
if (result == subscription_manager.AcquireSubscriptionResult.Subscribing) {
this.changeOperationState(operation, OperationState.PendingSubscription);
return;
}
if (operation.type == OperationType.Streaming) {
this.changeOperationState(operation, OperationState.Subscribed);
var streamingOperation = operation;
streamingOperation.operation.triggerSubscriptionStatusUpdateEvent({
type: mqtt_request_response.SubscriptionStatusEventType.SubscriptionEstablished
});
}
else {
this.applyRequestResponsePublish(operation);
}
};
RequestResponseClient.prototype.service = function () {
this.serviceTask = undefined;
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
return;
}
this.subscriptionManager.purge();
io.logDebug(RequestResponseClient.logSubject, "servicing operation queue with ".concat(this.operationQueue.length, " entries"));
while (this.operationQueue.length > 0) {
var headId = this.operationQueue[0];
var operation = this.operations.get(headId);
if (!operation) {
this.operationQueue.shift();
continue;
}
if (!this.canOperationDequeue(operation)) {
io.logDebug(RequestResponseClient.logSubject, "operation ".concat(headId, " cannot be dequeued"));
break;
}
var acquireOptions = {
topicFilters: RequestResponseClient.buildSuscriptionListFromOperation(operation),
operationId: headId,
type: (operation.type == OperationType.RequestResponse) ? subscription_manager.SubscriptionType.RequestResponse : subscription_manager.SubscriptionType.EventStream,
};
var acquireResult = this.subscriptionManager.acquireSubscription(acquireOptions);
io.logDebug(RequestResponseClient.logSubject, "servicing queued operation ".concat(operation.id, " yielded acquire subscription result of \"").concat(subscription_manager.acquireSubscriptionResultToString(acquireResult), "\""));
if (acquireResult == subscription_manager.AcquireSubscriptionResult.Blocked) {
break;
}
this.operationQueue.shift();
this.handleAcquireSubscriptionResult(operation, acquireResult);
}
};
RequestResponseClient.prototype.clearServiceTask = function () {
if (this.serviceTask) {
clearTimeout(this.serviceTask.serviceTask);
this.serviceTask = undefined;
}
};
RequestResponseClient.prototype.tryScheduleServiceTask = function (serviceTime) {
var _this = this;
if (this.serviceTask) {
if (serviceTime >= this.serviceTask.nextServiceTime) {
return;
}
this.clearServiceTask();
}
var futureMs = Math.max(0, Date.now() - serviceTime);
this.serviceTask = {
serviceTask: setTimeout(function () { _this.service(); }, futureMs),
nextServiceTime: serviceTime,
};
io.logDebug(RequestResponseClient.logSubject, "service task scheduled for execution in ".concat(futureMs, " MS"));
};
RequestResponseClient.prototype.wakeServiceTask = function () {
this.tryScheduleServiceTask(Date.now());
};
RequestResponseClient.prototype.closeAllOperations = function () {
var e_2, _a;
var operations = Array.from(this.operations).map(function (_a) {
var _b = __read(_a, 2), key = _b[0], value = _b[1];
return key;
});
try {
for (var operations_1 = __values(operations), operations_1_1 = operations_1.next(); !operations_1_1.done; operations_1_1 = operations_1.next()) {
var id = operations_1_1.value;
this.completeOperationWithError(id, new error_1.CrtError("Request-response client closed"));
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (operations_1_1 && !operations_1_1.done && (_a = operations_1.return)) _a.call(operations_1);
}
finally { if (e_2) throw e_2.error; }
}
};
RequestResponseClient.prototype.removeStreamingOperationFromTopicFilterSet = function (topicFilter, id) {
var operationSet = this.streamingOperationsByTopicFilter.get(topicFilter);
if (!operationSet) {
return;
}
operationSet.delete(id);
io.logDebug(RequestResponseClient.logSubject, "removed operation ".concat(id, " from streaming topic filter table entry for \"").concat(topicFilter, "\""));
if (operationSet.size > 0) {
return;
}
this.streamingOperationsByTopicFilter.delete(topicFilter);
io.logDebug(RequestResponseClient.logSubject, "removed streaming topic filter table entry for \"".concat(topicFilter, "\""));
};
RequestResponseClient.prototype.decRefResponsePaths = function (topic) {
var pathEntry = this.correlationTokenPathsByResponsePaths.get(topic);
if (!pathEntry) {
return;
}
pathEntry.refCount--;
io.logDebug(RequestResponseClient.logSubject, "dec-refing response path entry for \"".concat(topic, "\", ").concat(pathEntry.refCount, " references left"));
if (pathEntry.refCount < 1) {
io.logDebug(RequestResponseClient.logSubject, "removing response path entry for \"".concat(topic, "\""));
this.correlationTokenPathsByResponsePaths.delete(topic);
}
};
RequestResponseClient.prototype.removeRequestResponseOperation = function (operation) {
var e_3, _a;
var _b;
io.logDebug(RequestResponseClient.logSubject, "removing request-response operation ".concat(operation.id, " from client state"));
this.operations.delete(operation.id);
if (operation.inClientTables) {
try {
for (var _c = __values(operation.options.responsePaths), _d = _c.next(); !_d.done; _d = _c.next()) {
var responsePath = _d.value;
this.decRefResponsePaths(responsePath.topic);
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
}
finally { if (e_3) throw e_3.error; }
}
var correlationToken = (_b = operation.options.correlationToken) !== null && _b !== void 0 ? _b : "";
this.operationsByCorrelationToken.delete(correlationToken);
}
var releaseOptions = {
topicFilters: operation.options.subscriptionTopicFilters,
operationId: operation.id,
};
this.subscriptionManager.releaseSubscription(releaseOptions);
};
RequestResponseClient.prototype.removeStreamingOperation = function (operation) {
io.logDebug(RequestResponseClient.logSubject, "removing streaming operation ".concat(operation.id, " from client state"));
this.operations.delete(operation.id);
if (operation.inClientTables) {
this.removeStreamingOperationFromTopicFilterSet(operation.options.subscriptionTopicFilter, operation.id);
}
var releaseOptions = {
topicFilters: new Array(operation.options.subscriptionTopicFilter),
operationId: operation.id,
};
this.subscriptionManager.releaseSubscription(releaseOptions);
};
RequestResponseClient.prototype.removeOperation = function (id) {
var operation = this.operations.get(id);
if (!operation) {
return;
}
if (operation.type == OperationType.RequestResponse) {
this.removeRequestResponseOperation(operation);
}
else {
this.removeStreamingOperation(operation);
}
};
RequestResponseClient.prototype.completeRequestResponseOperationWithError = function (id, err) {
var operation = this.operations.get(id);
if (!operation) {
return;
}
io.logInfo(RequestResponseClient.logSubject, "request-response operation ".concat(id, " completed with error: \"").concat(JSON.stringify(err), "\""));
this.removeOperation(id);
if (operation.type != OperationType.RequestResponse) {
return;
}
var rrOperation = operation;
var promise = rrOperation.resultPromise;
promise.reject(err);
};
RequestResponseClient.prototype.haltStreamingOperationWithError = function (id, err) {
var operation = this.operations.get(id);
if (!operation) {
return;
}
io.logInfo(RequestResponseClient.logSubject, "streaming operation ".concat(id, " halted with error: \"").concat(JSON.stringify(err), "\""));
this.removeOperation(id);
if (operation.type != OperationType.Streaming) {
return;
}
var streamingOperation = operation;
if (operation.state != OperationState.Terminal && operation.state != OperationState.None) {
streamingOperation.operation.triggerSubscriptionStatusUpdateEvent({
type: mqtt_request_response.SubscriptionStatusEventType.SubscriptionHalted,
error: err
});
}
this.changeOperationState(operation, OperationState.Terminal);
// this is mostly a no-op except it's the only way we can guarantee that the streaming operation state also gets
// flipped to closed
streamingOperation.operation.close();
};
RequestResponseClient.prototype.completeOperationWithError = function (id, err) {
var operation = this.operations.get(id);
if (!operation) {
return;
}
if (operation.type == OperationType.RequestResponse) {
this.completeRequestResponseOperationWithError(id, err);
}
else {
this.haltStreamingOperationWithError(id, err);
}
};
RequestResponseClient.prototype.completeRequestResponseOperationWithResponse = function (id, responseTopic, payload) {
var operation = this.operations.get(id);
if (!operation) {
return;
}
io.logInfo(RequestResponseClient.logSubject, "request-response operation ".concat(id, " successfully completed with response\""));
this.removeOperation(id);
if (operation.type != OperationType.RequestResponse) {
return;
}
var rrOperation = operation;
var promise = rrOperation.resultPromise;
promise.resolve({
topic: responseTopic,
payload: payload
});
};
RequestResponseClient.prototype.handlePublishCompletionEvent = function (event) {
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
return;
}
var id = event.completionData;
if (event.err) {
this.completeRequestResponseOperationWithError(id, event.err);
}
else {
io.logDebug(RequestResponseClient.logSubject, "request-response operation ".concat(id, " successfully published request payload\""));
}
};
RequestResponseClient.prototype.handleConnectionStatusEvent = function (event) {
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
return;
}
if (event.status == protocol_client_adapter.ConnectionState.Connected && this.operationQueue.length > 0) {
this.wakeServiceTask();
}
};
RequestResponseClient.prototype.handleIncomingPublishEventStreaming = function (event, operations) {
var e_4, _a;
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
return;
}
try {
for (var operations_2 = __values(operations), operations_2_1 = operations_2.next(); !operations_2_1.done; operations_2_1 = operations_2.next()) {
var id = operations_2_1.value;
var operation = this.operations.get(id);
if (!operation) {
continue;
}
if (operation.type != OperationType.Streaming) {
continue;
}
var streamingOperation = operation;
streamingOperation.operation.triggerIncomingPublishEvent({
topic: event.topic,
payload: event.payload
});
}
}
catch (e_4_1) { e_4 = { error: e_4_1 }; }
finally {
try {
if (operations_2_1 && !operations_2_1.done && (_a = operations_2.return)) _a.call(operations_2);
}
finally { if (e_4) throw e_4.error; }
}
};
RequestResponseClient.prototype.handleIncomingPublishEventRequestResponse = function (event, responsePathEntry) {
var e_5, _a;
io.logDebug(RequestResponseClient.logSubject, "processing incoming publish event on response path topic \"".concat(event.topic, "\""));
if (!event.payload) {
io.logError(RequestResponseClient.logSubject, "incoming publish on response path topic \"".concat(event.topic, "\" has no payload"));
return;
}
try {
var correlationToken = undefined;
if (!responsePathEntry.correlationTokenPath) {
correlationToken = "";
}
else {
var payloadAsString = new TextDecoder().decode(new Uint8Array(event.payload));
var payloadAsJson = JSON.parse(payloadAsString);
var segmentValue = payloadAsJson;
try {
for (var _b = __values(responsePathEntry.correlationTokenPath), _c = _b.next(); !_c.done; _c = _b.next()) {
var segment = _c.value;
var segmentPropertyValue = segmentValue[segment];
if (!segmentPropertyValue) {
io.logError(RequestResponseClient.logSubject, "incoming publish on response path topic \"".concat(event.topic, "\" does not have a correlation token at the expected JSON path"));
break;
}
segmentValue = segmentValue[segment];
}
}
catch (e_5_1) { e_5 = { error: e_5_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_5) throw e_5.error; }
}
if (segmentValue && typeof (segmentValue) === "string") {
correlationToken = segmentValue;
}
}
if (correlationToken === undefined) {
io.logError(RequestResponseClient.logSubject, "A valid correlation token could not be inferred for incoming publish on response path topic \"".concat(event.topic, "\""));
return;
}
var id = this.operationsByCorrelationToken.get(correlationToken);
if (!id) {
io.logDebug(RequestResponseClient.logSubject, "incoming publish on response path topic \"".concat(event.topic, "\" with correlation token \"").concat(correlationToken, "\" does not have an originating request entry"));
return;
}
this.completeRequestResponseOperationWithResponse(id, event.topic, event.payload);
}
catch (err) {
io.logError(RequestResponseClient.logSubject, "incoming publish on response path topic \"".concat(event.topic, "\" triggered exception: ").concat(JSON.stringify(err)));
}
};
RequestResponseClient.prototype.handleIncomingPublishEvent = function (event) {
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
return;
}
var responsePathEntry = this.correlationTokenPathsByResponsePaths.get(event.topic);
if (responsePathEntry) {
this.handleIncomingPublishEventRequestResponse(event, responsePathEntry);
}
var streamingOperationSet = this.streamingOperationsByTopicFilter.get(event.topic);
if (streamingOperationSet) {
this.handleIncomingPublishEventStreaming(event, streamingOperationSet);
}
};
RequestResponseClient.prototype.handleSubscribeSuccessEvent = function (event) {
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
return;
}
io.logDebug(RequestResponseClient.logSubject, "subscribe success event received for operation ".concat(event.operationId, " using topic filter \"").concat(event.topicFilter, "\""));
var operation = this.operations.get(event.operationId);
if (!operation) {
return;
}
var rrOperation = operation;
rrOperation.pendingSubscriptionCount--;
if (rrOperation.pendingSubscriptionCount === 0) {
this.applyRequestResponsePublish(rrOperation);
}
else {
io.logDebug(RequestResponseClient.logSubject, "operation ".concat(event.operationId, " has ").concat(rrOperation.pendingSubscriptionCount, " pending subscriptions left"));
}
};
RequestResponseClient.prototype.handleSubscribeFailureEvent = function (event) {
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
return;
}
io.logDebug(RequestResponseClient.logSubject, "subscribe failure event received for operation ".concat(event.operationId, " using topic filter \"").concat(event.topicFilter, "\""));
this.completeRequestResponseOperationWithError(event.operationId, new error_1.CrtError("Subscribe failure"));
};
RequestResponseClient.prototype.handleSubscriptionEndedEvent = function (event) {
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
return;
}
io.logDebug(RequestResponseClient.logSubject, "subscription ended event received for operation ".concat(event.operationId, " using topic filter \"").concat(event.topicFilter, "\""));
this.completeRequestResponseOperationWithError(event.operationId, new error_1.CrtError("Subscription Ended Early"));
};
RequestResponseClient.prototype.handleStreamingSubscriptionEstablishedEvent = function (event) {
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
return;
}
var operation = this.operations.get(event.operationId);
if (!operation) {
return;
}
if (operation.state == OperationState.Terminal) {
return;
}
if (operation.type != OperationType.Streaming) {
return;
}
var streamingOperation = operation;
streamingOperation.operation.triggerSubscriptionStatusUpdateEvent({
type: mqtt_request_response.SubscriptionStatusEventType.SubscriptionEstablished
});
this.changeOperationState(operation, OperationState.Subscribed);
};
RequestResponseClient.prototype.handleStreamingSubscriptionLostEvent = function (event) {
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
return;
}
var operation = this.operations.get(event.operationId);
if (!operation) {
return;
}
if (operation.state == OperationState.Terminal) {
return;
}
if (operation.type != OperationType.Streaming) {
return;
}
var streamingOperation = operation;
streamingOperation.operation.triggerSubscriptionStatusUpdateEvent({
type: mqtt_request_response.SubscriptionStatusEventType.SubscriptionLost,
});
};
RequestResponseClient.prototype.handleStreamingSubscriptionHaltedEvent = function (event) {
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
return;
}
var operation = this.operations.get(event.operationId);
if (!operation) {
return;
}
if (operation.state == OperationState.Terminal) {
return;
}
if (operation.type != OperationType.Streaming) {
return;
}
var streamingOperation = operation;
streamingOperation.operation.triggerSubscriptionStatusUpdateEvent({
type: mqtt_request_response.SubscriptionStatusEventType.SubscriptionHalted,
error: new error_1.CrtError("Subscription Failure for topic filter \"".concat(event.topicFilter, "\""))
});
this.changeOperationState(operation, OperationState.Terminal);
};
RequestResponseClient.prototype.handleSubscriptionOrphanedEvent = function (event) {
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
return;
}
io.logDebug(RequestResponseClient.logSubject, "subscription orphaned event received for topic filter \"".concat(event.topicFilter, "\""));
this.wakeServiceTask();
};
RequestResponseClient.prototype.handleUnsubscribeCompleteEvent = function (event) {
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
return;
}
io.logDebug(RequestResponseClient.logSubject, "unsubscribe completion event received for topic filter \"".concat(event.topicFilter, "\""));
this.wakeServiceTask();
};
RequestResponseClient.prototype.changeOperationState = function (operation, state) {
if (state == operation.state) {
return;
}
io.logDebug(RequestResponseClient.logSubject, "operation ".concat(operation.id, " changing state from \"").concat(operationStateToString(operation.state), "\" to \"").concat(operationStateToString(state), "\""));
operation.state = state;
};
RequestResponseClient.prototype.applyRequestResponsePublish = function (operation) {
var publishOptions = {
topic: operation.options.publishTopic,
payload: operation.options.payload,
timeoutInSeconds: this.operationTimeoutInSeconds,
completionData: operation.id
};
try {
io.logDebug(RequestResponseClient.logSubject, "submitting publish for request-response operation ".concat(operation.id));
this.protocolClientAdapter.publish(publishOptions);
this.changeOperationState(operation, OperationStat