UNPKG

@launchdarkly/js-server-sdk-common

Version:
122 lines 6.31 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const js_sdk_common_1 = require("@launchdarkly/js-sdk-common"); const serialization_1 = require("../store/serialization"); class StreamingProcessorFDv2 { constructor(clientContext, _streamUriPath, _parameters, baseHeaders, _diagnosticsManager, _streamInitialReconnectDelay = 1) { this._streamUriPath = _streamUriPath; this._parameters = _parameters; this._diagnosticsManager = _diagnosticsManager; this._streamInitialReconnectDelay = _streamInitialReconnectDelay; const { basicConfiguration, platform } = clientContext; const { logger, serviceEndpoints } = basicConfiguration; const { requests } = platform; this._headers = Object.assign({}, baseHeaders); this._serviceEndpoints = serviceEndpoints; this._logger = logger; this._requests = requests; } _logConnectionAttempt() { this._connectionAttemptStartTime = Date.now(); } _logConnectionResult(success) { if (this._connectionAttemptStartTime && this._diagnosticsManager) { this._diagnosticsManager.recordStreamInit(this._connectionAttemptStartTime, !success, Date.now() - this._connectionAttemptStartTime); } this._connectionAttemptStartTime = undefined; } /** * This is a wrapper around the passed errorHandler which adds additional * diagnostics and logging logic. * * @param err The error to be logged and handled. * @return boolean whether to retry the connection. * * @private */ _retryAndHandleError(err, statusCallback) { var _a, _b, _c; // this is a short term error and will be removed once FDv2 adoption is sufficient. if (((_a = err.headers) === null || _a === void 0 ? void 0 : _a[`x-ld-fd-fallback`]) === `true`) { const fallbackErr = new js_sdk_common_1.LDFlagDeliveryFallbackError(js_sdk_common_1.DataSourceErrorKind.ErrorResponse, `Response header indicates to fallback to FDv1`, err.status); statusCallback(js_sdk_common_1.subsystem.DataSourceState.Closed, fallbackErr); return false; } if (!(0, js_sdk_common_1.shouldRetry)(err)) { (_b = this._logger) === null || _b === void 0 ? void 0 : _b.error((0, js_sdk_common_1.httpErrorMessage)(err, 'streaming request')); this._logConnectionResult(false); statusCallback(js_sdk_common_1.subsystem.DataSourceState.Closed, new js_sdk_common_1.LDStreamingError(js_sdk_common_1.DataSourceErrorKind.ErrorResponse, err.message, err.status, false)); return false; } (_c = this._logger) === null || _c === void 0 ? void 0 : _c.warn((0, js_sdk_common_1.httpErrorMessage)(err, 'streaming request', 'will retry')); this._logConnectionResult(false); this._logConnectionAttempt(); statusCallback(js_sdk_common_1.subsystem.DataSourceState.Interrupted); return true; } start(dataCallback, statusCallback, selectorGetter) { var _a; this._logConnectionAttempt(); statusCallback(js_sdk_common_1.subsystem.DataSourceState.Initializing); const selector = selectorGetter === null || selectorGetter === void 0 ? void 0 : selectorGetter(); const params = selector ? [...this._parameters, { key: 'basis', value: selector }] // if selector exists add basis parameter : this._parameters; // otherwise use params as is const uri = (0, js_sdk_common_1.getStreamingUri)(this._serviceEndpoints, this._streamUriPath, params); (_a = this._logger) === null || _a === void 0 ? void 0 : _a.debug(`Streaming processor opening event source to uri: ${uri}`); const eventSource = this._requests.createEventSource(uri, { headers: this._headers, errorFilter: (error) => this._retryAndHandleError(error, statusCallback), initialRetryDelayMillis: 1000 * this._streamInitialReconnectDelay, readTimeoutMillis: 5 * 60 * 1000, retryResetIntervalMillis: 60 * 1000, }); this._eventSource = eventSource; const payloadReader = new js_sdk_common_1.internal.PayloadStreamReader(eventSource, { flag: (flag) => { (0, serialization_1.processFlag)(flag); return flag; }, segment: (segment) => { (0, serialization_1.processSegment)(segment); return segment; }, }, (errorKind, message) => { statusCallback(js_sdk_common_1.subsystem.DataSourceState.Interrupted, new js_sdk_common_1.LDStreamingError(errorKind, message)); // parsing error was encountered, defensively close the data source this.stop(); }, this._logger); payloadReader.addPayloadListener((payload) => { this._logConnectionResult(true); dataCallback(payload.basis, { initMetadata: this._initMetadata, payload }); }); eventSource.onclose = () => { var _a; (_a = this._logger) === null || _a === void 0 ? void 0 : _a.info('Closed LaunchDarkly stream connection'); statusCallback(js_sdk_common_1.subsystem.DataSourceState.Closed); }; eventSource.onerror = () => { // The work is done by `errorFilter`. }; eventSource.onopen = (e) => { var _a; (_a = this._logger) === null || _a === void 0 ? void 0 : _a.info('Opened LaunchDarkly stream connection'); this._initMetadata = js_sdk_common_1.internal.initMetadataFromHeaders(e.headers); statusCallback(js_sdk_common_1.subsystem.DataSourceState.Valid); }; eventSource.onretrying = (e) => { var _a; (_a = this._logger) === null || _a === void 0 ? void 0 : _a.info(`Will retry stream connection in ${e.delayMillis} milliseconds`); }; } stop() { var _a; (_a = this._eventSource) === null || _a === void 0 ? void 0 : _a.close(); this._eventSource = undefined; } close() { this.stop(); } } exports.default = StreamingProcessorFDv2; //# sourceMappingURL=StreamingProcessorFDv2.js.map