UNPKG

@microsoft/1ds-post-js

Version:

Microsoft Application Insights JavaScript SDK - 1ds-post-channel-js

793 lines 73.7 kB
/* * 1DS JS SDK POST plugin, 4.3.11 * Copyright (c) Microsoft and contributors. All rights reserved. * (Microsoft Internal Only) */ var _a; /** * HttpManager.ts * @author Abhilash Panwar (abpanwar); Hector Hernandez (hectorh); Nev Wylie (newylie) * @copyright Microsoft 2018-2020 */ import dynamicProto from "@microsoft/dynamicproto-js"; import { FullVersionString, SenderPostManager, _getAllResponseHeaders, _throwInternal, _warnToConsole, arrForEach, dateNow, doPerf, dumpObj, extend, getCommonSchemaMetaData, getNavigator, getResponseText, getTime, hasOwnProperty, isBeaconsSupported, isFeatureEnabled, isFetchSupported, isNullOrUndefined, isReactNative, isUndefined, isValueAssigned, objForEachKey, objKeys, onConfigChange, optimizeObject, prependTransports, strUndefined } from "@microsoft/1ds-core-js"; import { arrAppend, getInst, isFunction } from "@nevware21/ts-utils"; import { ClockSkewManager } from "./ClockSkewManager"; import { EventBatch } from "./EventBatch"; import { DEFAULT_CACHE_CONTROL, DEFAULT_CONTENT_TYPE, STR_API_KEY, STR_AUTH_XTOKEN, STR_CACHE_CONTROL, STR_CLIENT_ID, STR_CLIENT_VERSION, STR_CONTENT_TYPE_HEADER, STR_DROPPED, STR_EMPTY, STR_KILL_DURATION_HEADER, STR_KILL_TOKENS_HEADER, STR_MSA_DEVICE_TICKET, STR_MSFPC, STR_NO_RESPONSE_BODY, STR_OTHER, STR_REQUEUE, STR_RESPONSE_FAIL, STR_SENDING, STR_TIME_DELTA_HEADER, STR_TIME_DELTA_TO_APPLY, STR_UPLOAD_TIME } from "./InternalConstants"; import { KillSwitch } from "./KillSwitch"; import { retryPolicyGetMillisToBackoffForRetry, retryPolicyShouldRetryForStatus } from "./RetryPolicy"; import { Serializer } from "./Serializer"; import { createTimeoutWrapper } from "./TimeoutOverrideWrapper"; import { _DYN_ADD_NO_RESPONSE, _DYN_ALLOW_REQUEST_SENDIN0, _DYN_ALWAYS_USE_XHR_OVERR7, _DYN_AVOID_OPTIONS, _DYN_BATCHES, _DYN_CAN_SEND_REQUEST, _DYN_CLEAR_TIMEOUT_OVERRI3, _DYN_CONCAT, _DYN_COUNT, _DYN_CREATE_ONE_DSPAYLOAD, _DYN_CREATE_PAYLOAD, _DYN_DISABLE_EVENT_TIMING5, _DYN_DISABLE_FETCH_KEEP_A6, _DYN_DISABLE_XHR_SYNC, _DYN_ENABLE_COMPOUND_KEY, _DYN_EVENTS, _DYN_FETCH_CREDENTIALS, _DYN_GET_CLOCK_SKEW_HEADE2, _DYN_GET_OFFLINE_REQUEST_9, _DYN_GET_WPARAM, _DYN_HDRS, _DYN_HEADERS, _DYN_INITIALIZE, _DYN_IS_BEACON, _DYN_IS_TEARDOWN, _DYN_I_KEY, _DYN_LENGTH, _DYN_OVERRIDE_ENDPOINT_UR4, _DYN_PAYLOAD_BLOB, _DYN_PAYLOAD_PREPROCESSOR, _DYN_PUSH, _DYN_SEND_QUEUED_REQUESTS, _DYN_SEND_SYNCHRONOUS_BAT10, _DYN_SEND_TYPE, _DYN_SERIALIZE_OFFLINE_EV8, _DYN_SET_CLOCK_SKEW, _DYN_SET_KILL_SWITCH_TENA11, _DYN_SET_TIMEOUT_OVERRIDE, _DYN_SET_UNLOADING, _DYN_SHOULD_ADD_CLOCK_SKE1, _DYN_SPLIT, _DYN_TIMINGS, _DYN_TO_LOWER_CASE, _DYN_USE_HDRS, _DYN_USE_SEND_BEACON, _DYN__BACK_OFF_TRANSMISSI12, _DYN__SEND_REASON, _DYN__THE_PAYLOAD, _DYN__TRANSPORT } from "./__DynamicConstants"; var strSendAttempt = "sendAttempt"; var _noResponseQs = "&" + STR_NO_RESPONSE_BODY + "=true"; var UrlQueryString = "?cors=true&" + STR_CONTENT_TYPE_HEADER[_DYN_TO_LOWER_CASE /* @min:%2etoLowerCase */]() + "=" + DEFAULT_CONTENT_TYPE; /** * Identifies the default notification reason to the action names */ var _eventActionMap = (_a = {}, _a[1 /* EventBatchNotificationReason.Paused */] = STR_REQUEUE, _a[100 /* EventBatchNotificationReason.RequeueEvents */] = STR_REQUEUE, _a[200 /* EventBatchNotificationReason.Complete */] = "sent", _a[8004 /* EventBatchNotificationReason.KillSwitch */] = STR_DROPPED, _a[8003 /* EventBatchNotificationReason.SizeLimitExceeded */] = STR_DROPPED, _a[8006 /* EventBatchNotificationReason.BeaconSendFailure */] = STR_DROPPED, _a); var _collectorQsHeaders = {}; var _collectorHeaderToQs = {}; function _addCollectorHeaderQsMapping(qsName, headerName, allowQs) { _collectorQsHeaders[qsName] = headerName; if (allowQs !== false) { _collectorHeaderToQs[headerName] = qsName; } } _addCollectorHeaderQsMapping(STR_MSA_DEVICE_TICKET, STR_MSA_DEVICE_TICKET, false); _addCollectorHeaderQsMapping(STR_CLIENT_VERSION, STR_CLIENT_VERSION); _addCollectorHeaderQsMapping(STR_CLIENT_ID, "Client-Id"); _addCollectorHeaderQsMapping(STR_API_KEY, STR_API_KEY); _addCollectorHeaderQsMapping(STR_TIME_DELTA_TO_APPLY, STR_TIME_DELTA_TO_APPLY); _addCollectorHeaderQsMapping(STR_UPLOAD_TIME, STR_UPLOAD_TIME); _addCollectorHeaderQsMapping(STR_AUTH_XTOKEN, STR_AUTH_XTOKEN); function _hasHeader(headers, header) { var hasHeader = false; if (headers && header) { var keys = objKeys(headers); if (keys && keys[_DYN_LENGTH /* @min:%2elength */] > 0) { var lowerHeader = header[_DYN_TO_LOWER_CASE /* @min:%2etoLowerCase */](); for (var lp = 0; lp < keys[_DYN_LENGTH /* @min:%2elength */]; lp++) { var value = keys[lp]; if (value && hasOwnProperty(header, value) && value[_DYN_TO_LOWER_CASE /* @min:%2etoLowerCase */]() === lowerHeader) { hasHeader = true; break; } } } } return hasHeader; } function _addRequestDetails(details, name, value, useHeaders) { if (name && value && value[_DYN_LENGTH /* @min:%2elength */] > 0) { if (useHeaders && _collectorQsHeaders[name]) { details[_DYN_HDRS /* @min:%2ehdrs */][_collectorQsHeaders[name]] = value; details[_DYN_USE_HDRS /* @min:%2euseHdrs */] = true; } else { details.url += "&" + name + "=" + value; } } } function _addQueryStringParameter(qsParams, name, value) { for (var i = 0; i < qsParams[_DYN_LENGTH /* @min:%2elength */]; i++) { if (qsParams[i].name === name) { qsParams[i].value = value; return; } } qsParams[_DYN_PUSH /* @min:%2epush */]({ name: name, value: value }); } function _removeQueryStringParameter(qsParams, name) { for (var i = 0; i < qsParams[_DYN_LENGTH /* @min:%2elength */]; i++) { if (qsParams[i].name === name) { qsParams.splice(i, 1); return; } } } /** * Class managing the sending of requests. */ var HttpManager = /** @class */ (function () { /** * @constructor * @param requestQueue - The queue that contains the requests to be sent. */ function HttpManager(maxEventsPerBatch, maxConnections, maxRequestRetriesBeforeBackoff, actions) { // ------------------------------------------------------------------------------------------------------------------------ // Only set "Default" values in the _initDefaults() method, unless value are not "reset" during unloading // ------------------------------------------------------------------------------------------------------------------------ var _urlString; var _killSwitch; var _paused; var _clockSkewManager; var _useBeacons = false; var _outstandingRequests; // Holds the number of outstanding async requests that have not returned a response yet var _postManager; var _logger; var _sendInterfaces; var _core; var _customHttpInterface; var _queryStringParameters; var _headers; var _batchQueue; var _serializer; var _enableEventTimings; var _cookieMgr; var _isUnloading; var _useHeaders; var _xhrTimeout; var _zipPayload; var _disableXhrSync; var _disableFetchKeepAlive; var _canHaveReducedPayload; var _addNoResponse; var _unloadHooks; var _sendHook; var _sendListener; var _responseHandlers; var _isInitialized; var _timeoutWrapper; var _excludeCsMetaData; var _sendPostMgr; var _fetchCredentials; var _maxEvtPerBatch = maxEventsPerBatch; // Sets default value in case the value is null dynamicProto(HttpManager, this, function (_self) { _initDefaults(); var _sendCredentials = true; _self[_DYN_INITIALIZE /* @min:%2einitialize */] = function (theConfig, core, postChannel) { if (!_isInitialized) { _core = core; _cookieMgr = core.getCookieMgr(); _postManager = postChannel; _logger = _postManager.diagLog(); arrAppend(_unloadHooks, onConfigChange(theConfig, function (details) { var _a; var coreConfig = details.cfg; var channelConfig = details.cfg.extensionConfig[postChannel.identifier]; _timeoutWrapper = createTimeoutWrapper(channelConfig[_DYN_SET_TIMEOUT_OVERRIDE /* @min:%2esetTimeoutOverride */], channelConfig[_DYN_CLEAR_TIMEOUT_OVERRI3 /* @min:%2eclearTimeoutOverride */]); if (isValueAssigned(coreConfig.anonCookieName)) { _addQueryStringParameter(_queryStringParameters, "anoncknm", coreConfig.anonCookieName); } else { _removeQueryStringParameter(_queryStringParameters, "anoncknm"); } _sendHook = channelConfig[_DYN_PAYLOAD_PREPROCESSOR /* @min:%2epayloadPreprocessor */]; _sendListener = channelConfig.payloadListener; var httpInterface = channelConfig.httpXHROverride; // Override endpointUrl if provided in Post config var endpointUrl = channelConfig[_DYN_OVERRIDE_ENDPOINT_UR4 /* @min:%2eoverrideEndpointUrl */] ? channelConfig[_DYN_OVERRIDE_ENDPOINT_UR4 /* @min:%2eoverrideEndpointUrl */] : coreConfig.endpointUrl; _urlString = endpointUrl + UrlQueryString; _useHeaders = !isUndefined(channelConfig[_DYN_AVOID_OPTIONS /* @min:%2eavoidOptions */]) ? !channelConfig[_DYN_AVOID_OPTIONS /* @min:%2eavoidOptions */] : true; _enableEventTimings = !channelConfig[_DYN_DISABLE_EVENT_TIMING5 /* @min:%2edisableEventTimings */]; var maxEvtCfg = channelConfig.maxEvtPerBatch; _maxEvtPerBatch = maxEvtCfg && maxEvtCfg <= maxEventsPerBatch ? maxEvtCfg : maxEventsPerBatch; var valueSanitizer = channelConfig.valueSanitizer; var stringifyObjects = channelConfig.stringifyObjects; var enableCompoundKey = !!coreConfig[_DYN_ENABLE_COMPOUND_KEY /* @min:%2eenableCompoundKey */]; if (!isUndefined(channelConfig[_DYN_ENABLE_COMPOUND_KEY /* @min:%2eenableCompoundKey */])) { enableCompoundKey = !!channelConfig[_DYN_ENABLE_COMPOUND_KEY /* @min:%2eenableCompoundKey */]; } _xhrTimeout = channelConfig.xhrTimeout; var csStream = getInst("CompressionStream"); // Controls whether payload compression (gzip) is enabled. _zipPayload = isFeatureEnabled("zipPayload", coreConfig, false); // if user has payload processor (_sendHook), they may compress the payload themselves // to avoid double compression, we should disable the zipPayload if (!isFunction(csStream) || _sendHook) { _zipPayload = false; } _disableXhrSync = !!channelConfig[_DYN_DISABLE_XHR_SYNC /* @min:%2edisableXhrSync */]; _disableFetchKeepAlive = !!channelConfig[_DYN_DISABLE_FETCH_KEEP_A6 /* @min:%2edisableFetchKeepAlive */]; _addNoResponse = channelConfig[_DYN_ADD_NO_RESPONSE /* @min:%2eaddNoResponse */] !== false; _excludeCsMetaData = !!channelConfig.excludeCsMetaData; if (!!core.getPlugin("LocalStorage")) { // Always disable fetch keep alive when persisten storage is available _disableFetchKeepAlive = true; } _useBeacons = !isReactNative(); // Only use beacons if not running in React Native _serializer = new Serializer(_core, valueSanitizer, stringifyObjects, enableCompoundKey, getCommonSchemaMetaData, _excludeCsMetaData, channelConfig); if (!isNullOrUndefined(channelConfig[_DYN_USE_SEND_BEACON /* @min:%2euseSendBeacon */])) { _useBeacons = !!channelConfig[_DYN_USE_SEND_BEACON /* @min:%2euseSendBeacon */]; } if (channelConfig[_DYN_FETCH_CREDENTIALS /* @min:%2efetchCredentials */]) { _fetchCredentials = channelConfig[_DYN_FETCH_CREDENTIALS /* @min:%2efetchCredentials */]; } var sendPostConfig = _getSendPostMgrConfig(); // only init it once if (!_sendPostMgr) { _sendPostMgr = new SenderPostManager(); _sendPostMgr[_DYN_INITIALIZE /* @min:%2einitialize */](sendPostConfig, _logger); } else { _sendPostMgr.SetConfig(sendPostConfig); } var syncHttpInterface = httpInterface; var beaconHttpInterface = channelConfig[_DYN_ALWAYS_USE_XHR_OVERR7 /* @min:%2ealwaysUseXhrOverride */] ? httpInterface : null; var fetchSyncHttpInterface = channelConfig[_DYN_ALWAYS_USE_XHR_OVERR7 /* @min:%2ealwaysUseXhrOverride */] ? httpInterface : null; var beaconUnloadTransports = [3 /* TransportType.Beacon */, 2 /* TransportType.Fetch */]; if (!httpInterface) { _customHttpInterface = false; // this is handled in SendPostManager now // let location = getLocation(); // if (location && location.protocol && location.protocol.toLowerCase() === "file:") { // // Special case where a local html file fails with a CORS error on Chromium browsers // _sendCredentials = false; // } var theTransports = []; if (isReactNative()) { // Use Fetch or XDR/XHR theTransports = [2 /* TransportType.Fetch */, 1 /* TransportType.Xhr */]; beaconUnloadTransports = [2 /* TransportType.Fetch */, 1 /* TransportType.Xhr */, 3 /* TransportType.Beacon */]; } else { // Use XDR/XHR, Fetch or beacons theTransports = [1 /* TransportType.Xhr */, 2 /* TransportType.Fetch */, 3 /* TransportType.Beacon */]; } // Prefix any user requested transport(s) values theTransports = prependTransports(theTransports, channelConfig.transports); httpInterface = _getSenderInterface(theTransports, false); if (!httpInterface) { _warnToConsole(_logger, "No available transport to send events"); } syncHttpInterface = _getSenderInterface(theTransports, true); } if (!beaconHttpInterface) { // Allow overriding the usage of sendBeacon beaconUnloadTransports = prependTransports(beaconUnloadTransports, channelConfig.unloadTransports); beaconHttpInterface = _getSenderInterface(beaconUnloadTransports, true); } _canHaveReducedPayload = !_customHttpInterface && ((_useBeacons && isBeaconsSupported()) || (!_disableFetchKeepAlive && isFetchSupported(true))); _sendInterfaces = (_a = {}, _a[0 /* EventSendType.Batched */] = httpInterface, _a[1 /* EventSendType.Synchronous */] = syncHttpInterface || _getSenderInterface([1 /* TransportType.Xhr */, 2 /* TransportType.Fetch */, 3 /* TransportType.Beacon */], true), _a[2 /* EventSendType.SendBeacon */] = beaconHttpInterface || syncHttpInterface || _getSenderInterface([1 /* TransportType.Xhr */], true), _a[3 /* EventSendType.SyncFetch */] = fetchSyncHttpInterface || _getSenderInterface([2 /* TransportType.Fetch */, 3 /* TransportType.Beacon */], true) || syncHttpInterface || _getSenderInterface([1 /* TransportType.Xhr */], true), _a); })); _isInitialized = true; } }; _self.addResponseHandler = function (responseHandler) { _responseHandlers[_DYN_PUSH /* @min:%2epush */](responseHandler); return { rm: function () { var index = _responseHandlers.indexOf(responseHandler); if (index >= 0) { _responseHandlers.splice(index, 1); } } }; }; _self[_DYN_SERIALIZE_OFFLINE_EV8 /* @min:%2eserializeOfflineEvt */] = function (evt) { try { if (_serializer) { return _serializer.getEventBlob(evt); } } catch (e) { // eslint-disable-next-line no-empty } return STR_EMPTY; }; _self[_DYN_GET_OFFLINE_REQUEST_9 /* @min:%2egetOfflineRequestDetails */] = function () { try { // get current url without paramter var payload = _serializer && _serializer[_DYN_CREATE_PAYLOAD /* @min:%2ecreatePayload */](0, false, false, false, 1 /* SendRequestReason.NormalSchedule */, 0 /* EventSendType.Batched */); return _buildRequestDetails(payload, _useHeaders); } catch (e) { // eslint-disable-next-line no-empty } return null; }; _self[_DYN_CREATE_ONE_DSPAYLOAD /* @min:%2ecreateOneDSPayload */] = function (evts, optimize) { try { // TODO: optimize var theBatches_1 = []; // create a eventBatch for each event arrForEach(evts, function (evt) { if (optimize) { evt = optimizeObject(evt); } var batch = EventBatch.create(evt[_DYN_I_KEY /* @min:%2eiKey */], [evt]); theBatches_1[_DYN_PUSH /* @min:%2epush */](batch); }); var thePayload = null; while (theBatches_1[_DYN_LENGTH /* @min:%2elength */] > 0 && _serializer) { var theBatch = theBatches_1.shift(); if (theBatch && theBatch[_DYN_COUNT /* @min:%2ecount */]() > 0) { thePayload = thePayload || _serializer[_DYN_CREATE_PAYLOAD /* @min:%2ecreatePayload */](0, false, false, false, 1 /* SendRequestReason.NormalSchedule */, 0 /* EventSendType.Batched */); _serializer.appendPayload(thePayload, theBatch, _maxEvtPerBatch); } } var requestDetails = _buildRequestDetails(thePayload, _useHeaders); var payloadData = { data: thePayload[_DYN_PAYLOAD_BLOB /* @min:%2epayloadBlob */], urlString: requestDetails.url, headers: requestDetails[_DYN_HDRS /* @min:%2ehdrs */], timeout: _xhrTimeout, disableXhrSync: _disableXhrSync, disableFetchKeepAlive: _disableFetchKeepAlive }; // Only automatically add the following headers if already sending headers and we are not attempting to avoid an options call if (_useHeaders) { if (!_hasHeader(payloadData[_DYN_HEADERS /* @min:%2eheaders */], STR_CACHE_CONTROL)) { payloadData[_DYN_HEADERS /* @min:%2eheaders */][STR_CACHE_CONTROL] = DEFAULT_CACHE_CONTROL; } if (!_hasHeader(payloadData[_DYN_HEADERS /* @min:%2eheaders */], STR_CONTENT_TYPE_HEADER)) { payloadData[_DYN_HEADERS /* @min:%2eheaders */][STR_CONTENT_TYPE_HEADER] = DEFAULT_CONTENT_TYPE; } } return payloadData; } catch (e) { // eslint-disable-next-line no-empty } return null; }; // Special internal method to allow the DebugPlugin to hook embedded objects function _getSenderInterface(transports, syncSupport) { try { return _sendPostMgr && _sendPostMgr.getSenderInst(transports, syncSupport); } catch (e) { // eslint-disable-next-line no-empty } return null; } _self["_getDbgPlgTargets"] = function () { return [_sendInterfaces[0 /* EventSendType.Batched */], _killSwitch, _serializer, _sendInterfaces, _getSendPostMgrConfig(), _urlString, _maxEvtPerBatch]; }; function _getSendPostMgrConfig() { try { var onCompleteFuncs = { xdrOnComplete: _xdrOncomplete, fetchOnComplete: _fetchOnComplete, xhrOnComplete: _xhrOnComplete, beaconOnRetry: _onBeaconRetry }; var config = { enableSendPromise: false, isOneDs: true, disableCredentials: !_sendCredentials, fetchCredentials: _fetchCredentials, disableXhr: false, disableBeacon: !_useBeacons, disableBeaconSync: !_useBeacons, disableFetchKeepAlive: _disableFetchKeepAlive, timeWrapper: _timeoutWrapper, addNoResponse: _addNoResponse, senderOnCompleteCallBack: onCompleteFuncs }; return config; } catch (e) { // eslint-disable-next-line no-empty } return null; } function _xdrOncomplete(xdr, oncomplete, payload) { var response = getResponseText(xdr); _doOnComplete(oncomplete, 200, {}, response); _handleCollectorResponse(response); } function _initDefaults() { var undefValue; _urlString = null; _killSwitch = new KillSwitch(); _paused = false; _clockSkewManager = new ClockSkewManager(); _useBeacons = false; _outstandingRequests = 0; // Holds the number of outstanding async requests that have not returned a response yet _postManager = null; _logger = null; _sendInterfaces = null; _core = null; _customHttpInterface = true; _queryStringParameters = []; _headers = {}; _batchQueue = []; _serializer = null; _enableEventTimings = false; _cookieMgr = null; _isUnloading = false; _useHeaders = false; _xhrTimeout = undefValue; _disableXhrSync = undefValue; _disableFetchKeepAlive = undefValue; _canHaveReducedPayload = undefValue; _addNoResponse = undefValue; _unloadHooks = []; _sendHook = undefValue; _sendListener = undefValue; _responseHandlers = []; _isInitialized = false; _timeoutWrapper = createTimeoutWrapper(); _excludeCsMetaData = false; _sendPostMgr = null; _maxEvtPerBatch = null; } function _fetchOnComplete(response, onComplete, resValue, payload) { var handleResponse = function (status, headerMap, responseText) { _doOnComplete(onComplete, status, headerMap, responseText); _handleCollectorResponse(responseText); }; var headerMap = {}; var headers = response[_DYN_HEADERS /* @min:%2eheaders */]; if (headers) { headers["forEach"](function (value, name) { headerMap[name] = value; }); } handleResponse(response.status, headerMap, resValue || STR_EMPTY); } function _xhrOnComplete(request, oncomplete, payload) { var response = getResponseText(request); _doOnComplete(oncomplete, request.status, _getAllResponseHeaders(request, true), response); _handleCollectorResponse(response); } function _doOnComplete(oncomplete, status, headers, response) { try { oncomplete(status, headers, response); } catch (e) { _throwInternal(_logger, 2 /* eLoggingSeverity.WARNING */, 518 /* _eExtendedInternalMessageId.SendPostOnCompleteFailure */, dumpObj(e)); } } function _onBeaconRetry(payload, onComplete, canSend) { // Custom headers not supported in sendBeacon payload.headers would be ignored var internalPayloadData = payload; var status = 200; var thePayload = internalPayloadData[_DYN__THE_PAYLOAD /* @min:%2e_thePayload */]; var theUrl = payload.urlString + (_addNoResponse ? _noResponseQs : STR_EMPTY); try { var nav_1 = getNavigator(); if (thePayload) { var persistStorage = !!_core.getPlugin("LocalStorage"); // Failed to send entire payload so try and split data and try to send as much events as possible var droppedBatches_1 = []; var sentBatches_1 = []; arrForEach(thePayload[_DYN_BATCHES /* @min:%2ebatches */], function (theBatch) { if (droppedBatches_1 && theBatch && theBatch[_DYN_COUNT /* @min:%2ecount */]() > 0) { var theEvents = theBatch[_DYN_EVENTS /* @min:%2eevents */](); for (var lp = 0; lp < theEvents[_DYN_LENGTH /* @min:%2elength */]; lp++) { if (!nav_1.sendBeacon(theUrl, _serializer.getEventBlob(theEvents[lp]))) { // Can't send anymore, so split the batch and drop the rest droppedBatches_1[_DYN_PUSH /* @min:%2epush */](theBatch[_DYN_SPLIT /* @min:%2esplit */](lp)); break; } else { sentBatches_1[_DYN_PUSH /* @min:%2epush */](theBatch[lp]); } } } else { // Remove all of the events from the existing batch in the payload as the copy includes the original droppedBatches_1[_DYN_PUSH /* @min:%2epush */](theBatch[_DYN_SPLIT /* @min:%2esplit */](0)); } }); if (sentBatches_1[_DYN_LENGTH /* @min:%2elength */] > 0) { // Update the payload with the sent batches thePayload.sentEvts = sentBatches_1; } if (!persistStorage) { // Events passed Serializer size validation, log BeaconSendFailure // because it could still be size related but we did not exceed the // configured limit, and sendBeacon could fail for other reasons _sendBatchesNotification(droppedBatches_1, 8006 /* EventBatchNotificationReason.BeaconSendFailure */, thePayload[_DYN_SEND_TYPE /* @min:%2esendType */], true); } } else { status = 0; } } catch (ex) { _warnToConsole(_logger, "Failed to send telemetry using sendBeacon API. Ex:" + dumpObj(ex)); status = 0; } finally { _doOnComplete(onComplete, status, {}, STR_EMPTY); } } function _isBeaconPayload(sendType) { // Sync Fetch has the same payload limitation as sendBeacon -- 64kb limit, so treat both as a beacon send return sendType === 2 /* EventSendType.SendBeacon */ || sendType === 3 /* EventSendType.SyncFetch */; } function _adjustSendType(sendType) { if (_isUnloading && _isBeaconPayload(sendType)) { sendType = 2 /* EventSendType.SendBeacon */; } return sendType; } _self.addHeader = function (name, value) { _headers[name] = value; }; _self.removeHeader = function (name) { delete _headers[name]; }; _self[_DYN_CAN_SEND_REQUEST /* @min:%2ecanSendRequest */] = function () { return _hasIdleConnection() && _clockSkewManager[_DYN_ALLOW_REQUEST_SENDIN0 /* @min:%2eallowRequestSending */](); }; _self[_DYN_SEND_QUEUED_REQUESTS /* @min:%2esendQueuedRequests */] = function (sendType, sendReason) { if (isUndefined(sendType)) { sendType = 0 /* EventSendType.Batched */; } if (_isUnloading) { sendType = _adjustSendType(sendType); sendReason = 2 /* SendRequestReason.Unload */; } if (_canSendPayload(_batchQueue, sendType, 0)) { _sendBatches(_clearQueue(), 0, false, sendType, sendReason || 0 /* SendRequestReason.Undefined */); } }; _self.isCompletelyIdle = function () { return !_paused && _outstandingRequests === 0 && _batchQueue[_DYN_LENGTH /* @min:%2elength */] === 0; }; _self[_DYN_SET_UNLOADING /* @min:%2esetUnloading */] = function (value) { _isUnloading = value; }; _self.addBatch = function (theBatch) { if (theBatch && theBatch[_DYN_COUNT /* @min:%2ecount */]() > 0) { // Try and kill the event faster if (_killSwitch.isTenantKilled(theBatch[_DYN_I_KEY /* @min:%2eiKey */]())) { return false; } _batchQueue[_DYN_PUSH /* @min:%2epush */](theBatch); } return true; }; /** * Queue all the remaining requests to be sent. The requests will be * sent using HTML5 Beacons if they are available. */ _self.teardown = function () { if (_batchQueue[_DYN_LENGTH /* @min:%2elength */] > 0) { _sendBatches(_clearQueue(), 0, true, 2 /* EventSendType.SendBeacon */, 2 /* SendRequestReason.Unload */); } arrForEach(_unloadHooks, function (hook) { hook && hook.rm && hook.rm(); }); _unloadHooks = []; }; /** * Pause the sending of requests. No new requests will be sent. */ _self.pause = function () { _paused = true; }; /** * Resume the sending of requests. */ _self.resume = function () { _paused = false; _self[_DYN_SEND_QUEUED_REQUESTS /* @min:%2esendQueuedRequests */](0 /* EventSendType.Batched */, 4 /* SendRequestReason.Resumed */); }; /** * Sends a request synchronously to the Aria collector. This api is used to send * a request containing a single immediate event. * * @param batch - The request to be sent. * @param sendReason - The token used to send the request. */ _self[_DYN_SEND_SYNCHRONOUS_BAT10 /* @min:%2esendSynchronousBatch */] = function (batch, sendType, sendReason) { // This will not take into account the max connections restriction. Since this is sync, we can // only send one of this request at a time and thus should not worry about multiple connections // being used to send synchronous events. // Increment active connection since we are still going to use a connection to send the request. if (batch && batch[_DYN_COUNT /* @min:%2ecount */]() > 0) { if (isNullOrUndefined(sendType)) { sendType = 1 /* EventSendType.Synchronous */; } if (_isUnloading) { sendType = _adjustSendType(sendType); sendReason = 2 /* SendRequestReason.Unload */; } // For sync requests we will not wait for the clock skew. _sendBatches([batch], 0, false, sendType, sendReason || 0 /* SendRequestReason.Undefined */); } }; function _hasIdleConnection() { return !_paused && _outstandingRequests < maxConnections; } function _clearQueue() { var theQueue = _batchQueue; _batchQueue = []; return theQueue; } function _canSendPayload(theBatches, sendType, retryCnt) { var result = false; if (theBatches && theBatches[_DYN_LENGTH /* @min:%2elength */] > 0 && !_paused && _sendInterfaces[sendType] && _serializer) { // Always attempt to send synchronous events don't wait for idle or clockSkew // and don't block retry requests if clockSkew is not yet set result = (sendType !== 0 /* EventSendType.Batched */) || (_hasIdleConnection() && (retryCnt > 0 || _clockSkewManager[_DYN_ALLOW_REQUEST_SENDIN0 /* @min:%2eallowRequestSending */]())); } return result; } function _createDebugBatches(theBatches) { var values = {}; if (theBatches) { arrForEach(theBatches, function (theBatch, idx) { values[idx] = { iKey: theBatch[_DYN_I_KEY /* @min:%2eiKey */](), evts: theBatch[_DYN_EVENTS /* @min:%2eevents */]() }; }); } return values; } function _sendBatches(theBatches, retryCount, isTeardown, sendType, sendReason) { if (!theBatches || theBatches[_DYN_LENGTH /* @min:%2elength */] === 0) { // Nothing to do return; } if (_paused) { _sendBatchesNotification(theBatches, 1 /* EventBatchNotificationReason.Paused */, sendType); return; } // Make sure that if we are unloading the sendType is a supported version sendType = _adjustSendType(sendType); try { var orgBatches_1 = theBatches; var isSynchronous_1 = sendType !== 0 /* EventSendType.Batched */; doPerf(_core, function () { return "HttpManager:_sendBatches"; }, function (perfEvt) { if (perfEvt) { // Perf Monitoring is enabled, so create a "Quick" copy of the original batches so we still report // the original values as part of the perfEvent. This is because theBatches uses .shift() to remove each // batch as they are processed - removing from the original array, so by the time the _createDebugBatches() // function is called the passed in value has changed and therefore the reported value for the perfEvent is incorrect theBatches = theBatches.slice(0); } var droppedBatches = []; var thePayload = null; var serializationStart = getTime(); var sendInterface = _sendInterfaces[sendType] || (isSynchronous_1 ? _sendInterfaces[1 /* EventSendType.Synchronous */] : _sendInterfaces[0 /* EventSendType.Batched */]); var sendTransport = sendInterface && sendInterface[_DYN__TRANSPORT /* @min:%2e_transport */]; // Sync Fetch has the same payload limitation as sendBeacon -- 64kb limit var isReducedPayload = _canHaveReducedPayload && (_isUnloading || _isBeaconPayload(sendType) || (sendTransport === 3 /* TransportType.Beacon */ || (sendInterface._isSync && sendTransport === 2 /* TransportType.Fetch */))); while (_canSendPayload(theBatches, sendType, retryCount)) { var theBatch = theBatches.shift(); if (theBatch && theBatch[_DYN_COUNT /* @min:%2ecount */]() > 0) { if (!_killSwitch.isTenantKilled(theBatch[_DYN_I_KEY /* @min:%2eiKey */]())) { // Make sure we have a payload object thePayload = thePayload || _serializer[_DYN_CREATE_PAYLOAD /* @min:%2ecreatePayload */](retryCount, isTeardown, isSynchronous_1, isReducedPayload, sendReason, sendType); // Add the batch to the current payload if (!_serializer.appendPayload(thePayload, theBatch, _maxEvtPerBatch)) { // Entire batch was not added so send the payload and retry adding this batch _doPayloadSend(thePayload, serializationStart, getTime(), sendReason); serializationStart = getTime(); theBatches = [theBatch][_DYN_CONCAT /* @min:%2econcat */](theBatches); thePayload = null; } else if (thePayload.overflow !== null) { // Total Payload size was exceeded so send the payload and add the unsent as the next batch to send theBatches = [thePayload.overflow][_DYN_CONCAT /* @min:%2econcat */](theBatches); thePayload.overflow = null; _doPayloadSend(thePayload, serializationStart, getTime(), sendReason); serializationStart = getTime(); thePayload = null; } } else { droppedBatches[_DYN_PUSH /* @min:%2epush */](theBatch); } } } // Make sure to flush any remaining payload if (thePayload) { _doPayloadSend(thePayload, serializationStart, getTime(), sendReason); } if (theBatches[_DYN_LENGTH /* @min:%2elength */] > 0) { // Add any unsent batches back to the head of the queue _batchQueue = theBatches[_DYN_CONCAT /* @min:%2econcat */](_batchQueue); } // Now send notification about any dropped events _sendBatchesNotification(droppedBatches, 8004 /* EventBatchNotificationReason.KillSwitch */, sendType); }, function () { return ({ batches: _createDebugBatches(orgBatches_1), retryCount: retryCount, isTeardown: isTeardown, isSynchronous: isSynchronous_1, sendReason: sendReason, useSendBeacon: _isBeaconPayload(sendType), sendType: sendType }); }, !isSynchronous_1); } catch (ex) { _throwInternal(_logger, 2 /* eLoggingSeverity.WARNING */, 48 /* _eInternalMessageId.CannotSerializeObject */, "Unexpected Exception sending batch: " + dumpObj(ex)); } } function _buildRequestDetails(thePayload, useHeaders) { var requestDetails = { url: _urlString, hdrs: {}, useHdrs: false // Assume no headers }; if (!useHeaders) { // Attempt to map headers to a query string if possible objForEachKey(_headers, function (name, value) { if (_collectorHeaderToQs[name]) { _addRequestDetails(requestDetails, _collectorHeaderToQs[name], value, false); } else { // No mapping, so just include in the headers anyway (may not get sent if using sendBeacon()) requestDetails[_DYN_HDRS /* @min:%2ehdrs */][name] = value; requestDetails[_DYN_USE_HDRS /* @min:%2euseHdrs */] = true; } }); } else { // Copy the pre-defined headers into the payload headers requestDetails[_DYN_HDRS /* @min:%2ehdrs */] = extend(requestDetails[_DYN_HDRS /* @min:%2ehdrs */], _headers); requestDetails.useHdrs = (objKeys(requestDetails.hdrs)[_DYN_LENGTH /* @min:%2elength */] > 0); } _addRequestDetails(requestDetails, STR_CLIENT_ID, "NO_AUTH", useHeaders); _addRequestDetails(requestDetails, STR_CLIENT_VERSION, FullVersionString, useHeaders); var apiQsKeys = STR_EMPTY; arrForEach(thePayload.apiKeys, function (apiKey) { if (apiQsKeys[_DYN_LENGTH /* @min:%2elength */] > 0) { apiQsKeys += ","; } apiQsKeys += apiKey; }); _addRequestDetails(requestDetails, STR_API_KEY, apiQsKeys, useHeaders); _addRequestDetails(requestDetails, STR_UPLOAD_TIME, dateNow().toString(), useHeaders); var msfpc = _getMsfpc(thePayload); if (isValueAssigned(msfpc)) { requestDetails.url += "&ext.intweb.msfpc=" + msfpc; } if (_clockSkewManager[_DYN_SHOULD_ADD_CLOCK_SKE1 /* @min:%2eshouldAddClockSkewHeaders */]()) { _addRequestDetails(requestDetails, STR_TIME_DELTA_TO_APPLY, _clockSkewManager[_DYN_GET_CLOCK_SKEW_HEADE2 /* @min:%2egetClockSkewHeaderValue */](), useHeaders); } if (_core[_DYN_GET_WPARAM /* @min:%2egetWParam */]) { var wParam = _core[_DYN_GET_WPARAM /* @min:%2egetWParam */](); if (wParam >= 0) { requestDetails.url += "&w=" + wParam; } } for (var i = 0; i < _queryStringParameters[_DYN_LENGTH /* @min:%2elength */]; i++) { requestDetails.url += "&" + _queryStringParameters[i].name + "=" + _queryStringParameters[i].value; } return requestDetails; } function _setTimingValue(timings, name, value) { timings[name] = timings[name] || {}; timings[name][_postManager.identifier] = value; } function _doPayloadSend(thePayload, serializationStart, serializationCompleted, sendReason) { if (thePayload && thePayload.payloadBlob && thePayload.payloadBlob[_DYN_LENGTH /* @min:%2elength */] > 0) { var useSendHook_1 = !!_sendHook; var sendInterface_1 = _sendInterfaces[thePayload.sendType]; // Send all data using a beacon style transport if closing mode is on or channel was teared down if (!_isBeaconPayload(thePayload[_DYN_SEND_TYPE /* @min:%2esendType */]) && thePayload[_DYN_IS_BEACON /* @min:%2eisBeacon */] && thePayload.sendReason === 2 /* SendRequestReason.Unload */) { sendInterface_1 = _sendInterfaces[2 /* EventSendType.SendBeacon */] || _sendInterfaces[3 /* EventSendType.SyncFetch */] || sendInterface_1; } var useHeaders_1 = _useHeaders; // Disable header usage if we know we are using sendBeacon as additional headers are not supported if (thePayload.isBeacon || sendInterface_1[_DYN__TRANSPORT /* @min:%2e_transport */] === 3 /* TransportType.Beacon */) { useHeaders_1 = false; } var requestDetails_1 = _buildRequestDetails(thePayload, useHeaders_1); useHeaders_1 = useHeaders_1 || requestDetails_1[_DYN_USE_HDRS /* @min:%2euseHdrs */]; var sendEventStart_1 = getTime(); doPerf(_core, function () { return "HttpManager:_doPayloadSend"; }, function () { // Increment the send attempt count and add timings after packaging (So it's not serialized in the 1st attempt) for (var batchLp = 0; batchLp < thePayload.batches[_DYN_LENGTH /* @min:%2elength */]; batchLp++) { var theBatch = thePayload[_DYN_BATCHES /* @min:%2ebatches */][batchLp]; var theEvents = theBatch[_DYN_EVENTS /* @min:%2eevents */](); for (var evtLp = 0; evtLp < theEvents[_DYN_LENGTH /* @min:%2elength */]; evtLp++) { var telemetryItem = theEvents[evtLp]; if (_enableEventTimings) { var timings = telemetryItem[_DYN_TIMINGS /* @min:%2etimings */] = telemetryItem[_DYN_TIMINGS /* @min:%2etimings */] || {}; _setTimingValue(timings, "sendEventStart", sendEventStart_1); _setTimingValue(timings, "serializationStart", serializationStart); _setTimingValue(timings, "serializationCompleted", serializationCompleted); } telemetryItem[strSendAttempt] > 0 ? telemetryItem[strSendAttempt]++ : telemetryItem[strSendAttempt] = 1; } } // Note: always sending this notification in a synchronous manner. _sendBatchesNotification(thePayload[_DYN_BATCHES /* @min:%2ebatches */], (1000 /* EventBatchNotificationReason.SendingUndefined */ + (sendReason || 0 /* SendRequestReason.Undefined */)), thePayload[_DYN_SEND_TYPE /* @min:%2esendType */], true); //