@launchdarkly/js-server-sdk-common
Version:
LaunchDarkly Server SDK for JavaScript - common code
122 lines • 6.31 kB
JavaScript
"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